in service/dataflow/ConstantUses.cpp [201:534]
TypeDemand ConstantUses::get_type_demand(IRInstruction* insn,
size_t src_index) const {
always_assert(src_index < insn->srcs_size());
switch (insn->opcode()) {
case OPCODE_GOTO:
case IOPCODE_LOAD_PARAM:
case IOPCODE_LOAD_PARAM_OBJECT:
case IOPCODE_LOAD_PARAM_WIDE:
case OPCODE_NOP:
case IOPCODE_MOVE_RESULT_PSEUDO:
case OPCODE_MOVE_RESULT:
case IOPCODE_MOVE_RESULT_PSEUDO_OBJECT:
case OPCODE_MOVE_RESULT_OBJECT:
case IOPCODE_MOVE_RESULT_PSEUDO_WIDE:
case OPCODE_MOVE_RESULT_WIDE:
case OPCODE_MOVE_EXCEPTION:
case OPCODE_RETURN_VOID:
case OPCODE_CONST:
case OPCODE_CONST_WIDE:
case OPCODE_CONST_STRING:
case OPCODE_CONST_CLASS:
case OPCODE_NEW_INSTANCE:
case OPCODE_SGET:
case OPCODE_SGET_BOOLEAN:
case OPCODE_SGET_BYTE:
case OPCODE_SGET_CHAR:
case OPCODE_SGET_SHORT:
case OPCODE_SGET_WIDE:
case OPCODE_SGET_OBJECT:
case IOPCODE_INIT_CLASS:
not_reached();
case OPCODE_RETURN:
case OPCODE_RETURN_WIDE:
return m_rtype ? get_type_demand(m_rtype) : TypeDemand::Error;
case OPCODE_MOVE:
return TypeDemand::IntOrFloat;
case OPCODE_MOVE_WIDE:
return TypeDemand::LongOrDouble;
case OPCODE_MOVE_OBJECT:
case OPCODE_RETURN_OBJECT:
case OPCODE_MONITOR_ENTER:
case OPCODE_MONITOR_EXIT:
case OPCODE_ARRAY_LENGTH:
case OPCODE_FILL_ARRAY_DATA:
case OPCODE_THROW:
case OPCODE_IGET:
case OPCODE_IGET_BOOLEAN:
case OPCODE_IGET_BYTE:
case OPCODE_IGET_CHAR:
case OPCODE_IGET_SHORT:
case OPCODE_IGET_WIDE:
case OPCODE_IGET_OBJECT:
return TypeDemand::Object;
case OPCODE_CHECK_CAST:
// In the Android verifier, the check-cast instruction updates the assumed
// exact type on the incoming register, even in the case of a zero constant.
// We don't track exact types here, and just bail out.
return TypeDemand::Error;
case OPCODE_INSTANCE_OF:
// The Android verifier in some ART versions match a pattern of
// instance-of + ifXXX, and then may strengthen assumptions on the incoming
// register, even in the case of a zero constant.
// https://android.googlesource.com/platform/art/+/refs/tags/android-10.0.0_r5/runtime/verifier/method_verifier.cc#2683
// We don't track exact types here, and certainly don't want to deal with
// somewhat fragile pattern matching, and so just bail out.
return TypeDemand::Error;
case OPCODE_NEW_ARRAY:
case OPCODE_SWITCH:
case OPCODE_NEG_INT:
case OPCODE_NOT_INT:
case OPCODE_INT_TO_BYTE:
case OPCODE_INT_TO_CHAR:
case OPCODE_INT_TO_SHORT:
case OPCODE_INT_TO_LONG:
case OPCODE_INT_TO_FLOAT:
case OPCODE_INT_TO_DOUBLE:
case OPCODE_ADD_INT:
case OPCODE_SUB_INT:
case OPCODE_MUL_INT:
case OPCODE_AND_INT:
case OPCODE_OR_INT:
case OPCODE_XOR_INT:
case OPCODE_SHL_INT:
case OPCODE_SHR_INT:
case OPCODE_USHR_INT:
case OPCODE_DIV_INT:
case OPCODE_REM_INT:
case OPCODE_ADD_INT_LIT16:
case OPCODE_RSUB_INT:
case OPCODE_MUL_INT_LIT16:
case OPCODE_AND_INT_LIT16:
case OPCODE_OR_INT_LIT16:
case OPCODE_XOR_INT_LIT16:
case OPCODE_ADD_INT_LIT8:
case OPCODE_RSUB_INT_LIT8:
case OPCODE_MUL_INT_LIT8:
case OPCODE_AND_INT_LIT8:
case OPCODE_OR_INT_LIT8:
case OPCODE_XOR_INT_LIT8:
case OPCODE_SHL_INT_LIT8:
case OPCODE_SHR_INT_LIT8:
case OPCODE_USHR_INT_LIT8:
case OPCODE_DIV_INT_LIT16:
case OPCODE_REM_INT_LIT16:
case OPCODE_DIV_INT_LIT8:
case OPCODE_REM_INT_LIT8:
return TypeDemand::Int;
case OPCODE_FILLED_NEW_ARRAY: {
DexType* component_type = type::get_array_component_type(insn->get_type());
return get_type_demand(component_type);
}
case OPCODE_CMPL_FLOAT:
case OPCODE_CMPG_FLOAT:
case OPCODE_NEG_FLOAT:
case OPCODE_FLOAT_TO_INT:
case OPCODE_FLOAT_TO_LONG:
case OPCODE_FLOAT_TO_DOUBLE:
case OPCODE_ADD_FLOAT:
case OPCODE_SUB_FLOAT:
case OPCODE_MUL_FLOAT:
case OPCODE_DIV_FLOAT:
case OPCODE_REM_FLOAT:
return TypeDemand::Float;
case OPCODE_CMPL_DOUBLE:
case OPCODE_CMPG_DOUBLE:
case OPCODE_NEG_DOUBLE:
case OPCODE_DOUBLE_TO_INT:
case OPCODE_DOUBLE_TO_LONG:
case OPCODE_DOUBLE_TO_FLOAT:
case OPCODE_ADD_DOUBLE:
case OPCODE_SUB_DOUBLE:
case OPCODE_MUL_DOUBLE:
case OPCODE_DIV_DOUBLE:
case OPCODE_REM_DOUBLE:
return TypeDemand::Double;
case OPCODE_CMP_LONG:
case OPCODE_NEG_LONG:
case OPCODE_NOT_LONG:
case OPCODE_LONG_TO_INT:
case OPCODE_LONG_TO_FLOAT:
case OPCODE_LONG_TO_DOUBLE:
case OPCODE_ADD_LONG:
case OPCODE_SUB_LONG:
case OPCODE_MUL_LONG:
case OPCODE_AND_LONG:
case OPCODE_OR_LONG:
case OPCODE_XOR_LONG:
case OPCODE_DIV_LONG:
case OPCODE_REM_LONG:
return TypeDemand::Long;
case OPCODE_SHL_LONG:
case OPCODE_SHR_LONG:
case OPCODE_USHR_LONG:
if (src_index == 0) return TypeDemand::Long;
always_assert(src_index == 1);
return TypeDemand::Int;
case OPCODE_IF_EQ:
case OPCODE_IF_NE:
if (m_type_inference) {
auto& type_environments = m_type_inference->get_type_environments();
auto& type_environment = type_environments.at(insn);
auto t1 = type_environment.get_type(insn->src(0));
auto t2 = type_environment.get_type(insn->src(1));
if (!t1.is_top() && !t1.is_bottom() && !t2.is_top() && !t2.is_bottom()) {
if (t1.element() == REFERENCE || t2.element() == REFERENCE) {
return TypeDemand::Object;
}
if (is_non_zero_int(t1.element()) || is_non_zero_int(t2.element())) {
return TypeDemand::Int;
}
return TypeDemand::IntOrObject;
}
} else {
TRACE(CU, 3,
"[CU] if-eq or if-ne instruction encountered {%s}, but type "
"inference is unavailable",
SHOW(insn));
}
return TypeDemand::Error;
case OPCODE_IF_EQZ:
case OPCODE_IF_NEZ:
if (m_type_inference) {
auto& type_environments = m_type_inference->get_type_environments();
auto& type_environment = type_environments.at(insn);
auto t = type_environment.get_type(insn->src(0));
if (!t.is_top() && !t.is_bottom()) {
if (t.element() == REFERENCE) {
return TypeDemand::Object;
}
if (is_non_zero_int(t.element())) {
return TypeDemand::Int;
}
return TypeDemand::IntOrObject;
}
} else {
TRACE(CU, 3,
"[CU] if-eqz or if-nez instruction encountered {%s}, but type "
"inference is unavailable",
SHOW(insn));
}
return TypeDemand::Error;
case OPCODE_IF_LTZ:
case OPCODE_IF_GEZ:
case OPCODE_IF_GTZ:
case OPCODE_IF_LEZ:
return TypeDemand::IntOrObject;
case OPCODE_IF_LT:
case OPCODE_IF_GE:
case OPCODE_IF_GT:
case OPCODE_IF_LE:
return TypeDemand::Int;
case OPCODE_AGET:
case OPCODE_AGET_BOOLEAN:
case OPCODE_AGET_BYTE:
case OPCODE_AGET_CHAR:
case OPCODE_AGET_SHORT:
case OPCODE_AGET_WIDE:
case OPCODE_AGET_OBJECT:
if (src_index == 0) return TypeDemand::Object;
always_assert(src_index == 1);
return TypeDemand::Int;
case OPCODE_APUT:
case OPCODE_APUT_BOOLEAN:
case OPCODE_APUT_BYTE:
case OPCODE_APUT_CHAR:
case OPCODE_APUT_SHORT:
case OPCODE_APUT_WIDE:
case OPCODE_APUT_OBJECT:
if (src_index == 1) return TypeDemand::Object;
if (src_index == 2) return TypeDemand::Int;
always_assert(src_index == 0);
switch (insn->opcode()) {
case OPCODE_APUT:
case OPCODE_APUT_WIDE: {
if (m_type_inference) {
auto& type_environments = m_type_inference->get_type_environments();
auto& type_environment = type_environments.at(insn);
auto dex_type = type_environment.get_dex_type(insn->src(1));
TRACE(CU, 3, "[CU] aput(-wide) instruction array type: %s",
dex_type ? SHOW(dex_type) : "(unknown dex type)");
if (dex_type && type::is_array(*dex_type)) {
auto type_demand =
get_type_demand(type::get_array_component_type(*dex_type));
always_assert(insn->opcode() != OPCODE_APUT ||
(type_demand == TypeDemand::Error ||
type_demand == TypeDemand::Int ||
type_demand == TypeDemand::Float));
always_assert(insn->opcode() != OPCODE_APUT_WIDE ||
(type_demand == TypeDemand::Error ||
type_demand == TypeDemand::Long ||
type_demand == TypeDemand::Double));
return type_demand;
}
} else {
TRACE(CU, 3,
"[CU] aput(-wide) instruction encountered {%s}, but type "
"inference is unavailable",
SHOW(insn));
}
return TypeDemand::Error;
}
case OPCODE_APUT_BOOLEAN:
case OPCODE_APUT_BYTE:
case OPCODE_APUT_CHAR:
case OPCODE_APUT_SHORT:
return TypeDemand::Int;
case OPCODE_APUT_OBJECT:
return TypeDemand::Object;
default:
not_reached();
}
case OPCODE_IPUT:
case OPCODE_IPUT_BOOLEAN:
case OPCODE_IPUT_BYTE:
case OPCODE_IPUT_CHAR:
case OPCODE_IPUT_SHORT:
case OPCODE_IPUT_WIDE:
case OPCODE_IPUT_OBJECT:
if (src_index == 1) return TypeDemand::Object;
always_assert(src_index == 0);
return get_type_demand(insn->get_field()->get_type());
case OPCODE_SPUT:
case OPCODE_SPUT_BOOLEAN:
case OPCODE_SPUT_BYTE:
case OPCODE_SPUT_CHAR:
case OPCODE_SPUT_SHORT:
case OPCODE_SPUT_WIDE:
case OPCODE_SPUT_OBJECT:
return get_type_demand(insn->get_field()->get_type());
case OPCODE_INVOKE_VIRTUAL:
case OPCODE_INVOKE_SUPER:
case OPCODE_INVOKE_DIRECT:
case OPCODE_INVOKE_STATIC:
case OPCODE_INVOKE_INTERFACE: {
DexMethodRef* dex_method = insn->get_method();
const auto* arg_types = dex_method->get_proto()->get_args();
size_t expected_args =
(insn->opcode() != OPCODE_INVOKE_STATIC ? 1 : 0) + arg_types->size();
always_assert(insn->srcs_size() == expected_args);
if (insn->opcode() != OPCODE_INVOKE_STATIC) {
// The first argument is a reference to the object instance on which the
// method is invoked.
if (src_index-- == 0) return TypeDemand::Object;
}
return get_type_demand(arg_types->at(src_index));
}
case OPCODE_INVOKE_CUSTOM:
case OPCODE_INVOKE_POLYMORPHIC:
not_reached_log(
"Unsupported instruction {%s} in ConstantUses::get_type_demand\n",
SHOW(insn));
}
}