diff options
-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()) { |