summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/verifier/method_verifier.cc419
-rw-r--r--runtime/verifier/reg_type-inl.h28
-rw-r--r--runtime/verifier/reg_type.cc26
-rw-r--r--runtime/verifier/reg_type.h56
-rw-r--r--runtime/verifier/reg_type_cache-inl.h18
-rw-r--r--runtime/verifier/reg_type_cache.h44
-rw-r--r--runtime/verifier/register_line-inl.h57
-rw-r--r--runtime/verifier/register_line.h18
8 files changed, 388 insertions, 278 deletions
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index a18b00e66d..ede8e366d7 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -708,6 +708,39 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier {
}
}
+ NO_INLINE void FailForRegisterType(uint32_t vsrc,
+ const RegType& check_type,
+ const RegType& src_type,
+ VerifyError fail_type = VERIFY_ERROR_BAD_CLASS_HARD)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ Fail(fail_type)
+ << "register v" << vsrc << " has type " << src_type << " but expected " << check_type;
+ }
+
+ NO_INLINE void FailForRegisterType(uint32_t vsrc,
+ RegType::Kind check_kind,
+ uint16_t src_type_id)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ FailForRegisterType(
+ vsrc, reg_types_.GetFromRegKind(check_kind), reg_types_.GetFromId(src_type_id));
+ }
+
+ NO_INLINE void FailForRegisterTypeWide(uint32_t vsrc,
+ const RegType& src_type,
+ const RegType& src_type_h)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "wide register v" << vsrc << " has type " << src_type << "/" << src_type_h;
+ }
+
+ NO_INLINE void FailForRegisterTypeWide(uint32_t vsrc,
+ uint16_t src_type_id,
+ uint16_t src_type_id_h)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ FailForRegisterTypeWide(
+ vsrc, reg_types_.GetFromId(src_type_id), reg_types_.GetFromId(src_type_id_h));
+ }
+
ALWAYS_INLINE inline bool VerifyRegisterType(uint32_t vsrc, const RegType& check_type)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Verify the src register type against the check type refining the type of the register
@@ -725,15 +758,13 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier {
} else {
fail_type = VERIFY_ERROR_BAD_CLASS_HARD;
}
- Fail(fail_type) << "register v" << vsrc << " has type "
- << src_type << " but expected " << check_type;
+ FailForRegisterType(vsrc, check_type, src_type, fail_type);
return false;
}
if (check_type.IsLowHalf()) {
const RegType& src_type_h = work_line_->GetRegisterType(this, vsrc + 1);
if (UNLIKELY(!src_type.CheckWidePair(src_type_h))) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD)
- << "wide register v" << vsrc << " has type " << src_type << "/" << src_type_h;
+ FailForRegisterTypeWide(vsrc, src_type, src_type_h);
return false;
}
}
@@ -744,22 +775,46 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier {
return true;
}
- bool VerifyRegisterTypeWide(uint32_t vsrc,
- const RegType& check_type1,
- const RegType& check_type2)
+ ALWAYS_INLINE inline bool VerifyRegisterType(uint32_t vsrc, RegType::Kind check_kind)
REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(check_type1.CheckWidePair(check_type2));
+ DCHECK(check_kind == RegType::Kind::kInteger || check_kind == RegType::Kind::kFloat);
// Verify the src register type against the check type refining the type of the register
- const RegType& src_type = work_line_->GetRegisterType(this, vsrc);
- if (!check_type1.IsAssignableFrom(src_type, this)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD)
- << "register v" << vsrc << " has type " << src_type << " but expected " << check_type1;
+ uint16_t src_type_id = work_line_->GetRegisterTypeId(vsrc);
+ if (UNLIKELY(src_type_id >= RegTypeCache::NumberOfRegKindCacheIds()) ||
+ UNLIKELY(RegType::AssignabilityFrom(check_kind, RegTypeCache::RegKindForId(src_type_id)) !=
+ RegType::Assignability::kAssignable)) {
+ // Integer or float assignability is never a `kNarrowingConversion` or `kReference`.
+ DCHECK_EQ(
+ RegType::AssignabilityFrom(check_kind, reg_types_.GetFromId(src_type_id).GetKind()),
+ RegType::Assignability::kNotAssignable);
+ FailForRegisterType(vsrc, check_kind, src_type_id);
return false;
}
- const RegType& src_type_h = work_line_->GetRegisterType(this, vsrc + 1);
- if (!src_type.CheckWidePair(src_type_h)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD)
- << "wide register v" << vsrc << " has type " << src_type << "/" << src_type_h;
+ return true;
+ }
+
+ bool VerifyRegisterTypeWide(uint32_t vsrc, RegType::Kind check_kind)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(check_kind == RegType::Kind::kLongLo || check_kind == RegType::Kind::kDoubleLo);
+ // Verify the src register type against the check type refining the type of the register
+ uint16_t src_type_id = work_line_->GetRegisterTypeId(vsrc);
+ if (UNLIKELY(src_type_id >= RegTypeCache::NumberOfRegKindCacheIds()) ||
+ UNLIKELY(RegType::AssignabilityFrom(check_kind, RegTypeCache::RegKindForId(src_type_id)) !=
+ RegType::Assignability::kAssignable)) {
+ // Wide assignability is never a `kNarrowingConversion` or `kReference`.
+ DCHECK_EQ(
+ RegType::AssignabilityFrom(check_kind, reg_types_.GetFromId(src_type_id).GetKind()),
+ RegType::Assignability::kNotAssignable);
+ FailForRegisterType(vsrc, check_kind, src_type_id);
+ return false;
+ }
+ uint16_t src_type_id_h = work_line_->GetRegisterTypeId(vsrc + 1);
+ uint16_t expected_src_type_id_h =
+ RegTypeCache::IdForRegKind(RegType::ToHighHalf(RegTypeCache::RegKindForId(src_type_id)));
+ DCHECK_EQ(src_type_id_h == expected_src_type_id_h,
+ reg_types_.GetFromId(src_type_id).CheckWidePair(reg_types_.GetFromId(src_type_id_h)));
+ if (UNLIKELY(src_type_id_h != expected_src_type_id_h)) {
+ FailForRegisterTypeWide(vsrc, src_type_id, src_type_id_h);
return false;
}
// The register at vsrc has a defined type, we know the lower-upper-bound, but this is less
@@ -773,41 +828,37 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier {
* Verify types for a simple two-register instruction (e.g. "neg-int").
* "dst_type" is stored into vA, and "src_type" is verified against vB.
*/
- void CheckUnaryOp(const Instruction* inst, const RegType& dst_type, const RegType& src_type)
+ void CheckUnaryOp(const Instruction* inst, RegType::Kind dst_kind, RegType::Kind src_kind)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (VerifyRegisterType(inst->VRegB_12x(), src_type)) {
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_12x(), dst_type);
+ if (VerifyRegisterType(inst->VRegB_12x(), src_kind)) {
+ work_line_->SetRegisterType(inst->VRegA_12x(), dst_kind);
}
}
void CheckUnaryOpWide(const Instruction* inst,
- const RegType& dst_type1,
- const RegType& dst_type2,
- const RegType& src_type1,
- const RegType& src_type2)
+ RegType::Kind dst_kind,
+ RegType::Kind src_kind)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (VerifyRegisterTypeWide(inst->VRegB_12x(), src_type1, src_type2)) {
- work_line_->SetRegisterTypeWide(inst->VRegA_12x(), dst_type1, dst_type2);
+ if (VerifyRegisterTypeWide(inst->VRegB_12x(), src_kind)) {
+ work_line_->SetRegisterTypeWide(inst->VRegA_12x(), dst_kind, RegType::ToHighHalf(dst_kind));
}
}
void CheckUnaryOpToWide(const Instruction* inst,
- const RegType& dst_type1,
- const RegType& dst_type2,
- const RegType& src_type)
+ RegType::Kind dst_kind,
+ RegType::Kind src_kind)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (VerifyRegisterType(inst->VRegB_12x(), src_type)) {
- work_line_->SetRegisterTypeWide(inst->VRegA_12x(), dst_type1, dst_type2);
+ if (VerifyRegisterType(inst->VRegB_12x(), src_kind)) {
+ work_line_->SetRegisterTypeWide(inst->VRegA_12x(), dst_kind, RegType::ToHighHalf(dst_kind));
}
}
void CheckUnaryOpFromWide(const Instruction* inst,
- const RegType& dst_type,
- const RegType& src_type1,
- const RegType& src_type2)
+ RegType::Kind dst_kind,
+ RegType::Kind src_kind)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (VerifyRegisterTypeWide(inst->VRegB_12x(), src_type1, src_type2)) {
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_12x(), dst_type);
+ if (VerifyRegisterTypeWide(inst->VRegB_12x(), src_kind)) {
+ work_line_->SetRegisterType(inst->VRegA_12x(), dst_kind);
}
}
@@ -817,49 +868,60 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier {
* against vB/vC.
*/
void CheckBinaryOp(const Instruction* inst,
- const RegType& dst_type,
- const RegType& src_type1,
- const RegType& src_type2,
+ RegType::Kind dst_kind,
+ RegType::Kind src_kind1,
+ RegType::Kind src_kind2,
bool check_boolean_op)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint32_t vregA = inst->VRegA_23x();
const uint32_t vregB = inst->VRegB_23x();
const uint32_t vregC = inst->VRegC_23x();
- if (VerifyRegisterType(vregB, src_type1) &&
- VerifyRegisterType(vregC, src_type2)) {
+ if (VerifyRegisterType(vregB, src_kind1) &&
+ VerifyRegisterType(vregC, src_kind2)) {
if (check_boolean_op) {
- DCHECK(dst_type.IsInteger());
- if (work_line_->GetRegisterType(this, vregB).IsBooleanTypes() &&
- work_line_->GetRegisterType(this, vregC).IsBooleanTypes()) {
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_23x(), reg_types_.Boolean());
+ DCHECK_EQ(dst_kind, RegType::Kind::kInteger);
+ if (RegType::IsBooleanTypes(
+ RegTypeCache::RegKindForId(work_line_->GetRegisterTypeId(vregB))) &&
+ RegType::IsBooleanTypes(
+ RegTypeCache::RegKindForId(work_line_->GetRegisterTypeId(vregC)))) {
+ work_line_->SetRegisterType(vregA, RegType::Kind::kBoolean);
return;
}
}
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_23x(), dst_type);
+ work_line_->SetRegisterType(vregA, dst_kind);
}
}
void CheckBinaryOpWide(const Instruction* inst,
- const RegType& dst_type1,
- const RegType& dst_type2,
- const RegType& src_type1_1,
- const RegType& src_type1_2,
- const RegType& src_type2_1,
- const RegType& src_type2_2)
+ RegType::Kind dst_kind,
+ RegType::Kind src_kind1,
+ RegType::Kind src_kind2)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (VerifyRegisterTypeWide(inst->VRegB_23x(), src_kind1) &&
+ VerifyRegisterTypeWide(inst->VRegC_23x(), src_kind2)) {
+ work_line_->SetRegisterTypeWide(inst->VRegA_23x(), dst_kind, RegType::ToHighHalf(dst_kind));
+ }
+ }
+
+ void CheckBinaryOpWideCmp(const Instruction* inst,
+ RegType::Kind dst_kind,
+ RegType::Kind src_kind1,
+ RegType::Kind src_kind2)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (VerifyRegisterTypeWide(inst->VRegB_23x(), src_type1_1, src_type1_2) &&
- VerifyRegisterTypeWide(inst->VRegC_23x(), src_type2_1, src_type2_2)) {
- work_line_->SetRegisterTypeWide(inst->VRegA_23x(), dst_type1, dst_type2);
+ if (VerifyRegisterTypeWide(inst->VRegB_23x(), src_kind1) &&
+ VerifyRegisterTypeWide(inst->VRegC_23x(), src_kind2)) {
+ work_line_->SetRegisterType(inst->VRegA_23x(), dst_kind);
}
}
void CheckBinaryOpWideShift(const Instruction* inst,
- const RegType& long_lo_type,
- const RegType& long_hi_type,
- const RegType& int_type)
+ RegType::Kind long_lo_kind,
+ RegType::Kind int_kind)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (VerifyRegisterTypeWide(inst->VRegB_23x(), long_lo_type, long_hi_type) &&
- VerifyRegisterType(inst->VRegC_23x(), int_type)) {
- work_line_->SetRegisterTypeWide(inst->VRegA_23x(), long_lo_type, long_hi_type);
+ if (VerifyRegisterTypeWide(inst->VRegB_23x(), long_lo_kind) &&
+ VerifyRegisterType(inst->VRegC_23x(), int_kind)) {
+ RegType::Kind long_hi_kind = RegType::ToHighHalf(long_lo_kind);
+ work_line_->SetRegisterTypeWide(inst->VRegA_23x(), long_lo_kind, long_hi_kind);
}
}
@@ -868,53 +930,52 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier {
* are verified against vA/vB, then "dst_type" is stored into vA.
*/
void CheckBinaryOp2addr(const Instruction* inst,
- const RegType& dst_type,
- const RegType& src_type1,
- const RegType& src_type2,
+ RegType::Kind dst_kind,
+ RegType::Kind src_kind1,
+ RegType::Kind src_kind2,
bool check_boolean_op)
REQUIRES_SHARED(Locks::mutator_lock_) {
const uint32_t vregA = inst->VRegA_12x();
const uint32_t vregB = inst->VRegB_12x();
- if (VerifyRegisterType(vregA, src_type1) &&
- VerifyRegisterType(vregB, src_type2)) {
+ if (VerifyRegisterType(vregA, src_kind1) &&
+ VerifyRegisterType(vregB, src_kind2)) {
if (check_boolean_op) {
- DCHECK(dst_type.IsInteger());
- if (work_line_->GetRegisterType(this, vregA).IsBooleanTypes() &&
- work_line_->GetRegisterType(this, vregB).IsBooleanTypes()) {
- work_line_->SetRegisterType<LockOp::kClear>(vregA, reg_types_.Boolean());
+ DCHECK_EQ(dst_kind, RegType::Kind::kInteger);
+ if (RegType::IsBooleanTypes(
+ RegTypeCache::RegKindForId(work_line_->GetRegisterTypeId(vregA))) &&
+ RegType::IsBooleanTypes(
+ RegTypeCache::RegKindForId(work_line_->GetRegisterTypeId(vregB)))) {
+ work_line_->SetRegisterType(vregA, RegType::Kind::kBoolean);
return;
}
}
- work_line_->SetRegisterType<LockOp::kClear>(vregA, dst_type);
+ work_line_->SetRegisterType(vregA, dst_kind);
}
}
void CheckBinaryOp2addrWide(const Instruction* inst,
- const RegType& dst_type1,
- const RegType& dst_type2,
- const RegType& src_type1_1,
- const RegType& src_type1_2,
- const RegType& src_type2_1,
- const RegType& src_type2_2)
+ RegType::Kind dst_kind,
+ RegType::Kind src_kind1,
+ RegType::Kind src_kind2)
REQUIRES_SHARED(Locks::mutator_lock_) {
const uint32_t vregA = inst->VRegA_12x();
const uint32_t vregB = inst->VRegB_12x();
- if (VerifyRegisterTypeWide(vregA, src_type1_1, src_type1_2) &&
- VerifyRegisterTypeWide(vregB, src_type2_1, src_type2_2)) {
- work_line_->SetRegisterTypeWide(vregA, dst_type1, dst_type2);
+ if (VerifyRegisterTypeWide(vregA, src_kind1) &&
+ VerifyRegisterTypeWide(vregB, src_kind2)) {
+ work_line_->SetRegisterTypeWide(vregA, dst_kind, RegType::ToHighHalf(dst_kind));
}
}
void CheckBinaryOp2addrWideShift(const Instruction* inst,
- const RegType& long_lo_type,
- const RegType& long_hi_type,
- const RegType& int_type)
+ RegType::Kind long_lo_kind,
+ RegType::Kind int_kind)
REQUIRES_SHARED(Locks::mutator_lock_) {
const uint32_t vregA = inst->VRegA_12x();
const uint32_t vregB = inst->VRegB_12x();
- if (VerifyRegisterTypeWide(vregA, long_lo_type, long_hi_type) &&
- VerifyRegisterType(vregB, int_type)) {
- work_line_->SetRegisterTypeWide(vregA, long_lo_type, long_hi_type);
+ if (VerifyRegisterTypeWide(vregA, long_lo_kind) &&
+ VerifyRegisterType(vregB, int_kind)) {
+ RegType::Kind long_hi_kind = RegType::ToHighHalf(long_lo_kind);
+ work_line_->SetRegisterTypeWide(vregA, long_lo_kind, long_hi_kind);
}
}
@@ -925,24 +986,24 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier {
* If "check_boolean_op" is set, we use the constant value in vC.
*/
void CheckLiteralOp(const Instruction* inst,
- const RegType& dst_type,
- const RegType& src_type,
+ RegType::Kind dst_kind,
+ RegType::Kind src_kind,
bool check_boolean_op,
bool is_lit16)
REQUIRES_SHARED(Locks::mutator_lock_) {
const uint32_t vregA = is_lit16 ? inst->VRegA_22s() : inst->VRegA_22b();
const uint32_t vregB = is_lit16 ? inst->VRegB_22s() : inst->VRegB_22b();
- if (VerifyRegisterType(vregB, src_type)) {
+ if (VerifyRegisterType(vregB, src_kind)) {
if (check_boolean_op) {
- DCHECK(dst_type.IsInteger());
+ DCHECK_EQ(dst_kind, RegType::Kind::kInteger);
/* check vB with the call, then check the constant manually */
const uint32_t val = is_lit16 ? inst->VRegC_22s() : inst->VRegC_22b();
if (work_line_->GetRegisterType(this, vregB).IsBooleanTypes() && (val == 0 || val == 1)) {
- work_line_->SetRegisterType<LockOp::kClear>(vregA, reg_types_.Boolean());
+ work_line_->SetRegisterType(vregA, RegType::Kind::kBoolean);
return;
}
}
- work_line_->SetRegisterType<LockOp::kClear>(vregA, dst_type);
+ work_line_->SetRegisterType(vregA, dst_kind);
}
}
@@ -950,7 +1011,7 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier {
return &GetModifiableInstructionFlags(work_insn_idx_);
}
- const RegType& DetermineCat1Constant(int32_t value)
+ RegType::Kind DetermineCat1Constant(int32_t value)
REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE bool FailOrAbort(bool condition, const char* error_msg, uint32_t work_insn_idx);
@@ -1968,22 +2029,22 @@ bool MethodVerifier<kVerifierDebug>::SetTypesFromSignature() {
}
break;
case 'Z':
- reg_line->SetRegisterType<LockOp::kClear>(arg_start + cur_arg, reg_types_.Boolean());
+ reg_line->SetRegisterType(arg_start + cur_arg, RegType::Kind::kBoolean);
break;
case 'C':
- reg_line->SetRegisterType<LockOp::kClear>(arg_start + cur_arg, reg_types_.Char());
+ reg_line->SetRegisterType(arg_start + cur_arg, RegType::Kind::kChar);
break;
case 'B':
- reg_line->SetRegisterType<LockOp::kClear>(arg_start + cur_arg, reg_types_.Byte());
+ reg_line->SetRegisterType(arg_start + cur_arg, RegType::Kind::kByte);
break;
case 'I':
- reg_line->SetRegisterType<LockOp::kClear>(arg_start + cur_arg, reg_types_.Integer());
+ reg_line->SetRegisterType(arg_start + cur_arg, RegType::Kind::kInteger);
break;
case 'S':
- reg_line->SetRegisterType<LockOp::kClear>(arg_start + cur_arg, reg_types_.Short());
+ reg_line->SetRegisterType(arg_start + cur_arg, RegType::Kind::kShort);
break;
case 'F':
- reg_line->SetRegisterType<LockOp::kClear>(arg_start + cur_arg, reg_types_.Float());
+ reg_line->SetRegisterType(arg_start + cur_arg, RegType::Kind::kFloat);
break;
case 'J':
case 'D': {
@@ -2291,6 +2352,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
RegisterLineArenaUniquePtr branch_line;
RegisterLineArenaUniquePtr fallthrough_line;
+ using enum RegType::Kind;
switch (inst->Opcode()) {
case Instruction::NOP:
/*
@@ -2398,7 +2460,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
} else {
/* check the register contents */
const uint32_t vregA = inst->VRegA_11x();
- bool success = VerifyRegisterType(vregA, return_type);
+ bool success = VerifyRegisterTypeWide(vregA, return_type.GetKind());
if (!success) {
AppendToLastFailMessage(StringPrintf(" return-wide on invalid register v%d", vregA));
}
@@ -2445,22 +2507,22 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
/* could be boolean, int, float, or a null reference */
case Instruction::CONST_4: {
int32_t val = static_cast<int32_t>(inst->VRegB_11n() << 28) >> 28;
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_11n(), DetermineCat1Constant(val));
+ work_line_->SetRegisterType(inst->VRegA_11n(), DetermineCat1Constant(val));
break;
}
case Instruction::CONST_16: {
int16_t val = static_cast<int16_t>(inst->VRegB_21s());
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_21s(), DetermineCat1Constant(val));
+ work_line_->SetRegisterType(inst->VRegA_21s(), DetermineCat1Constant(val));
break;
}
case Instruction::CONST: {
int32_t val = inst->VRegB_31i();
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_31i(), DetermineCat1Constant(val));
+ work_line_->SetRegisterType(inst->VRegA_31i(), DetermineCat1Constant(val));
break;
}
case Instruction::CONST_HIGH16: {
int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_21h(), DetermineCat1Constant(val));
+ work_line_->SetRegisterType(inst->VRegA_21h(), DetermineCat1Constant(val));
break;
}
/* could be long or double; resolved upon use */
@@ -2633,7 +2695,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
DCHECK_NE(failures_.size(), 0U);
if (!is_checkcast) {
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_22c(), reg_types_.Boolean());
+ work_line_->SetRegisterType(inst->VRegA_22c(), kBoolean);
}
break; // bad class
}
@@ -2664,7 +2726,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
if (is_checkcast) {
work_line_->SetRegisterType<LockOp::kKeep>(inst->VRegA_21c(), res_type);
} else {
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_22c(), reg_types_.Boolean());
+ work_line_->SetRegisterType(inst->VRegA_22c(), kBoolean);
}
}
break;
@@ -2676,7 +2738,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
// ie not an array or null
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type;
} else {
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_12x(), reg_types_.Integer());
+ work_line_->SetRegisterType(inst->VRegA_12x(), kInteger);
}
} else {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type;
@@ -2718,34 +2780,14 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
break;
case Instruction::CMPL_FLOAT:
case Instruction::CMPG_FLOAT:
- if (!VerifyRegisterType(inst->VRegB_23x(), reg_types_.Float())) {
- break;
- }
- if (!VerifyRegisterType(inst->VRegC_23x(), reg_types_.Float())) {
- break;
- }
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_23x(), reg_types_.Integer());
+ CheckBinaryOp(inst, kInteger, kFloat, kFloat, /*check_boolean_op=*/ false);
break;
case Instruction::CMPL_DOUBLE:
case Instruction::CMPG_DOUBLE:
- if (!VerifyRegisterTypeWide(inst->VRegB_23x(), reg_types_.DoubleLo(),
- reg_types_.DoubleHi())) {
- break;
- }
- if (!VerifyRegisterTypeWide(inst->VRegC_23x(), reg_types_.DoubleLo(),
- reg_types_.DoubleHi())) {
- break;
- }
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_23x(), reg_types_.Integer());
+ CheckBinaryOpWideCmp(inst, kInteger, kDoubleLo, kDoubleLo);
break;
case Instruction::CMP_LONG:
- if (!VerifyRegisterTypeWide(inst->VRegB_23x(), reg_types_.LongLo(), reg_types_.LongHi())) {
- break;
- }
- if (!VerifyRegisterTypeWide(inst->VRegC_23x(), reg_types_.LongLo(), reg_types_.LongHi())) {
- break;
- }
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_23x(), reg_types_.Integer());
+ CheckBinaryOpWideCmp(inst, kInteger, kLongLo, kLongLo);
break;
case Instruction::THROW: {
const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegA_11x());
@@ -2771,7 +2813,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
case Instruction::PACKED_SWITCH:
case Instruction::SPARSE_SWITCH:
/* verify that vAA is an integer, or can be converted to one */
- VerifyRegisterType(inst->VRegA_31t(), reg_types_.Integer());
+ VerifyRegisterType(inst->VRegA_31t(), kInteger);
break;
case Instruction::FILL_ARRAY_DATA: {
@@ -3321,69 +3363,62 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
}
case Instruction::NEG_INT:
case Instruction::NOT_INT:
- CheckUnaryOp(inst, reg_types_.Integer(), reg_types_.Integer());
+ CheckUnaryOp(inst, kInteger, kInteger);
break;
case Instruction::NEG_LONG:
case Instruction::NOT_LONG:
- CheckUnaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.LongLo(), reg_types_.LongHi());
+ CheckUnaryOpWide(inst, kLongLo, kLongLo);
break;
case Instruction::NEG_FLOAT:
- CheckUnaryOp(inst, reg_types_.Float(), reg_types_.Float());
+ CheckUnaryOp(inst, kFloat, kFloat);
break;
case Instruction::NEG_DOUBLE:
- CheckUnaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
+ CheckUnaryOpWide(inst, kDoubleLo, kDoubleLo);
break;
case Instruction::INT_TO_LONG:
- CheckUnaryOpToWide(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.Integer());
+ CheckUnaryOpToWide(inst, kLongLo, kInteger);
break;
case Instruction::INT_TO_FLOAT:
- CheckUnaryOp(inst, reg_types_.Float(), reg_types_.Integer());
+ CheckUnaryOp(inst, kFloat, kInteger);
break;
case Instruction::INT_TO_DOUBLE:
- CheckUnaryOpToWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.Integer());
+ CheckUnaryOpToWide(inst, kDoubleLo, kInteger);
break;
case Instruction::LONG_TO_INT:
- CheckUnaryOpFromWide(inst, reg_types_.Integer(), reg_types_.LongLo(), reg_types_.LongHi());
+ CheckUnaryOpFromWide(inst, kInteger, kLongLo);
break;
case Instruction::LONG_TO_FLOAT:
- CheckUnaryOpFromWide(inst, reg_types_.Float(), reg_types_.LongLo(), reg_types_.LongHi());
+ CheckUnaryOpFromWide(inst, kFloat, kLongLo);
break;
case Instruction::LONG_TO_DOUBLE:
- CheckUnaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.LongLo(), reg_types_.LongHi());
+ CheckUnaryOpWide(inst, kDoubleLo, kLongLo);
break;
case Instruction::FLOAT_TO_INT:
- CheckUnaryOp(inst, reg_types_.Integer(), reg_types_.Float());
+ CheckUnaryOp(inst, kInteger, kFloat);
break;
case Instruction::FLOAT_TO_LONG:
- CheckUnaryOpToWide(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.Float());
+ CheckUnaryOpToWide(inst, kLongLo, kFloat);
break;
case Instruction::FLOAT_TO_DOUBLE:
- CheckUnaryOpToWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(), reg_types_.Float());
+ CheckUnaryOpToWide(inst, kDoubleLo, kFloat);
break;
case Instruction::DOUBLE_TO_INT:
- CheckUnaryOpFromWide(inst, reg_types_.Integer(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
+ CheckUnaryOpFromWide(inst, kInteger, kDoubleLo);
break;
case Instruction::DOUBLE_TO_LONG:
- CheckUnaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
+ CheckUnaryOpWide(inst, kLongLo, kDoubleLo);
break;
case Instruction::DOUBLE_TO_FLOAT:
- CheckUnaryOpFromWide(inst, reg_types_.Float(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
+ CheckUnaryOpFromWide(inst, kFloat, kDoubleLo);
break;
case Instruction::INT_TO_BYTE:
- CheckUnaryOp(inst, reg_types_.Byte(), reg_types_.Integer());
+ CheckUnaryOp(inst, kByte, kInteger);
break;
case Instruction::INT_TO_CHAR:
- CheckUnaryOp(inst, reg_types_.Char(), reg_types_.Integer());
+ CheckUnaryOp(inst, kChar, kInteger);
break;
case Instruction::INT_TO_SHORT:
- CheckUnaryOp(inst, reg_types_.Short(), reg_types_.Integer());
+ CheckUnaryOp(inst, kShort, kInteger);
break;
case Instruction::ADD_INT:
@@ -3394,12 +3429,12 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
case Instruction::SHL_INT:
case Instruction::SHR_INT:
case Instruction::USHR_INT:
- CheckBinaryOp(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false);
+ CheckBinaryOp(inst, kInteger, kInteger, kInteger, /*check_boolean_op=*/ false);
break;
case Instruction::AND_INT:
case Instruction::OR_INT:
case Instruction::XOR_INT:
- CheckBinaryOp(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), true);
+ CheckBinaryOp(inst, kInteger, kInteger, kInteger, /*check_boolean_op=*/ true);
break;
case Instruction::ADD_LONG:
case Instruction::SUB_LONG:
@@ -3409,31 +3444,27 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
case Instruction::AND_LONG:
case Instruction::OR_LONG:
case Instruction::XOR_LONG:
- CheckBinaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.LongLo(), reg_types_.LongHi());
+ CheckBinaryOpWide(inst, kLongLo, kLongLo, kLongLo);
break;
case Instruction::SHL_LONG:
case Instruction::SHR_LONG:
case Instruction::USHR_LONG:
/* shift distance is Int, making these different from other binary operations */
- CheckBinaryOpWideShift(inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.Integer());
+ CheckBinaryOpWideShift(inst, kLongLo, kInteger);
break;
case Instruction::ADD_FLOAT:
case Instruction::SUB_FLOAT:
case Instruction::MUL_FLOAT:
case Instruction::DIV_FLOAT:
case Instruction::REM_FLOAT:
- CheckBinaryOp(inst, reg_types_.Float(), reg_types_.Float(), reg_types_.Float(), false);
+ CheckBinaryOp(inst, kFloat, kFloat, kFloat, /*check_boolean_op=*/ false);
break;
case Instruction::ADD_DOUBLE:
case Instruction::SUB_DOUBLE:
case Instruction::MUL_DOUBLE:
case Instruction::DIV_DOUBLE:
case Instruction::REM_DOUBLE:
- CheckBinaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
+ CheckBinaryOpWide(inst, kDoubleLo, kDoubleLo, kDoubleLo);
break;
case Instruction::ADD_INT_2ADDR:
case Instruction::SUB_INT_2ADDR:
@@ -3442,18 +3473,15 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
case Instruction::SHL_INT_2ADDR:
case Instruction::SHR_INT_2ADDR:
case Instruction::USHR_INT_2ADDR:
- CheckBinaryOp2addr(
- inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false);
+ CheckBinaryOp2addr(inst, kInteger, kInteger, kInteger, /*check_boolean_op=*/ false);
break;
case Instruction::AND_INT_2ADDR:
case Instruction::OR_INT_2ADDR:
case Instruction::XOR_INT_2ADDR:
- CheckBinaryOp2addr(
- inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), true);
+ CheckBinaryOp2addr(inst, kInteger, kInteger, kInteger, /*check_boolean_op=*/ true);
break;
case Instruction::DIV_INT_2ADDR:
- CheckBinaryOp2addr(
- inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false);
+ CheckBinaryOp2addr(inst, kInteger, kInteger, kInteger, /*check_boolean_op=*/ false);
break;
case Instruction::ADD_LONG_2ADDR:
case Instruction::SUB_LONG_2ADDR:
@@ -3463,43 +3491,38 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
case Instruction::AND_LONG_2ADDR:
case Instruction::OR_LONG_2ADDR:
case Instruction::XOR_LONG_2ADDR:
- CheckBinaryOp2addrWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.LongLo(), reg_types_.LongHi());
+ CheckBinaryOp2addrWide(inst, kLongLo, kLongLo, kLongLo);
break;
case Instruction::SHL_LONG_2ADDR:
case Instruction::SHR_LONG_2ADDR:
case Instruction::USHR_LONG_2ADDR:
- CheckBinaryOp2addrWideShift(
- inst, reg_types_.LongLo(), reg_types_.LongHi(), reg_types_.Integer());
+ CheckBinaryOp2addrWideShift(inst, kLongLo, kInteger);
break;
case Instruction::ADD_FLOAT_2ADDR:
case Instruction::SUB_FLOAT_2ADDR:
case Instruction::MUL_FLOAT_2ADDR:
case Instruction::DIV_FLOAT_2ADDR:
case Instruction::REM_FLOAT_2ADDR:
- CheckBinaryOp2addr(inst, reg_types_.Float(), reg_types_.Float(), reg_types_.Float(), false);
+ CheckBinaryOp2addr(inst, kFloat, kFloat, kFloat, /*check_boolean_op=*/ false);
break;
case Instruction::ADD_DOUBLE_2ADDR:
case Instruction::SUB_DOUBLE_2ADDR:
case Instruction::MUL_DOUBLE_2ADDR:
case Instruction::DIV_DOUBLE_2ADDR:
case Instruction::REM_DOUBLE_2ADDR:
- CheckBinaryOp2addrWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
+ CheckBinaryOp2addrWide(inst, kDoubleLo, kDoubleLo, kDoubleLo);
break;
case Instruction::ADD_INT_LIT16:
case Instruction::RSUB_INT_LIT16:
case Instruction::MUL_INT_LIT16:
case Instruction::DIV_INT_LIT16:
case Instruction::REM_INT_LIT16:
- CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), false, true);
+ CheckLiteralOp(inst, kInteger, kInteger, /*check_boolean_op=*/ false, /*is_lit16=*/ true);
break;
case Instruction::AND_INT_LIT16:
case Instruction::OR_INT_LIT16:
case Instruction::XOR_INT_LIT16:
- CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, true);
+ CheckLiteralOp(inst, kInteger, kInteger, /*check_boolean_op=*/ true, /*is_lit16=*/ true);
break;
case Instruction::ADD_INT_LIT8:
case Instruction::RSUB_INT_LIT8:
@@ -3509,12 +3532,12 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
case Instruction::SHL_INT_LIT8:
case Instruction::SHR_INT_LIT8:
case Instruction::USHR_INT_LIT8:
- CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), false, false);
+ CheckLiteralOp(inst, kInteger, kInteger, /*check_boolean_op=*/ false, /*is_lit16=*/ false);
break;
case Instruction::AND_INT_LIT8:
case Instruction::OR_INT_LIT8:
case Instruction::XOR_INT_LIT8:
- CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, false);
+ CheckLiteralOp(inst, kInteger, kInteger, /*check_boolean_op=*/ true, /*is_lit16=*/ false);
break;
/* These should never appear during verification. */
@@ -4468,7 +4491,7 @@ void MethodVerifier<kVerifierDebug>::VerifyNewArray(const Instruction* inst,
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "new-array on non-array class " << res_type;
} else if (!is_filled) {
/* make sure "size" register is valid type */
- VerifyRegisterType(inst->VRegB_22c(), reg_types_.Integer());
+ VerifyRegisterType(inst->VRegB_22c(), RegType::Kind::kInteger);
/* set register type to array class */
work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_22c(), res_type);
} else {
@@ -4512,7 +4535,7 @@ void MethodVerifier<kVerifierDebug>::VerifyAGet(const Instruction* inst,
} else if (insn_type.IsInteger()) {
// Pick a non-zero constant (to distinguish with null) that can fit in any primitive.
// We cannot use 'insn_type' as it could be a float array or an int array.
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_23x(), DetermineCat1Constant(1));
+ work_line_->SetRegisterType(inst->VRegA_23x(), DetermineCat1Constant(1));
} else if (insn_type.IsCategory1Types()) {
// Category 1
// The 'insn_type' is exactly the type we need.
@@ -4534,7 +4557,7 @@ void MethodVerifier<kVerifierDebug>::VerifyAGet(const Instruction* inst,
Fail(VERIFY_ERROR_NO_CLASS) << "cannot verify aget for " << array_type
<< " because of missing class";
// Approximate with java.lang.Object[].
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_23x(), reg_types_.JavaLangObject());
+ work_line_->SetRegisterType(inst->VRegA_23x(), RegType::Kind::kJavaLangObject);
}
} else {
/* verify the class */
@@ -5017,26 +5040,26 @@ const RegType& MethodVerifier<kVerifierDebug>::GetMethodReturnType() {
}
template <bool kVerifierDebug>
-const RegType& MethodVerifier<kVerifierDebug>::DetermineCat1Constant(int32_t value) {
+RegType::Kind MethodVerifier<kVerifierDebug>::DetermineCat1Constant(int32_t value) {
// Imprecise constant type.
if (value < -32768) {
- return reg_types_.IntegerConstant();
+ return RegType::Kind::kIntegerConstant;
} else if (value < -128) {
- return reg_types_.ShortConstant();
+ return RegType::Kind::kShortConstant;
} else if (value < 0) {
- return reg_types_.ByteConstant();
+ return RegType::Kind::kByteConstant;
} else if (value == 0) {
- return reg_types_.Zero();
+ return RegType::Kind::kZero;
} else if (value == 1) {
- return reg_types_.BooleanConstant();
+ return RegType::Kind::kBooleanConstant;
} else if (value < 128) {
- return reg_types_.PositiveByteConstant();
+ return RegType::Kind::kPositiveByteConstant;
} else if (value < 32768) {
- return reg_types_.PositiveShortConstant();
+ return RegType::Kind::kPositiveShortConstant;
} else if (value < 65536) {
- return reg_types_.CharConstant();
+ return RegType::Kind::kCharConstant;
} else {
- return reg_types_.IntegerConstant();
+ return RegType::Kind::kIntegerConstant;
}
}
diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h
index 0e14583561..bc6c6c3ee9 100644
--- a/runtime/verifier/reg_type-inl.h
+++ b/runtime/verifier/reg_type-inl.h
@@ -40,18 +40,18 @@ inline Handle<mirror::Class> RegType::GetClassHandle() const {
namespace detail {
-class RegTypeAssignableImpl final : RegType {
+class RegTypeAssignabilityImpl final : RegType {
public:
- explicit constexpr RegTypeAssignableImpl(RegType::Kind kind)
+ explicit constexpr RegTypeAssignabilityImpl(RegType::Kind kind)
: RegType("", /* unused cache id */ 0, kind) {}
static constexpr Assignability AssignableFrom(RegType::Kind lhs_kind, RegType::Kind rhs_kind);
};
-constexpr RegType::Assignability RegTypeAssignableImpl::AssignableFrom(RegType::Kind lhs_kind,
- RegType::Kind rhs_kind) {
- RegTypeAssignableImpl lhs(lhs_kind);
- RegTypeAssignableImpl rhs(rhs_kind);
+constexpr RegType::Assignability RegTypeAssignabilityImpl::AssignableFrom(RegType::Kind lhs_kind,
+ RegType::Kind rhs_kind) {
+ RegTypeAssignabilityImpl lhs(lhs_kind);
+ RegTypeAssignabilityImpl rhs(rhs_kind);
auto maybe_narrowing_conversion = [&rhs]() constexpr {
return rhs.IsIntegralTypes() ? Assignability::kNarrowingConversion
: Assignability::kNotAssignable;
@@ -105,28 +105,32 @@ constexpr RegType::Assignability RegTypeAssignableImpl::AssignableFrom(RegType::
} // namespace detail
-inline bool RegType::AssignableFrom(const RegType& lhs,
- const RegType& rhs,
- bool strict,
- MethodVerifier* verifier) {
+inline RegType::Assignability RegType::AssignabilityFrom(Kind lhs, Kind rhs) {
static constexpr size_t kNumKinds = NumberOfKinds();
using AssignabilityTable = std::array<std::array<Assignability, kNumKinds>, kNumKinds>;
static constexpr AssignabilityTable kAssignabilityTable = []() constexpr {
AssignabilityTable result;
for (size_t lhs = 0u; lhs != kNumKinds; ++lhs) {
for (size_t rhs = 0u; rhs != kNumKinds; ++rhs) {
- result[lhs][rhs] = detail::RegTypeAssignableImpl::AssignableFrom(
+ result[lhs][rhs] = detail::RegTypeAssignabilityImpl::AssignableFrom(
enum_cast<RegType::Kind>(lhs), enum_cast<RegType::Kind>(rhs));
}
}
return result;
}();
+ return kAssignabilityTable[lhs][rhs];
+}
+
+inline bool RegType::AssignableFrom(const RegType& lhs,
+ const RegType& rhs,
+ bool strict,
+ MethodVerifier* verifier) {
if (lhs.Equals(rhs)) {
return true;
}
- Assignability assignable = kAssignabilityTable[lhs.GetKind()][rhs.GetKind()];
+ Assignability assignable = AssignabilityFrom(lhs.GetKind(), rhs.GetKind());
DCHECK(assignable != Assignability::kInvalid)
<< "Unexpected register type in IsAssignableFrom: '" << lhs << "' := '" << rhs << "'";
if (assignable == Assignability::kAssignable) {
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index e69b4d96cb..c5e979ac71 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -55,6 +55,30 @@ std::ostream& operator<<(std::ostream& os, RegType::Kind kind) {
return os << kind_name;
}
+std::ostream& operator<<(std::ostream& os, RegType::Assignability assignability) {
+ const char* assignability_name;
+ switch (assignability) {
+ case RegType::Assignability::kAssignable:
+ assignability_name = "Assignable";
+ break;
+ case RegType::Assignability::kNotAssignable:
+ assignability_name = "NotAssignable";
+ break;
+ case RegType::Assignability::kNarrowingConversion:
+ assignability_name = "NarrowingConversion";
+ break;
+ case RegType::Assignability::kReference:
+ assignability_name = "Reference";
+ break;
+ case RegType::Assignability::kInvalid:
+ assignability_name = "Invalid";
+ break;
+ default:
+ return os << "Corrupted RegType::Assignability: " << static_cast<uint32_t>(assignability);
+ }
+ return os << assignability_name;
+}
+
std::string RegType::Dump() const {
std::string_view reference_tag;
switch (GetKind()) {
@@ -469,7 +493,7 @@ const RegType& RegType::Merge(const RegType& incoming_type,
Kind merge_kind = kMergeTable[GetKind()][incoming_type.GetKind()];
if (merge_kind != Kind::kUnresolvedMergedReference) {
- return RegTypeFromKind(reg_types, merge_kind);
+ return reg_types->GetFromRegKind(merge_kind);
} else {
// The `UnresolvedMergedReference` tells us to do non-trivial reference merging which
// requires more information than the two kinds used for the lookup in `kMergeTable`.
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index a926671924..56c0485bf4 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -131,19 +131,35 @@ class RegType {
constexpr bool IsUninitializedTypes() const;
constexpr bool IsUnresolvedTypes() const;
- constexpr bool IsLowHalf() const { return (IsLongLo() || IsDoubleLo() || IsConstantLo()); }
- constexpr bool IsHighHalf() const { return (IsLongHi() || IsDoubleHi() || IsConstantHi()); }
+ static constexpr bool IsLowHalf(Kind kind) {
+ return kind == Kind::kLongLo || kind == Kind::kDoubleLo || kind == Kind::kConstantLo;
+ }
+ static constexpr bool IsHighHalf(Kind kind) {
+ return kind == Kind::kLongHi || kind == Kind::kDoubleHi || kind == Kind::kConstantHi;
+ }
+
+ constexpr bool IsLowHalf() const { return IsLowHalf(GetKind()); }
+ constexpr bool IsHighHalf() const { return IsHighHalf(GetKind()); }
constexpr bool IsLongOrDoubleTypes() const { return IsLowHalf(); }
+ static constexpr Kind ToHighHalf(Kind low) {
+ static_assert(Kind::kConstantLo + 1 == Kind::kConstantHi);
+ static_assert(Kind::kDoubleLo + 1 == Kind::kDoubleHi);
+ static_assert(Kind::kLongLo + 1 == Kind::kLongHi);
+ DCHECK(low == Kind::kConstantLo || low == Kind::kDoubleLo || low == Kind::kLongLo);
+ return enum_cast<Kind>(low + 1);
+ }
+
+ // Check that `low` is the low half, and that `high` is its matching high-half.
+ static inline bool CheckWidePair(Kind low, Kind high) {
+ return (low == Kind::kConstantLo || low == Kind::kDoubleLo || low == Kind::kLongLo) &&
+ high == ToHighHalf(low);
+ }
// Check this is the low half, and that type_h is its matching high-half.
inline bool CheckWidePair(const RegType& type_h) const {
- if (IsLowHalf()) {
- return ((IsConstantLo() && type_h.IsConstantHi()) ||
- (IsDoubleLo() && type_h.IsDoubleHi()) ||
- (IsLongLo() && type_h.IsLongHi()));
- }
- return false;
+ return CheckWidePair(GetKind(), type_h.GetKind());
}
+
// The high half that corresponds to this low half
const RegType& HighHalf(RegTypeCache* cache) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -160,7 +176,10 @@ class RegType {
bool IsCategory2Types() const {
return IsLowHalf(); // Don't expect explicit testing of high halves
}
- constexpr bool IsBooleanTypes() const { return IsBoolean() || IsZero() || IsBooleanConstant(); }
+ static constexpr bool IsBooleanTypes(Kind kind) {
+ return kind == Kind::kBoolean || kind == Kind::kZero || kind == Kind::kBooleanConstant;
+ }
+ constexpr bool IsBooleanTypes() const { return IsBooleanTypes(GetKind()); }
constexpr bool IsByteTypes() const {
return IsByte() || IsPositiveByteConstant() || IsByteConstant() || IsBooleanTypes();
}
@@ -211,6 +230,16 @@ class RegType {
std::string Dump() const REQUIRES_SHARED(Locks::mutator_lock_);
+ enum class Assignability : uint8_t {
+ kAssignable,
+ kNotAssignable,
+ kNarrowingConversion,
+ kReference,
+ kInvalid,
+ };
+
+ ALWAYS_INLINE static inline Assignability AssignabilityFrom(Kind lhs, Kind rhs);
+
// Can this type be assigned by src?
// Note: Object and interface types may always be assigned to one another, see
// comment on
@@ -253,14 +282,6 @@ class RegType {
static void* operator new(size_t size, ArenaAllocator* allocator);
static void* operator new(size_t size, ScopedArenaAllocator* allocator) = delete;
- enum class Assignability : uint8_t {
- kAssignable,
- kNotAssignable,
- kNarrowingConversion,
- kReference,
- kInvalid,
- };
-
protected:
constexpr RegType(const std::string_view& descriptor, uint16_t cache_id, Kind kind)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -289,6 +310,7 @@ class RegType {
};
std::ostream& operator<<(std::ostream& os, RegType::Kind kind);
+std::ostream& operator<<(std::ostream& os, RegType::Assignability assignability);
// Bottom type.
class ConflictType final : public RegType {
diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h
index fd80aac302..10114c2f0f 100644
--- a/runtime/verifier/reg_type_cache-inl.h
+++ b/runtime/verifier/reg_type_cache-inl.h
@@ -68,11 +68,19 @@ struct RegKindToCacheId : RegTypeCache {
} // namespace detail
-// Note: To avoid including `reg_types.h` from `reg_type_cache.h`, we define this as
-// a free function because we cannot forward-declare the nested enum `RegType::Kind`.
-inline const art::verifier::RegType& RegTypeFromKind(const RegTypeCache* reg_types,
- RegType::Kind kind) {
- return reg_types->GetFromId(detail::RegKindToCacheId::Translate(kind));
+constexpr uint16_t RegTypeCache::IdForRegKind(RegType::Kind kind) {
+ return detail::RegKindToCacheId::Translate(kind);
+}
+
+constexpr RegType::Kind RegTypeCache::RegKindForId(uint16_t id) {
+ DCHECK_LT(id, NumberOfRegKindCacheIds());
+ RegType::Kind kind = enum_cast<RegType::Kind>(id);
+ DCHECK_EQ(id, IdForRegKind(kind));
+ return kind;
+}
+
+inline const RegType& RegTypeCache::GetFromRegKind(RegType::Kind kind) const {
+ return GetFromId(IdForRegKind(kind));
}
inline const RegType& RegTypeCache::FromTypeIndex(dex::TypeIndex type_index) {
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index c3df79dbdb..a78ad587f2 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -27,6 +27,7 @@
#include "dex/primitive.h"
#include "gc_root.h"
#include "handle_scope.h"
+#include "reg_type.h"
namespace art HIDDEN {
@@ -40,34 +41,7 @@ class DexFile;
namespace verifier {
-class BooleanConstantType;
-class BooleanType;
-class ByteConstantType;
-class ByteType;
-class CharConstantType;
-class CharType;
-class ConflictType;
-class ConstantHiType;
-class ConstantLoType;
-class DoubleHiType;
-class DoubleLoType;
-class FloatType;
-class IntegerConstantType;
-class IntegerType;
-class JavaLangObjectType;
-class LongHiType;
-class LongLoType;
class MethodVerifier;
-class NullType;
-class PositiveByteConstantType;
-class PositiveShortConstantType;
-class ReferenceType;
-class RegType;
-class ShortConstantType;
-class ShortType;
-class UndefinedType;
-class UninitializedType;
-class ZeroType;
// Use 8 bytes since that is the default arena allocator alignment.
static constexpr size_t kDefaultArenaBitVectorBytes = 8;
@@ -98,7 +72,21 @@ class RegTypeCache {
return can_suspend_;
}
- const art::verifier::RegType& GetFromId(uint16_t id) const;
+ static constexpr uint32_t NumberOfRegKindCacheIds() { return kNumberOfRegKindCacheIds; }
+
+ // Translate `RegType::Kind` to id for a pre-initialized register type.
+ // Cannot be used for non-zero reference kinds other than `JavaLangObject()`; all other
+ // kinds (undefined, conflict, primitive and constant kinds) have pre-initialized types.
+ static constexpr uint16_t IdForRegKind(RegType::Kind kind);
+
+ // Translate `id` to `RegType::Kind`.
+ // The `id` must be lower than `NumberOfRegKindCacheIds()`.
+ static constexpr RegType::Kind RegKindForId(uint16_t id);
+
+ // Get register type for a `RegType::Kind` with the same restrictions as `IdForRegKind()`.
+ const RegType& GetFromRegKind(RegType::Kind kind) const;
+
+ const RegType& GetFromId(uint16_t id) const;
// Get or insert a reg type for a klass.
const RegType& FromClass(ObjPtr<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index ad4999b205..a775ac5561 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -30,44 +30,73 @@ namespace verifier {
// developers that their code will be slow.
static constexpr bool kDumpLockFailures = true;
-inline const RegType& RegisterLine::GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const {
+inline uint16_t RegisterLine::GetRegisterTypeId(uint32_t vsrc) const {
// The register index was validated during the static pass, so we don't need to check it here.
DCHECK_LT(vsrc, num_regs_);
- return verifier->GetRegTypeCache()->GetFromId(line_[vsrc]);
+ return line_[vsrc];
+}
+
+inline const RegType& RegisterLine::GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const {
+ return verifier->GetRegTypeCache()->GetFromId(GetRegisterTypeId(vsrc));
}
template <LockOp kLockOp>
-inline void RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) {
+inline void RegisterLine::SetRegisterTypeImpl(uint32_t vdst, uint16_t new_id) {
DCHECK_LT(vdst, num_regs_);
- DCHECK(!new_type.IsLowHalf());
- DCHECK(!new_type.IsHighHalf());
// Note: previously we failed when asked to set a conflict. However, conflicts are OK as long
// as they are not accessed, and our backends can handle this nowadays.
- line_[vdst] = new_type.GetId();
+ line_[vdst] = new_id;
switch (kLockOp) {
case LockOp::kClear:
// Clear the monitor entry bits for this register.
ClearAllRegToLockDepths(vdst);
break;
case LockOp::kKeep:
- // Should only be doing this with reference types.
- DCHECK(new_type.IsReferenceTypes());
break;
}
}
-inline void RegisterLine::SetRegisterTypeWide(uint32_t vdst,
- const RegType& new_type1,
- const RegType& new_type2) {
+inline void RegisterLine::SetRegisterType(uint32_t vdst, RegType::Kind new_kind) {
+ DCHECK(!RegType::IsLowHalf(new_kind));
+ DCHECK(!RegType::IsHighHalf(new_kind));
+ SetRegisterTypeImpl<LockOp::kClear>(vdst, RegTypeCache::IdForRegKind(new_kind));
+}
+
+template <LockOp kLockOp>
+inline void RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) {
+ DCHECK(!new_type.IsLowHalf());
+ DCHECK(!new_type.IsHighHalf());
+ // Should only keep locks for reference types.
+ DCHECK_IMPLIES(kLockOp == LockOp::kKeep, new_type.IsReferenceTypes());
+ SetRegisterTypeImpl<kLockOp>(vdst, new_type.GetId());
+}
+
+inline void RegisterLine::SetRegisterTypeWideImpl(uint32_t vdst,
+ uint16_t new_id1,
+ uint16_t new_id2) {
DCHECK_LT(vdst + 1, num_regs_);
- DCHECK(new_type1.CheckWidePair(new_type2));
- line_[vdst] = new_type1.GetId();
- line_[vdst + 1] = new_type2.GetId();
+ line_[vdst] = new_id1;
+ line_[vdst + 1] = new_id2;
// Clear the monitor entry bits for this register.
ClearAllRegToLockDepths(vdst);
ClearAllRegToLockDepths(vdst + 1);
}
+inline void RegisterLine::SetRegisterTypeWide(uint32_t vdst,
+ RegType::Kind new_kind1,
+ RegType::Kind new_kind2) {
+ DCHECK(RegType::CheckWidePair(new_kind1, new_kind2));
+ SetRegisterTypeWideImpl(
+ vdst, RegTypeCache::IdForRegKind(new_kind1), RegTypeCache::IdForRegKind(new_kind2));
+}
+
+inline void RegisterLine::SetRegisterTypeWide(uint32_t vdst,
+ const RegType& new_type1,
+ const RegType& new_type2) {
+ DCHECK(new_type1.CheckWidePair(new_type2));
+ SetRegisterTypeWideImpl(vdst, new_type1.GetId(), new_type2.GetId());
+}
+
inline void RegisterLine::SetResultTypeToUnknown(RegTypeCache* reg_types) {
result_[0] = reg_types->Undefined().GetId();
result_[1] = result_[0];
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index f51b107e28..19c2e2084f 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -27,6 +27,7 @@
#include "base/locks.h"
#include "base/macros.h"
#include "base/safe_map.h"
+#include "reg_type.h"
namespace art HIDDEN {
@@ -110,13 +111,15 @@ class RegisterLine {
// is typical when the underlying value did not change, but we have "different" type information
// available now. An example is sharpening types after a check-cast. Note that when given kKeep,
// the new_type is dchecked to be a reference type.
+ ALWAYS_INLINE void SetRegisterType(uint32_t vdst, RegType::Kind new_kind)
+ REQUIRES_SHARED(Locks::mutator_lock_);
template <LockOp kLockOp>
ALWAYS_INLINE void SetRegisterType(uint32_t vdst, const RegType& new_type)
REQUIRES_SHARED(Locks::mutator_lock_);
- void SetRegisterTypeWide(uint32_t vdst,
- const RegType& new_type1,
- const RegType& new_type2)
+ void SetRegisterTypeWide(uint32_t vdst, RegType::Kind new_kind1, RegType::Kind new_kind2)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetRegisterTypeWide(uint32_t vdst, const RegType& new_type1, const RegType& new_type2)
REQUIRES_SHARED(Locks::mutator_lock_);
/* Set the type of the "result" register. */
@@ -135,6 +138,9 @@ class RegisterLine {
void SetRegisterTypeForNewInstance(uint32_t vdst, const RegType& uninit_type, uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Get the id of the register tyoe of register vsrc.
+ uint16_t GetRegisterTypeId(uint32_t vsrc) const;
+
// Get the type of register vsrc.
const RegType& GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const;
@@ -257,6 +263,12 @@ class RegisterLine {
void EnsureAllocationDexPcsAvailable();
+ template <LockOp kLockOp>
+ ALWAYS_INLINE void SetRegisterTypeImpl(uint32_t vdst, uint16_t new_id)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetRegisterTypeWideImpl(uint32_t vdst, uint16_t new_id1, uint16_t new_id2)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
void CopyRegToLockDepth(size_t dst, size_t src) {
auto it = reg_to_lock_depths_.find(src);
if (it != reg_to_lock_depths_.end()) {