TypeDemand ConstantUses::get_type_demand()

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));
  }
}