diff options
| -rw-r--r-- | compiler/optimizing/builder.cc | 49 | ||||
| -rw-r--r-- | compiler/optimizing/builder.h | 3 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator.cc | 20 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator.h | 5 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 30 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 28 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_mips64.cc | 18 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 30 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 30 | ||||
| -rw-r--r-- | compiler/optimizing/graph_visualizer.cc | 5 | ||||
| -rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 6 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 15 | ||||
| -rw-r--r-- | compiler/optimizing/reference_type_propagation.cc | 36 | ||||
| -rw-r--r-- | test/529-checker-unresolved/expected.txt | 2 | ||||
| -rw-r--r-- | test/529-checker-unresolved/src/Main.java | 16 | ||||
| -rw-r--r-- | test/529-checker-unresolved/src/Unresolved.java | 10 |
16 files changed, 200 insertions, 103 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index ebbfb14190..5acc5fda71 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -940,7 +940,8 @@ HClinitCheck* HGraphBuilder::ProcessClinitCheckForInvoke( storage_index, *dex_compilation_unit_->GetDexFile(), is_outer_class, - dex_pc); + dex_pc, + /*needs_access_check*/ false); current_block_->AddInstruction(load_class); clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); current_block_->AddInstruction(clinit_check); @@ -1384,7 +1385,8 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, storage_index, *dex_compilation_unit_->GetDexFile(), is_outer_class, - dex_pc); + dex_pc, + /*needs_access_check*/ false); current_block_->AddInstruction(constant); HInstruction* cls = constant; @@ -1615,7 +1617,9 @@ void HGraphBuilder::BuildFillWideArrayData(HInstruction* object, static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) SHARED_REQUIRES(Locks::mutator_lock_) { - if (cls->IsInterface()) { + if (cls.Get() == nullptr) { + return TypeCheckKind::kUnresolvedCheck; + } else if (cls->IsInterface()) { return TypeCheckKind::kInterfaceCheck; } else if (cls->IsArrayClass()) { if (cls->GetComponentType()->IsObjectClass()) { @@ -1634,11 +1638,20 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) } } -bool HGraphBuilder::BuildTypeCheck(const Instruction& instruction, +void HGraphBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, uint16_t type_index, uint32_t dex_pc) { + bool type_known_final, type_known_abstract, use_declaring_class; + bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( + dex_compilation_unit_->GetDexMethodIndex(), + *dex_compilation_unit_->GetDexFile(), + type_index, + &type_known_final, + &type_known_abstract, + &use_declaring_class); + ScopedObjectAccess soa(Thread::Current()); StackHandleScope<2> hs(soa.Self()); Handle<mirror::DexCache> dex_cache(hs.NewHandle( @@ -1646,22 +1659,14 @@ bool HGraphBuilder::BuildTypeCheck(const Instruction& instruction, soa.Self(), *dex_compilation_unit_->GetDexFile()))); Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); - if ((resolved_class.Get() == nullptr) || - // TODO: Remove this check once the compiler actually knows which - // ArtMethod it is compiling. - (GetCompilingClass() == nullptr) || - !GetCompilingClass()->CanAccess(resolved_class.Get())) { - MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType); - return false; - } - HInstruction* object = LoadLocal(reference, Primitive::kPrimNot, dex_pc); HLoadClass* cls = new (arena_) HLoadClass( graph_->GetCurrentMethod(), type_index, *dex_compilation_unit_->GetDexFile(), IsOutermostCompilingClass(type_index), - dex_pc); + dex_pc, + !can_access); current_block_->AddInstruction(cls); // The class needs a temporary before being used by the type check. @@ -1676,7 +1681,6 @@ bool HGraphBuilder::BuildTypeCheck(const Instruction& instruction, DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST); current_block_->AddInstruction(new (arena_) HCheckCast(object, cls, check_kind, dex_pc)); } - return true; } bool HGraphBuilder::NeedsAccessCheck(uint32_t type_index) const { @@ -2791,16 +2795,13 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, &type_known_final, &type_known_abstract, &dont_use_is_referrers_class); - if (!can_access) { - MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType); - return false; - } current_block_->AddInstruction(new (arena_) HLoadClass( graph_->GetCurrentMethod(), type_index, *dex_compilation_unit_->GetDexFile(), IsOutermostCompilingClass(type_index), - dex_pc)); + dex_pc, + !can_access)); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc); break; } @@ -2827,18 +2828,14 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 uint8_t destination = instruction.VRegA_22c(); uint8_t reference = instruction.VRegB_22c(); uint16_t type_index = instruction.VRegC_22c(); - if (!BuildTypeCheck(instruction, destination, reference, type_index, dex_pc)) { - return false; - } + BuildTypeCheck(instruction, destination, reference, type_index, dex_pc); break; } case Instruction::CHECK_CAST: { uint8_t reference = instruction.VRegA_21c(); uint16_t type_index = instruction.VRegB_21c(); - if (!BuildTypeCheck(instruction, -1, reference, type_index, dex_pc)) { - return false; - } + BuildTypeCheck(instruction, -1, reference, type_index, dex_pc); break; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index b2dc24169e..6910d5195c 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -236,8 +236,7 @@ class HGraphBuilder : public ValueObject { uint32_t dex_pc); // Builds a `HInstanceOf`, or a `HCheckCast` instruction. - // Returns whether we succeeded in building the instruction. - bool BuildTypeCheck(const Instruction& instruction, + void BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, uint16_t type_index, diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 8254277f96..00f316cf98 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -537,6 +537,26 @@ void CodeGenerator::GenerateUnresolvedFieldAccess( } } +void CodeGenerator::CreateLoadClassLocationSummary(HLoadClass* cls, + Location runtime_type_index_location, + Location runtime_return_location) { + ArenaAllocator* allocator = cls->GetBlock()->GetGraph()->GetArena(); + LocationSummary::CallKind call_kind = cls->NeedsAccessCheck() + ? LocationSummary::kCall + : (cls->CanCallRuntime() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall); + LocationSummary* locations = new (allocator) LocationSummary(cls, call_kind); + locations->SetInAt(0, Location::RequiresRegister()); + if (cls->NeedsAccessCheck()) { + locations->AddTemp(runtime_type_index_location); + locations->SetOut(runtime_return_location); + } else { + locations->SetOut(Location::RequiresRegister()); + } +} + + void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const { // The DCHECKS below check that a register is not specified twice in // the summary. The out location can overlap with an input, so we need diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index a3ebc43f11..0a3698946e 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -416,6 +416,11 @@ class CodeGenerator { uint32_t dex_pc, const FieldAccessCallingConvention& calling_convention); + // TODO: This overlaps a bit with MoveFromReturnRegister. Refactor for a better design. + static void CreateLoadClassLocationSummary(HLoadClass* cls, + Location runtime_type_index_location, + Location runtime_return_location); + void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; } DisassemblyInformation* GetDisassemblyInformation() const { return disasm_info_; } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 299350b879..54af41d420 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -4468,20 +4468,24 @@ void ParallelMoveResolverARM::RestoreScratch(int reg) { } void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { - LocationSummary::CallKind call_kind = cls->CanCallRuntime() - ? LocationSummary::kCallOnSlowPath - : LocationSummary::kNoCall; - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Location::RegisterLocation(R0)); } void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); Register out = locations->Out().AsRegister<Register>(); Register current_method = locations->InAt(0).AsRegister<Register>(); - if (cls->IsReferrersClass()) { + if (cls->NeedsAccessCheck()) { + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), + cls, + cls->GetDexPc(), + nullptr); + } else if (cls->IsReferrersClass()) { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); __ LoadFromOffset( @@ -4604,6 +4608,7 @@ void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kArrayObjectCheck: call_kind = LocationSummary::kNoCall; break; + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCall; break; @@ -4644,10 +4649,11 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { __ CompareAndBranchIfZero(obj, &zero); } - // In case of an interface check, we put the object class into the object register. + // In case of an interface/unresolved check, we put the object class into the object register. // This is safe, as the register is caller-save, and the object must be in another // register if it survives the runtime call. - Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) + Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) || + (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck) ? obj : out; __ LoadFromOffset(kLoadWord, target, obj, class_offset); @@ -4728,7 +4734,7 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { } break; } - + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: default: { codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), @@ -4769,6 +4775,7 @@ void LocationsBuilderARM::VisitCheckCast(HCheckCast* instruction) { ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; break; + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCall; break; @@ -4873,6 +4880,7 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel()); break; } + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: default: codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index c7ade65cd8..07758e9df7 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2388,6 +2388,7 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kArrayObjectCheck: call_kind = LocationSummary::kNoCall; break; + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCall; break; @@ -2429,10 +2430,11 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { __ Cbz(obj, &zero); } - // In case of an interface check, we put the object class into the object register. + // In case of an interface/unresolved check, we put the object class into the object register. // This is safe, as the register is caller-save, and the object must be in another // register if it survives the runtime call. - Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) + Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) || + (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck) ? obj : out; __ Ldr(target, HeapOperand(obj.W(), class_offset)); @@ -2513,7 +2515,7 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } break; } - + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: default: { codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), @@ -2554,6 +2556,7 @@ void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; break; + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCall; break; @@ -2659,6 +2662,7 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { __ Cbnz(temp, slow_path->GetEntryLabel()); break; } + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: default: codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), @@ -3014,17 +3018,23 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { } void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { - LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath - : LocationSummary::kNoCall; - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + LocationFrom(calling_convention.GetRegisterAt(0)), + LocationFrom(vixl::x0)); } void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { Register out = OutputRegister(cls); Register current_method = InputRegisterAt(cls, 0); - if (cls->IsReferrersClass()) { + if (cls->NeedsAccessCheck()) { + codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex()); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), + cls, + cls->GetDexPc(), + nullptr); + } else if (cls->IsReferrersClass()) { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); __ Ldr(out, MemOperand(current_method, ArtMethod::DeclaringClassOffset().Int32Value())); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index e95d283c1a..00bb5053f2 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -2590,18 +2590,24 @@ void InstructionCodeGeneratorMIPS64::VisitInvokeVirtual(HInvokeVirtual* invoke) } void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) { - LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath - : LocationSummary::kNoCall; - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Location::RegisterLocation(A0)); } void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>(); - if (cls->IsReferrersClass()) { + if (cls->NeedsAccessCheck()) { + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), + cls, + cls->GetDexPc(), + nullptr); + } else if (cls->IsReferrersClass()) { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); __ LoadFromOffset(kLoadUnsignedWord, out, current_method, diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 5078456eb1..b89ca11ad0 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -4989,20 +4989,24 @@ void ParallelMoveResolverX86::RestoreScratch(int reg) { } void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { - LocationSummary::CallKind call_kind = cls->CanCallRuntime() - ? LocationSummary::kCallOnSlowPath - : LocationSummary::kNoCall; - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Location::RegisterLocation(EAX)); } void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); Register out = locations->Out().AsRegister<Register>(); Register current_method = locations->InAt(0).AsRegister<Register>(); - if (cls->IsReferrersClass()) { + if (cls->NeedsAccessCheck()) { + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), + cls, + cls->GetDexPc(), + nullptr); + } else if (cls->IsReferrersClass()) { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); __ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value())); @@ -5121,6 +5125,7 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kArrayObjectCheck: call_kind = LocationSummary::kNoCall; break; + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCall; break; @@ -5161,10 +5166,11 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { __ j(kEqual, &zero); } - // In case of an interface check, we put the object class into the object register. + // In case of an interface/unresolved check, we put the object class into the object register. // This is safe, as the register is caller-save, and the object must be in another // register if it survives the runtime call. - Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) + Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) || + (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck) ? obj : out; __ movl(target, Address(obj, class_offset)); @@ -5273,7 +5279,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } break; } - + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: default: { codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), @@ -5315,6 +5321,7 @@ void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) { : LocationSummary::kNoCall; break; case TypeCheckKind::kInterfaceCheck: + case TypeCheckKind::kUnresolvedCheck: call_kind = LocationSummary::kCall; break; case TypeCheckKind::kArrayCheck: @@ -5441,6 +5448,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { __ j(kNotEqual, slow_path->GetEntryLabel()); break; } + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: default: codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 791bb9e6aa..ad6588c359 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -4694,20 +4694,24 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( } void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { - LocationSummary::CallKind call_kind = cls->CanCallRuntime() - ? LocationSummary::kCallOnSlowPath - : LocationSummary::kNoCall; - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Location::RegisterLocation(RAX)); } void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>(); - if (cls->IsReferrersClass()) { + if (cls->NeedsAccessCheck()) { + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), + cls, + cls->GetDexPc(), + nullptr); + } else if (cls->IsReferrersClass()) { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); __ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value())); @@ -4817,6 +4821,7 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kArrayObjectCheck: call_kind = LocationSummary::kNoCall; break; + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCall; break; @@ -4857,10 +4862,11 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { __ j(kEqual, &zero); } - // In case of an interface check, we put the object class into the object register. + // In case of an interface/unresolved check, we put the object class into the object register. // This is safe, as the register is caller-save, and the object must be in another // register if it survives the runtime call. - CpuRegister target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) + CpuRegister target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) || + (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck) ? obj : out; __ movl(target, Address(obj, class_offset)); @@ -4974,7 +4980,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } break; } - + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: default: { codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), @@ -5015,6 +5021,7 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; break; + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCall; break; @@ -5142,6 +5149,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { __ j(kNotEqual, slow_path->GetEntryLabel()); break; } + case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: default: codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 7a83662696..d38f4c862f 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -501,8 +501,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("can_be_null") << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha; + } else if (instruction->IsLoadClass()) { + StartAttributeStream("klass") << "unresolved"; } else { - DCHECK(!is_after_pass_) << "Type info should be valid after reference type propagation"; + DCHECK(!is_after_pass_) + << "Expected a valid rti after reference type propagation"; } } if (disasm_info_ != nullptr) { diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 3287a0a119..86a3ad98b4 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -216,7 +216,11 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo } ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI(); - DCHECK(class_rti.IsValid() && class_rti.IsExact()); + if (!class_rti.IsValid()) { + // Happens when the loaded class is unresolved. + return false; + } + DCHECK(class_rti.IsExact()); if (class_rti.IsSupertypeOf(obj_rti)) { *outcome = true; return true; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 849f876f36..489f71de74 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -4513,12 +4513,14 @@ class HLoadClass : public HExpression<1> { uint16_t type_index, const DexFile& dex_file, bool is_referrers_class, - uint32_t dex_pc) + uint32_t dex_pc, + bool needs_access_check) : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), type_index_(type_index), dex_file_(dex_file), is_referrers_class_(is_referrers_class), generate_clinit_check_(false), + needs_access_check_(needs_access_check), loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { SetRawInputAt(0, current_method); } @@ -4538,19 +4540,22 @@ class HLoadClass : public HExpression<1> { bool NeedsEnvironment() const OVERRIDE { // Will call runtime and load the class if the class is not loaded yet. // TODO: finer grain decision. - return !is_referrers_class_; + return !is_referrers_class_ || needs_access_check_; } bool MustGenerateClinitCheck() const { return generate_clinit_check_; } - void SetMustGenerateClinitCheck(bool generate_clinit_check) { generate_clinit_check_ = generate_clinit_check; } bool CanCallRuntime() const { - return MustGenerateClinitCheck() || !is_referrers_class_; + return MustGenerateClinitCheck() || !is_referrers_class_ || needs_access_check_; + } + + bool NeedsAccessCheck() const { + return needs_access_check_; } bool CanThrow() const OVERRIDE { @@ -4586,6 +4591,7 @@ class HLoadClass : public HExpression<1> { // Whether this instruction must generate the initialization check. // Used for code generation. bool generate_clinit_check_; + bool needs_access_check_; ReferenceTypeInfo loaded_class_rti_; @@ -4897,6 +4903,7 @@ class HThrow : public HTemplateInstruction<1> { * or `HCheckCast`. */ enum class TypeCheckKind { + kUnresolvedCheck, // Check against an unresolved type. kExactCheck, // Can do a single class compare. kClassHierarchyCheck, // Can just walk the super class chain. kAbstractClassCheck, // Can just walk the super class chain, starting one up. diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index d22f2540ad..f7a7e420bb 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -121,8 +121,9 @@ void ReferenceTypePropagation::Run() { if (instr->IsBoundType()) { DCHECK(instr->AsBoundType()->GetUpperBound().IsValid()); } else if (instr->IsLoadClass()) { - DCHECK(instr->AsLoadClass()->GetReferenceTypeInfo().IsExact()); - DCHECK(instr->AsLoadClass()->GetLoadedClassRTI().IsValid()); + HLoadClass* cls = instr->AsLoadClass(); + DCHECK(cls->GetReferenceTypeInfo().IsExact()); + DCHECK(!cls->GetLoadedClassRTI().IsValid() || cls->GetLoadedClassRTI().IsExact()); } else if (instr->IsNullCheck()) { DCHECK(instr->GetReferenceTypeInfo().IsEqual(instr->InputAt(0)->GetReferenceTypeInfo())) << "NullCheck " << instr->GetReferenceTypeInfo() @@ -168,6 +169,7 @@ static HBoundType* CreateBoundType(ArenaAllocator* arena, SHARED_REQUIRES(Locks::mutator_lock_) { ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo(); ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); + DCHECK(class_rti.IsValid()); HBoundType* bound_type = new (arena) HBoundType(obj, class_rti, upper_can_be_null); // Narrow the type as much as possible. if (class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) { @@ -316,6 +318,15 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) { return; } + HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass(); + ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); + { + ScopedObjectAccess soa(Thread::Current()); + if (!class_rti.IsValid()) { + // He have loaded an unresolved class. Don't bother bounding the type. + return; + } + } // We only need to bound the type if we have uses in the relevant block. // So start with null and create the HBoundType lazily, only if it's needed. HBoundType* bound_type = nullptr; @@ -336,8 +347,6 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) { if (instanceOfTrueBlock->Dominates(user->GetBlock())) { if (bound_type == nullptr) { ScopedObjectAccess soa(Thread::Current()); - HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass(); - ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction(); if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) { bound_type = CreateBoundType( @@ -475,10 +484,10 @@ void RTPVisitor::VisitLoadClass(HLoadClass* instr) { // Get type from dex cache assuming it was populated by the verifier. mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex()); // TODO: investigating why we are still getting unresolved classes: b/22821472. - ReferenceTypeInfo::TypeHandle handle = (resolved_class != nullptr) - ? handles_->NewHandle(resolved_class) - : object_class_handle_; - instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(handle, /* is_exact */ true)); + if (resolved_class != nullptr) { + instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( + handles_->NewHandle(resolved_class), /* is_exact */ true)); + } instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_class_handle_, /* is_exact */ true)); } @@ -517,6 +526,15 @@ void RTPVisitor::VisitFakeString(HFakeString* instr) { } void RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { + HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); + ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); + { + ScopedObjectAccess soa(Thread::Current()); + if (!class_rti.IsValid()) { + // He have loaded an unresolved class. Don't bother bounding the type. + return; + } + } HInstruction* obj = check_cast->InputAt(0); HBoundType* bound_type = nullptr; for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) { @@ -524,8 +542,6 @@ void RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { if (check_cast->StrictlyDominates(user)) { if (bound_type == nullptr) { ScopedObjectAccess soa(Thread::Current()); - HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); - ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); if (ShouldCreateBoundType(check_cast->GetNext(), obj, class_rti, check_cast, nullptr)) { bound_type = CreateBoundType( GetGraph()->GetArena(), diff --git a/test/529-checker-unresolved/expected.txt b/test/529-checker-unresolved/expected.txt index 358048c75b..1e7dbfed2e 100644 --- a/test/529-checker-unresolved/expected.txt +++ b/test/529-checker-unresolved/expected.txt @@ -3,3 +3,5 @@ UnresolvedClass.staticMethod() UnresolvedClass.virtualMethod() UnresolvedClass.interfaceMethod() UnresolvedClass.superMethod() +instanceof ok +checkcast ok diff --git a/test/529-checker-unresolved/src/Main.java b/test/529-checker-unresolved/src/Main.java index adb5adae82..5219c04c37 100644 --- a/test/529-checker-unresolved/src/Main.java +++ b/test/529-checker-unresolved/src/Main.java @@ -114,16 +114,30 @@ public class Main extends UnresolvedSuperClass { expectEquals(o, c.instanceObject); } + static public void testInstanceOf(Object o) { + if (o instanceof UnresolvedSuperClass) { + System.out.println("instanceof ok"); + } + } + + static public UnresolvedSuperClass testCheckCast(Object o) { + UnresolvedSuperClass c = (UnresolvedSuperClass) o; + System.out.println("checkcast ok"); + return c; + } /// CHECK-START: void Main.main(java.lang.String[]) register (before) /// CHECK: InvokeUnresolved invoke_type:direct static public void main(String[] args) { UnresolvedClass c = new UnresolvedClass(); + Main m = new Main(); callInvokeUnresolvedStatic(); callInvokeUnresolvedVirtual(c); callInvokeUnresolvedInterface(c); - callInvokeUnresolvedSuper(new Main()); + callInvokeUnresolvedSuper(m); callUnresolvedStaticFieldAccess(); callUnresolvedInstanceFieldAccess(c); + testInstanceOf(m); + testCheckCast(m); } public static void expectEquals(byte expected, byte result) { diff --git a/test/529-checker-unresolved/src/Unresolved.java b/test/529-checker-unresolved/src/Unresolved.java index 03ceb6857b..20ac6e0b89 100644 --- a/test/529-checker-unresolved/src/Unresolved.java +++ b/test/529-checker-unresolved/src/Unresolved.java @@ -58,13 +58,3 @@ class UnresolvedClass extends UnresolvedSuperClass implements UnresolvedInterfac public Object instanceObject; } -final class UnresolvedFinalClass { - public void directMethod() { - System.out.println("UnresolvedFinalClass.directMethod()"); - } -} - -class UnresolvedAtRuntime { - public void unresolvedAtRuntime() { } -} - |