diff options
author | 2024-11-20 14:55:49 +0000 | |
---|---|---|
committer | 2024-11-25 07:36:44 +0000 | |
commit | 98ffb6ed4cfe81af20815fe52aaf6f75b5150432 (patch) | |
tree | 593534fd0d609c76907b2f7e97dd6d55fc9267b6 | |
parent | 581aa379c235065caf81845fcfd0146af8a54871 (diff) |
verifier: Faster primitive assignability checks.
Rewrite primitive assignability checks to use the cache id
directly without going through the corresponding `RegType`.
Use the pre-calculated assignability table and protect
against unreachable `Assignability` values with `DCHECK()`,
avoiding unnecessary runtime checks in release builds.
Move failure output to `NO_INLINE` functions to avoid code
bloat from inlining.
Note that this change leaves field and array access and
invoke argument checks untouched because that code needs
to deal with references and narrowing conversions as well.
The verification of `return` is also untouched because the
special-casing of narrowing conversions is a mess - this
should be cleaned up with a standalone change.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 181943478
Change-Id: I8dc24d2d1c09762f38e58ce065ca914490967642
-rw-r--r-- | runtime/verifier/method_verifier.cc | 419 | ||||
-rw-r--r-- | runtime/verifier/reg_type-inl.h | 28 | ||||
-rw-r--r-- | runtime/verifier/reg_type.cc | 26 | ||||
-rw-r--r-- | runtime/verifier/reg_type.h | 56 | ||||
-rw-r--r-- | runtime/verifier/reg_type_cache-inl.h | 18 | ||||
-rw-r--r-- | runtime/verifier/reg_type_cache.h | 44 | ||||
-rw-r--r-- | runtime/verifier/register_line-inl.h | 57 | ||||
-rw-r--r-- | runtime/verifier/register_line.h | 18 |
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()) { |