diff options
author | 2015-12-02 09:06:11 +0000 | |
---|---|---|
committer | 2015-12-02 13:01:25 +0000 | |
commit | e523423a053af5cb55837f07ceae9ff2fd581712 (patch) | |
tree | 6c2d9c570bf0d9a0e2cd056e052c0be618b03fc5 | |
parent | 08a84acc7adb1bb076595eb961bd4667896e5075 (diff) |
Revert "Revert "Don't use the compiler driver for method resolution.""
This reverts commit c88ef3a10c474045a3476a02ae75d07ddd3230b7.
Change-Id: I0ed88a48b313a8d28bc39fae40631123aadb13ef
33 files changed, 521 insertions, 93 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index d7754e8ea9..8e75bdcdc9 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -735,6 +735,79 @@ static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) { } } +ArtMethod* HGraphBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(soa.Self()); + + ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); + Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass())); + + ArtMethod* resolved_method = class_linker->ResolveMethod( + *dex_compilation_unit_->GetDexFile(), + method_idx, + dex_compilation_unit_->GetDexCache(), + class_loader, + /* referrer */ nullptr, + invoke_type); + + if (UNLIKELY(resolved_method == nullptr)) { + // Clean up any exception left by type resolution. + soa.Self()->ClearException(); + return nullptr; + } + + // Check access. The class linker has a fast path for looking into the dex cache + // and does not check the access if it hits it. + if (compiling_class.Get() == nullptr) { + if (!resolved_method->IsPublic()) { + return nullptr; + } + } else if (!compiling_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(), + resolved_method, + dex_compilation_unit_->GetDexCache().Get(), + method_idx)) { + return nullptr; + } + + // We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not. + // We need to look at the referrer's super class vtable. + if (invoke_type == kSuper) { + if (compiling_class.Get() == nullptr) { + // Invoking a super method requires knowing the actual super class. If we did not resolve + // the compiling method's declaring class (which only happens for ahead of time compilation), + // bail out. + DCHECK(Runtime::Current()->IsAotCompiler()); + return nullptr; + } + uint16_t vtable_index = resolved_method->GetMethodIndex(); + ArtMethod* actual_method = compiling_class->GetSuperClass()->GetVTableEntry( + vtable_index, class_linker->GetImagePointerSize()); + if (actual_method != resolved_method && + !IsSameDexFile(*resolved_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) { + // TODO: The actual method could still be referenced in the current dex file, so we + // could try locating it. + // TODO: Remove the dex_file restriction. + return nullptr; + } + if (!actual_method->IsInvokable()) { + // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub + // could resolve the callee to the wrong method. + return nullptr; + } + resolved_method = actual_method; + } + + // Check for incompatible class changes. The class linker has a fast path for + // looking into the dex cache and does not check incompatible class changes if it hits it. + if (resolved_method->CheckIncompatibleClassChange(invoke_type)) { + return nullptr; + } + + return resolved_method; +} + bool HGraphBuilder::BuildInvoke(const Instruction& instruction, uint32_t dex_pc, uint32_t method_idx, @@ -742,22 +815,18 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, bool is_range, uint32_t* args, uint32_t register_index) { - InvokeType original_invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode()); - InvokeType optimized_invoke_type = original_invoke_type; + InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode()); const char* descriptor = dex_file_->GetMethodShorty(method_idx); Primitive::Type return_type = Primitive::GetType(descriptor[0]); // Remove the return type from the 'proto'. size_t number_of_arguments = strlen(descriptor) - 1; - if (original_invoke_type != kStatic) { // instance call + if (invoke_type != kStatic) { // instance call // One extra argument for 'this'. number_of_arguments++; } MethodReference target_method(dex_file_, method_idx); - int32_t table_index = 0; - uintptr_t direct_code = 0; - uintptr_t direct_method = 0; // Special handling for string init. int32_t string_init_offset = 0; @@ -780,7 +849,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, method_idx, target_method, dispatch_info, - original_invoke_type, + invoke_type, kStatic /* optimized_invoke_type */, HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit); return HandleStringInit(invoke, @@ -791,23 +860,16 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, descriptor); } - // Handle unresolved methods. - if (!compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_, - dex_pc, - true /* update_stats */, - true /* enable_devirtualization */, - &optimized_invoke_type, - &target_method, - &table_index, - &direct_code, - &direct_method)) { + ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type); + + if (resolved_method == nullptr) { MaybeRecordStat(MethodCompilationStat::kUnresolvedMethod); HInvoke* invoke = new (arena_) HInvokeUnresolved(arena_, number_of_arguments, return_type, dex_pc, method_idx, - original_invoke_type); + invoke_type); return HandleInvoke(invoke, number_of_vreg_arguments, args, @@ -817,21 +879,26 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, nullptr /* clinit_check */); } - // Handle resolved methods (non string init). - - DCHECK(optimized_invoke_type != kSuper); - // Potential class initialization check, in the case of a static method call. HClinitCheck* clinit_check = nullptr; HInvoke* invoke = nullptr; - if (optimized_invoke_type == kDirect || optimized_invoke_type == kStatic) { + if (invoke_type == kDirect || invoke_type == kStatic || invoke_type == kSuper) { // By default, consider that the called method implicitly requires // an initialization check of its declaring method. HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit; - if (optimized_invoke_type == kStatic) { - clinit_check = ProcessClinitCheckForInvoke(dex_pc, method_idx, &clinit_check_requirement); + ScopedObjectAccess soa(Thread::Current()); + if (invoke_type == kStatic) { + clinit_check = ProcessClinitCheckForInvoke( + dex_pc, resolved_method, method_idx, &clinit_check_requirement); + } else if (invoke_type == kSuper) { + if (IsSameDexFile(*resolved_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) { + // Update the target method to the one resolved. Note that this may be a no-op if + // we resolved to the method referenced by the instruction. + method_idx = resolved_method->GetDexMethodIndex(); + target_method = MethodReference(dex_file_, method_idx); + } } HInvokeStaticOrDirect::DispatchInfo dispatch_info = { @@ -847,24 +914,26 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, method_idx, target_method, dispatch_info, - original_invoke_type, - optimized_invoke_type, + invoke_type, + invoke_type, clinit_check_requirement); - } else if (optimized_invoke_type == kVirtual) { + } else if (invoke_type == kVirtual) { + ScopedObjectAccess soa(Thread::Current()); // Needed for the method index invoke = new (arena_) HInvokeVirtual(arena_, number_of_arguments, return_type, dex_pc, method_idx, - table_index); + resolved_method->GetMethodIndex()); } else { - DCHECK_EQ(optimized_invoke_type, kInterface); + DCHECK_EQ(invoke_type, kInterface); + ScopedObjectAccess soa(Thread::Current()); // Needed for the method index invoke = new (arena_) HInvokeInterface(arena_, number_of_arguments, return_type, dex_pc, method_idx, - table_index); + resolved_method->GetDexMethodIndex()); } return HandleInvoke(invoke, @@ -962,23 +1031,18 @@ bool HGraphBuilder::IsInitialized(Handle<mirror::Class> cls) const { HClinitCheck* HGraphBuilder::ProcessClinitCheckForInvoke( uint32_t dex_pc, + ArtMethod* resolved_method, uint32_t method_idx, HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) { - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<5> hs(soa.Self()); + const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); + Thread* self = Thread::Current(); + StackHandleScope<4> hs(self); Handle<mirror::DexCache> dex_cache(hs.NewHandle( dex_compilation_unit_->GetClassLinker()->FindDexCache( - soa.Self(), *dex_compilation_unit_->GetDexFile()))); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); - ArtMethod* resolved_method = compiler_driver_->ResolveMethod( - soa, dex_cache, class_loader, dex_compilation_unit_, method_idx, InvokeType::kStatic); - - DCHECK(resolved_method != nullptr); - - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); + self, *dex_compilation_unit_->GetDexFile()))); Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( - outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file))); + outer_compilation_unit_->GetClassLinker()->FindDexCache( + self, outer_dex_file))); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass())); diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 5ada93f684..c3979f3dd1 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -305,8 +305,10 @@ class HGraphBuilder : public ValueObject { HClinitCheck* ProcessClinitCheckForInvoke( uint32_t dex_pc, + ArtMethod* method, uint32_t method_idx, - HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement); + HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) + SHARED_REQUIRES(Locks::mutator_lock_); // Build a HNewInstance instruction. bool BuildNewInstance(uint16_t type_index, uint32_t dex_pc); @@ -315,6 +317,10 @@ class HGraphBuilder : public ValueObject { bool IsInitialized(Handle<mirror::Class> cls) const SHARED_REQUIRES(Locks::mutator_lock_); + // Try to resolve a method using the class linker. Return null if a method could + // not be resolved. + ArtMethod* ResolveMethod(uint16_t method_idx, InvokeType invoke_type); + ArenaAllocator* const arena_; // A list of the size of the dex code holding block information for diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 76bf951a47..ac6b5e823a 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -5973,12 +5973,16 @@ void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp Register temp = temp_location.AsRegister<Register>(); uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( invoke->GetVTableIndex(), kArmPointerSize).Uint32Value(); - LocationSummary* locations = invoke->GetLocations(); - Location receiver = locations->InAt(0); + + // Use the calling convention instead of the location of the receiver, as + // intrinsics may have put the receiver in a different register. In the intrinsics + // slow path, the arguments have been moved to the right place, so here we are + // guaranteed that the receiver is the first register of the calling convention. + InvokeDexCallingConvention calling_convention; + Register receiver = calling_convention.GetRegisterAt(0); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); - DCHECK(receiver.IsRegister()); // /* HeapReference<Class> */ temp = receiver->klass_ - __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); + __ LoadFromOffset(kLoadWord, temp, receiver, class_offset); MaybeRecordImplicitNullCheck(invoke); // Instead of simply (possibly) unpoisoning `temp` here, we should // emit a read barrier for the previous class reference load. diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index ac16268834..04acd9d32c 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -3618,8 +3618,12 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok } void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) { - LocationSummary* locations = invoke->GetLocations(); - Location receiver = locations->InAt(0); + // Use the calling convention instead of the location of the receiver, as + // intrinsics may have put the receiver in a different register. In the intrinsics + // slow path, the arguments have been moved to the right place, so here we are + // guaranteed that the receiver is the first register of the calling convention. + InvokeDexCallingConvention calling_convention; + Register receiver = calling_convention.GetRegisterAt(0); Register temp = XRegisterFrom(temp_in); size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( invoke->GetVTableIndex(), kArm64PointerSize).SizeValue(); @@ -3630,11 +3634,10 @@ void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location te DCHECK(receiver.IsRegister()); // /* HeapReference<Class> */ temp = receiver->klass_ - __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset)); + __ Ldr(temp.W(), HeapOperandFrom(LocationFrom(receiver), class_offset)); MaybeRecordImplicitNullCheck(invoke); // Instead of simply (possibly) unpoisoning `temp` here, we should // emit a read barrier for the previous class reference load. - // However this is not required in practice, as this is an // intermediate/temporary reference and because the current // concurrent copying collector keeps the from-space memory // intact/accessible until the end of the marking phase (the diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 934f24bfb0..bc5eb31405 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -2986,8 +2986,13 @@ void InstructionCodeGeneratorMIPS64::VisitInvokeStaticOrDirect(HInvokeStaticOrDi } void CodeGeneratorMIPS64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { - LocationSummary* locations = invoke->GetLocations(); - Location receiver = locations->InAt(0); + // Use the calling convention instead of the location of the receiver, as + // intrinsics may have put the receiver in a different register. In the intrinsics + // slow path, the arguments have been moved to the right place, so here we are + // guaranteed that the receiver is the first register of the calling convention. + InvokeDexCallingConvention calling_convention; + GpuRegister receiver = calling_convention.GetRegisterAt(0); + GpuRegister temp = temp_location.AsRegister<GpuRegister>(); size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( invoke->GetVTableIndex(), kMips64PointerSize).SizeValue(); @@ -2995,8 +3000,7 @@ void CodeGeneratorMIPS64::GenerateVirtualCall(HInvokeVirtual* invoke, Location t Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64WordSize); // temp = object->GetClass(); - DCHECK(receiver.IsRegister()); - __ LoadFromOffset(kLoadUnsignedWord, temp, receiver.AsRegister<GpuRegister>(), class_offset); + __ LoadFromOffset(kLoadUnsignedWord, temp, receiver, class_offset); MaybeRecordImplicitNullCheck(invoke); // temp = temp->GetMethodAt(method_offset); __ LoadFromOffset(kLoadDoubleword, temp, temp, method_offset); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index a0d31dad04..2fb87d3029 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1969,6 +1969,11 @@ void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirec } void LocationsBuilderX86::VisitInvokeVirtual(HInvokeVirtual* invoke) { + IntrinsicLocationsBuilderX86 intrinsic(codegen_); + if (intrinsic.TryDispatch(invoke)) { + return; + } + HandleInvoke(invoke); } @@ -4150,12 +4155,16 @@ void CodeGeneratorX86::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp Register temp = temp_in.AsRegister<Register>(); uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( invoke->GetVTableIndex(), kX86PointerSize).Uint32Value(); - LocationSummary* locations = invoke->GetLocations(); - Location receiver = locations->InAt(0); + + // Use the calling convention instead of the location of the receiver, as + // intrinsics may have put the receiver in a different register. In the intrinsics + // slow path, the arguments have been moved to the right place, so here we are + // guaranteed that the receiver is the first register of the calling convention. + InvokeDexCallingConvention calling_convention; + Register receiver = calling_convention.GetRegisterAt(0); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); - DCHECK(receiver.IsRegister()); // /* HeapReference<Class> */ temp = receiver->klass_ - __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset)); + __ movl(temp, Address(receiver, class_offset)); MaybeRecordImplicitNullCheck(invoke); // Instead of simply (possibly) unpoisoning `temp` here, we should // emit a read barrier for the previous class reference load. diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 534ee1c5ab..4618be9cc3 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -802,12 +802,17 @@ void CodeGeneratorX86_64::GenerateVirtualCall(HInvokeVirtual* invoke, Location t CpuRegister temp = temp_in.AsRegister<CpuRegister>(); size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( invoke->GetVTableIndex(), kX86_64PointerSize).SizeValue(); - LocationSummary* locations = invoke->GetLocations(); - Location receiver = locations->InAt(0); + + // Use the calling convention instead of the location of the receiver, as + // intrinsics may have put the receiver in a different register. In the intrinsics + // slow path, the arguments have been moved to the right place, so here we are + // guaranteed that the receiver is the first register of the calling convention. + InvokeDexCallingConvention calling_convention; + Register receiver = calling_convention.GetRegisterAt(0); + size_t class_offset = mirror::Object::ClassOffset().SizeValue(); - DCHECK(receiver.IsRegister()); // /* HeapReference<Class> */ temp = receiver->klass_ - __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset)); + __ movl(temp, Address(CpuRegister(receiver), class_offset)); MaybeRecordImplicitNullCheck(invoke); // Instead of simply (possibly) unpoisoning `temp` here, we should // emit a read barrier for the previous class reference load. diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 48bcd10b10..2bff21375e 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -402,6 +402,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { } } + void VisitInvokeVirtual(HInvokeVirtual* invoke) OVERRIDE { + VisitInvoke(invoke); + StartAttributeStream("intrinsic") << invoke->GetIntrinsic(); + } + void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) OVERRIDE { StartAttributeStream("field_type") << field_access->GetFieldType(); } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 0363f203b2..6d93be37a7 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -192,6 +192,10 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { // We can query the dex cache directly. The verifier has populated it already. ArtMethod* resolved_method; if (invoke_instruction->IsInvokeStaticOrDirect()) { + if (invoke_instruction->AsInvokeStaticOrDirect()->IsStringInit()) { + VLOG(compiler) << "Not inlining a String.<init> method"; + return false; + } MethodReference ref = invoke_instruction->AsInvokeStaticOrDirect()->GetTargetMethod(); mirror::DexCache* const dex_cache = (&caller_dex_file == ref.dex_file) ? caller_compilation_unit_.GetDexCache().Get() diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index b01324ec3b..834081188b 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -384,7 +384,7 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke, const DexFile // InvokeStaticOrDirect. InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic); InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ? - invoke->AsInvokeStaticOrDirect()->GetInvokeType() : + invoke->AsInvokeStaticOrDirect()->GetOptimizedInvokeType() : invoke->IsInvokeVirtual() ? kVirtual : kSuper; switch (intrinsic_type) { case kStatic: diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 441aa0493a..74ff526ac7 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -3431,7 +3431,7 @@ class HInvokeStaticOrDirect : public HInvoke { MethodReference target_method, DispatchInfo dispatch_info, InvokeType original_invoke_type, - InvokeType invoke_type, + InvokeType optimized_invoke_type, ClinitCheckRequirement clinit_check_requirement) : HInvoke(arena, number_of_arguments, @@ -3445,7 +3445,7 @@ class HInvokeStaticOrDirect : public HInvoke { dex_pc, method_index, original_invoke_type), - invoke_type_(invoke_type), + optimized_invoke_type_(optimized_invoke_type), clinit_check_requirement_(clinit_check_requirement), target_method_(target_method), dispatch_info_(dispatch_info) { } @@ -3491,7 +3491,11 @@ class HInvokeStaticOrDirect : public HInvoke { // platform-specific special input, such as PC-relative addressing base. uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); } - InvokeType GetInvokeType() const { return invoke_type_; } + InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; } + void SetOptimizedInvokeType(InvokeType invoke_type) { + optimized_invoke_type_ = invoke_type; + } + MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; } CodePtrLocation GetCodePtrLocation() const { return dispatch_info_.code_ptr_location; } bool IsRecursive() const { return GetMethodLoadKind() == MethodLoadKind::kRecursive; } @@ -3514,6 +3518,7 @@ class HInvokeStaticOrDirect : public HInvoke { } bool HasDirectCodePtr() const { return GetCodePtrLocation() == CodePtrLocation::kCallDirect; } MethodReference GetTargetMethod() const { return target_method_; } + void SetTargetMethod(MethodReference method) { target_method_ = method; } int32_t GetStringInitOffset() const { DCHECK(IsStringInit()); @@ -3539,7 +3544,7 @@ class HInvokeStaticOrDirect : public HInvoke { // Is this instruction a call to a static method? bool IsStatic() const { - return GetInvokeType() == kStatic; + return GetOriginalInvokeType() == kStatic; } // Remove the HClinitCheck or the replacement HLoadClass (set as last input by @@ -3612,7 +3617,7 @@ class HInvokeStaticOrDirect : public HInvoke { void RemoveInputAt(size_t index); private: - const InvokeType invoke_type_; + InvokeType optimized_invoke_type_; ClinitCheckRequirement clinit_check_requirement_; // The target method may refer to different dex file or method index than the original // invoke. This happens for sharpened calls and for calls where a method was redeclared diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index a128079cdb..5e1d1d9954 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -49,7 +49,8 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { } // TODO: Avoid CompilerDriver. - InvokeType invoke_type = invoke->GetOriginalInvokeType(); + InvokeType original_invoke_type = invoke->GetOriginalInvokeType(); + InvokeType optimized_invoke_type = original_invoke_type; MethodReference target_method(&graph_->GetDexFile(), invoke->GetDexMethodIndex()); int vtable_idx; uintptr_t direct_code, direct_method; @@ -58,15 +59,18 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { invoke->GetDexPc(), false /* update_stats: already updated in builder */, true /* enable_devirtualization */, - &invoke_type, + &optimized_invoke_type, &target_method, &vtable_idx, &direct_code, &direct_method); - DCHECK(success); - DCHECK_EQ(invoke_type, invoke->GetInvokeType()); - DCHECK_EQ(target_method.dex_file, invoke->GetTargetMethod().dex_file); - DCHECK_EQ(target_method.dex_method_index, invoke->GetTargetMethod().dex_method_index); + if (!success) { + // TODO: try using kDexCachePcRelative. It's always a valid method load + // kind as long as it's supported by the codegen + return; + } + invoke->SetOptimizedInvokeType(optimized_invoke_type); + invoke->SetTargetMethod(target_method); HInvokeStaticOrDirect::MethodLoadKind method_load_kind; HInvokeStaticOrDirect::CodePtrLocation code_ptr_location; diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 21e4e445e6..dccb1dad3b 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -598,8 +598,12 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, mirror::Object* this_objec } else if (type == kStatic || type == kDirect) { return resolved_method; } else if (type == kSuper) { - return referrer->GetDeclaringClass()->GetSuperClass()->GetVTableEntry( - resolved_method->GetMethodIndex(), sizeof(void*)); + mirror::Class* super_class = referrer->GetDeclaringClass()->GetSuperClass(); + if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) { + // The super class does not have the method. + return nullptr; + } + return super_class->GetVTableEntry(resolved_method->GetMethodIndex(), sizeof(void*)); } else { DCHECK(type == kVirtual); return this_object->GetClass()->GetVTableEntry( diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index abf9ac49e6..c41ee45e67 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1019,15 +1019,21 @@ extern "C" const void* artQuickResolutionTrampoline( // Incompatible class change should have been handled in resolve method. CHECK(!called->CheckIncompatibleClassChange(invoke_type)) << PrettyMethod(called) << " " << invoke_type; - if (virtual_or_interface) { - // Refine called method based on receiver. - CHECK(receiver != nullptr) << invoke_type; - + if (virtual_or_interface || invoke_type == kSuper) { + // Refine called method based on receiver for kVirtual/kInterface, and + // caller for kSuper. ArtMethod* orig_called = called; if (invoke_type == kVirtual) { + CHECK(receiver != nullptr) << invoke_type; called = receiver->GetClass()->FindVirtualMethodForVirtual(called, sizeof(void*)); - } else { + } else if (invoke_type == kInterface) { + CHECK(receiver != nullptr) << invoke_type; called = receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*)); + } else { + DCHECK_EQ(invoke_type, kSuper); + CHECK(caller != nullptr) << invoke_type; + called = caller->GetDeclaringClass()->GetSuperClass()->GetVTableEntry( + called->GetMethodIndex(), sizeof(void*)); } CHECK(called != nullptr) << PrettyMethod(orig_called) << " " diff --git a/test/464-checker-inline-sharpen-calls/src/Main.java b/test/464-checker-inline-sharpen-calls/src/Main.java index 6dce96c9ca..5080f142b1 100644 --- a/test/464-checker-inline-sharpen-calls/src/Main.java +++ b/test/464-checker-inline-sharpen-calls/src/Main.java @@ -19,23 +19,25 @@ public final class Main { public void invokeVirtual() { } - /// CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (before) - /// CHECK-DAG: <<Invoke:v\d+>> InvokeStaticOrDirect + /// CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) builder (after) + /// CHECK-DAG: <<Invoke:v\d+>> InvokeVirtual /// CHECK-DAG: ReturnVoid /// CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (after) + /// CHECK-NOT: InvokeVirtual /// CHECK-NOT: InvokeStaticOrDirect public static void inlineSharpenInvokeVirtual(Main m) { m.invokeVirtual(); } - /// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (before) - /// CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + /// CHECK-START: int Main.inlineSharpenStringInvoke() ssa_builder (after) + /// CHECK-DAG: <<Invoke:i\d+>> InvokeVirtual /// CHECK-DAG: Return [<<Invoke>>] /// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after) /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: InvokeVirtual /// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after) /// CHECK-DAG: <<Field:i\d+>> InstanceFieldGet diff --git a/test/492-checker-inline-invoke-interface/expected.txt b/test/492-checker-inline-invoke-interface/expected.txt index b0014d7529..42b331f722 100644 --- a/test/492-checker-inline-invoke-interface/expected.txt +++ b/test/492-checker-inline-invoke-interface/expected.txt @@ -2,4 +2,4 @@ Hello from clinit java.lang.Exception at ForceStatic.<clinit>(Main.java:24) at Main.$inline$foo(Main.java:31) - at Main.main(Main.java:48) + at Main.main(Main.java:50) diff --git a/test/492-checker-inline-invoke-interface/src/Main.java b/test/492-checker-inline-invoke-interface/src/Main.java index 9a4548542b..a8b63075be 100644 --- a/test/492-checker-inline-invoke-interface/src/Main.java +++ b/test/492-checker-inline-invoke-interface/src/Main.java @@ -31,15 +31,17 @@ public class Main implements Itf { int a = ForceStatic.field; } - /// CHECK-START: void Main.main(java.lang.String[]) inliner (before) - /// CHECK: InvokeStaticOrDirect + /// CHECK-START: void Main.main(java.lang.String[]) ssa_builder (after) /// CHECK: InvokeStaticOrDirect + /// CHECK: InvokeInterface /// CHECK-START: void Main.main(java.lang.String[]) inliner (before) /// CHECK-NOT: ClinitCheck /// CHECK-START: void Main.main(java.lang.String[]) inliner (after) /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: InvokeVirtual + /// CHECK-NOT: InvokeInterface /// CHECK-START: void Main.main(java.lang.String[]) inliner (after) /// CHECK: ClinitCheck diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java index 1b784ae367..3f65d5a312 100644 --- a/test/536-checker-intrinsic-optimization/src/Main.java +++ b/test/536-checker-intrinsic-optimization/src/Main.java @@ -35,7 +35,7 @@ public class Main { } /// CHECK-START: boolean Main.stringEqualsNull() register (after) - /// CHECK: <<Invoke:z\d+>> InvokeStaticOrDirect + /// CHECK: <<Invoke:z\d+>> InvokeVirtual /// CHECK: Return [<<Invoke>>] public static boolean stringEqualsNull() { String o = (String)myObject; @@ -47,7 +47,7 @@ public class Main { } /// CHECK-START-X86: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after) - /// CHECK: InvokeStaticOrDirect + /// CHECK: InvokeVirtual /// CHECK-NOT: test public static boolean stringArgumentNotNull(Object obj) { obj.getClass(); @@ -56,7 +56,7 @@ public class Main { // Test is very brittle as it depends on the order we emit instructions. /// CHECK-START-X86: boolean Main.stringArgumentIsString() disassembly (after) - /// CHECK: InvokeStaticOrDirect + /// CHECK: InvokeVirtual /// CHECK: test /// CHECK: jz/eq // Check that we don't try to compare the classes. diff --git a/test/551-invoke-super/expected.txt b/test/551-invoke-super/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/551-invoke-super/expected.txt diff --git a/test/551-invoke-super/info.txt b/test/551-invoke-super/info.txt new file mode 100644 index 0000000000..864ddfecb6 --- /dev/null +++ b/test/551-invoke-super/info.txt @@ -0,0 +1 @@ +Tests the invoke-super opcode when resolving to an abstract method. diff --git a/test/551-invoke-super/smali/invokesuper.smali b/test/551-invoke-super/smali/invokesuper.smali new file mode 100644 index 0000000000..ad3c218fae --- /dev/null +++ b/test/551-invoke-super/smali/invokesuper.smali @@ -0,0 +1,40 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +.class public LInvokeSuper; +.super LSuperClass; + +.method public constructor <init>()V +.registers 1 + invoke-direct {v0}, LSuperClass;-><init>()V + return-void +.end method + + +.method public run()I +.registers 2 + # Do an invoke super on a non-super class to force complex resolution. + invoke-super {v1}, LInvokeSuper;->returnInt()I + move-result v0 + return v0 +.end method + + +.method public returnInt()I +.registers 2 + const v0, 777 + return v0 +.end method diff --git a/test/551-invoke-super/smali/superclass.smali b/test/551-invoke-super/smali/superclass.smali new file mode 100644 index 0000000000..47fbee7cdb --- /dev/null +++ b/test/551-invoke-super/smali/superclass.smali @@ -0,0 +1,26 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class abstract public LSuperClass; +.super Ljava/lang/Object; + +.method public constructor <init>()V +.registers 1 + invoke-direct {v0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method abstract public returnInt()I +.end method diff --git a/test/551-invoke-super/src/Main.java b/test/551-invoke-super/src/Main.java new file mode 100644 index 0000000000..3a301847d3 --- /dev/null +++ b/test/551-invoke-super/src/Main.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class Main { + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) throws Exception { + Class<?> c = Class.forName("InvokeSuper"); + try { + Method m = c.getMethod("run"); + m.invoke(c.newInstance(), new Object[0]); + throw new Error("Expected AbstractMethodError"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof AbstractMethodError)) { + throw new Error("Expected AbstractMethodError"); + } + } + } +} diff --git a/test/552-invoke-non-existent-super/expected.txt b/test/552-invoke-non-existent-super/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/552-invoke-non-existent-super/expected.txt diff --git a/test/552-invoke-non-existent-super/info.txt b/test/552-invoke-non-existent-super/info.txt new file mode 100644 index 0000000000..c5428d49be --- /dev/null +++ b/test/552-invoke-non-existent-super/info.txt @@ -0,0 +1 @@ +Tests the invoke-super opcode when the super class does not have the method. diff --git a/test/552-invoke-non-existent-super/smali/invokesuper.smali b/test/552-invoke-non-existent-super/smali/invokesuper.smali new file mode 100644 index 0000000000..ad3c218fae --- /dev/null +++ b/test/552-invoke-non-existent-super/smali/invokesuper.smali @@ -0,0 +1,40 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +.class public LInvokeSuper; +.super LSuperClass; + +.method public constructor <init>()V +.registers 1 + invoke-direct {v0}, LSuperClass;-><init>()V + return-void +.end method + + +.method public run()I +.registers 2 + # Do an invoke super on a non-super class to force complex resolution. + invoke-super {v1}, LInvokeSuper;->returnInt()I + move-result v0 + return v0 +.end method + + +.method public returnInt()I +.registers 2 + const v0, 777 + return v0 +.end method diff --git a/test/552-invoke-non-existent-super/smali/superclass.smali b/test/552-invoke-non-existent-super/smali/superclass.smali new file mode 100644 index 0000000000..21d961ea6a --- /dev/null +++ b/test/552-invoke-non-existent-super/smali/superclass.smali @@ -0,0 +1,23 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class abstract public LSuperClass; +.super Ljava/lang/Object; + +.method public constructor <init>()V +.registers 1 + invoke-direct {v0}, Ljava/lang/Object;-><init>()V + return-void +.end method diff --git a/test/552-invoke-non-existent-super/src/Main.java b/test/552-invoke-non-existent-super/src/Main.java new file mode 100644 index 0000000000..c2644711a1 --- /dev/null +++ b/test/552-invoke-non-existent-super/src/Main.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class Main { + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) throws Exception { + Class<?> c = Class.forName("InvokeSuper"); + try { + Method m = c.getMethod("run"); + m.invoke(c.newInstance(), new Object[0]); + throw new Error("Expected NoSuchMethodError"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof NoSuchMethodError)) { + throw new Error("Expected NoSuchMethodError"); + } + } + } +} diff --git a/test/553-invoke-super/expected.txt b/test/553-invoke-super/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/553-invoke-super/expected.txt diff --git a/test/553-invoke-super/info.txt b/test/553-invoke-super/info.txt new file mode 100644 index 0000000000..ad99030a85 --- /dev/null +++ b/test/553-invoke-super/info.txt @@ -0,0 +1 @@ +Tests the invoke-super opcode. diff --git a/test/553-invoke-super/smali/invokesuper.smali b/test/553-invoke-super/smali/invokesuper.smali new file mode 100644 index 0000000000..a6f9b4e5ce --- /dev/null +++ b/test/553-invoke-super/smali/invokesuper.smali @@ -0,0 +1,40 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +.class public LInvokeSuper; +.super LSuperClass; + +.method public constructor <init>()V +.registers 1 + invoke-direct {v0}, LSuperClass;-><init>()V + return-void +.end method + + +.method public run()I +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + invoke-super {v1}, LInvokeSuper;->$noinline$returnInt()I + move-result v0 + return v0 +.end method + + +.method public $noinline$returnInt()I +.registers 2 + const v0, 777 + return v0 +.end method diff --git a/test/553-invoke-super/src/Main.java b/test/553-invoke-super/src/Main.java new file mode 100644 index 0000000000..91d23943cb --- /dev/null +++ b/test/553-invoke-super/src/Main.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + static void assertEquals(int expected, int value) { + if (expected != value) { + throw new Error("Expected " + expected + ", got " + value); + } + } + + public static void main(String[] args) throws Exception { + Class<?> c = Class.forName("InvokeSuper"); + Method m = c.getMethod("run"); + assertEquals(42, ((Integer)m.invoke(c.newInstance(), new Object[0])).intValue()); + } +} diff --git a/test/553-invoke-super/src/SuperClass.java b/test/553-invoke-super/src/SuperClass.java new file mode 100644 index 0000000000..36ce0933f0 --- /dev/null +++ b/test/553-invoke-super/src/SuperClass.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class SuperClass { + boolean doThrow = false; + + public int $noinline$returnInt() { + if (doThrow) { + throw new Error(); + } + return 42; + } +} |