diff options
Diffstat (limited to 'compiler/optimizing/instruction_builder.cc')
-rw-r--r-- | compiler/optimizing/instruction_builder.cc | 161 |
1 files changed, 91 insertions, 70 deletions
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 332aa262cb..aff059fdb5 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -819,19 +819,28 @@ static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) { } } -ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) { +// Try to resolve a method using the class linker. Return null if a method could +// not be resolved or the resolved method cannot be used for some reason. +// Also retrieve method data needed for creating the invoke intermediate +// representation while we hold the mutator lock here. +static ArtMethod* ResolveMethod(uint16_t method_idx, + ArtMethod* referrer, + const DexCompilationUnit& dex_compilation_unit, + /*inout*/InvokeType* invoke_type, + /*out*/MethodReference* target_method, + /*out*/bool* is_string_constructor) { ScopedObjectAccess soa(Thread::Current()); - ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); - Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); + ClassLinker* class_linker = dex_compilation_unit.GetClassLinker(); + Handle<mirror::ClassLoader> class_loader = dex_compilation_unit.GetClassLoader(); ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( method_idx, - dex_compilation_unit_->GetDexCache(), + dex_compilation_unit.GetDexCache(), class_loader, - graph_->GetArtMethod(), - invoke_type); + referrer, + *invoke_type); if (UNLIKELY(resolved_method == nullptr)) { // Clean up any exception left by type resolution. @@ -842,7 +851,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in // The referrer may be unresolved for AOT if we're compiling a class that cannot be // resolved because, for example, we don't find a superclass in the classpath. - if (graph_->GetArtMethod() == nullptr) { + if (referrer == nullptr) { // The class linker cannot check access without a referrer, so we have to do it. // Fall back to HInvokeUnresolved if the method isn't public. if (!resolved_method->IsPublic()) { @@ -854,16 +863,16 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in // We need to look at the referrer's super class vtable. We need to do this to know if we need to // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of // which require runtime handling. - if (invoke_type == kSuper) { - ObjPtr<mirror::Class> compiling_class = dex_compilation_unit_->GetCompilingClass().Get(); + if (*invoke_type == kSuper) { + ObjPtr<mirror::Class> compiling_class = dex_compilation_unit.GetCompilingClass().Get(); if (compiling_class == nullptr) { // We could not determine the method's class we need to wait until runtime. DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; } ObjPtr<mirror::Class> referenced_class = class_linker->LookupResolvedType( - dex_compilation_unit_->GetDexFile()->GetMethodId(method_idx).class_idx_, - dex_compilation_unit_->GetDexCache().Get(), + dex_compilation_unit.GetDexFile()->GetMethodId(method_idx).class_idx_, + dex_compilation_unit.GetDexCache().Get(), class_loader.Get()); DCHECK(referenced_class != nullptr); // We have already resolved a method from this class. if (!referenced_class->IsAssignableFrom(compiling_class)) { @@ -881,7 +890,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in vtable_index, class_linker->GetImagePointerSize()); } if (actual_method != resolved_method && - !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) { + !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit.GetDexFile())) { // The back-end code generator relies on this check in order to ensure that it will not // attempt to read the dex_cache with a dex_method_index that is not from the correct // dex_file. If we didn't do this check then the dex_method_index will not be updated in the @@ -900,12 +909,33 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in resolved_method = actual_method; } - return resolved_method; -} + if (*invoke_type == kInterface) { + if (resolved_method->GetDeclaringClass()->IsObjectClass()) { + // If the resolved method is from j.l.Object, emit a virtual call instead. + // The IMT conflict stub only handles interface methods. + *invoke_type = kVirtual; + } else { + DCHECK(resolved_method->GetDeclaringClass()->IsInterface()); + } + } -static bool IsStringConstructor(ArtMethod* method) { - ScopedObjectAccess soa(Thread::Current()); - return method->GetDeclaringClass()->IsStringClass() && method->IsConstructor(); + if (*invoke_type == kDirect || *invoke_type == kStatic || *invoke_type == kSuper) { + // Record the target method needed for HInvokeStaticOrDirect. + *target_method = + MethodReference(resolved_method->GetDexFile(), resolved_method->GetDexMethodIndex()); + } else if (*invoke_type == kVirtual) { + // For HInvokeVirtual we need the vtable index. + *target_method = MethodReference(/*file=*/ nullptr, resolved_method->GetVtableIndex()); + } else { + DCHECK_EQ(*invoke_type, kInterface); + // For HInvokeInterface we need the IMT index. + *target_method = MethodReference(/*file=*/ nullptr, ImTable::GetImtIndex(resolved_method)); + } + + *is_string_constructor = + resolved_method->IsConstructor() && resolved_method->GetDeclaringClass()->IsStringClass(); + + return resolved_method; } bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, @@ -923,7 +953,14 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, number_of_arguments++; } - ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type); + MethodReference target_method(nullptr, 0u); + bool is_string_constructor = false; + ArtMethod* resolved_method = ResolveMethod(method_idx, + graph_->GetArtMethod(), + *dex_compilation_unit_, + &invoke_type, + &target_method, + &is_string_constructor); if (UNLIKELY(resolved_method == nullptr)) { DCHECK(!Thread::Current()->IsExceptionPending()); @@ -939,16 +976,13 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, } // Replace calls to String.<init> with StringFactory. - if (IsStringConstructor(resolved_method)) { + if (is_string_constructor) { uint32_t string_init_entry_point = WellKnownClasses::StringInitToEntryPoint(resolved_method); HInvokeStaticOrDirect::DispatchInfo dispatch_info = { HInvokeStaticOrDirect::MethodLoadKind::kStringInit, HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, dchecked_integral_cast<uint64_t>(string_init_entry_point) }; - ScopedObjectAccess soa(Thread::Current()); - MethodReference target_method(resolved_method->GetDexFile(), - resolved_method->GetDexMethodIndex()); // We pass null for the resolved_method to ensure optimizations // don't rely on it. HInvoke* invoke = new (allocator_) HInvokeStaticOrDirect( @@ -966,29 +1000,25 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, } // Potential class initialization check, in the case of a static method call. + HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement = + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; HClinitCheck* clinit_check = nullptr; + if (invoke_type == kStatic) { + clinit_check = ProcessClinitCheckForInvoke(dex_pc, resolved_method, &clinit_check_requirement); + } + HInvoke* invoke = nullptr; 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; - ScopedObjectAccess soa(Thread::Current()); - if (invoke_type == kStatic) { - clinit_check = - ProcessClinitCheckForInvoke(dex_pc, resolved_method, &clinit_check_requirement); - } else if (invoke_type == kSuper) { - if (IsSameDexFile(*resolved_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) { + if (invoke_type == kSuper) { + if (IsSameDexFile(*target_method.dex_file, *dex_compilation_unit_->GetDexFile())) { // Update the method index 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(); + method_idx = target_method.index; } } HInvokeStaticOrDirect::DispatchInfo dispatch_info = HSharpening::SharpenInvokeStaticOrDirect(resolved_method, code_generator_); - MethodReference target_method(resolved_method->GetDexFile(), - resolved_method->GetDexMethodIndex()); invoke = new (allocator_) HInvokeStaticOrDirect(allocator_, number_of_arguments, return_type, @@ -1000,37 +1030,23 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, target_method, clinit_check_requirement); } else if (invoke_type == kVirtual) { - ScopedObjectAccess soa(Thread::Current()); // Needed for the method index + DCHECK(target_method.dex_file == nullptr); invoke = new (allocator_) HInvokeVirtual(allocator_, number_of_arguments, return_type, dex_pc, method_idx, resolved_method, - resolved_method->GetMethodIndex()); + /*vtable_index=*/ target_method.index); } else { DCHECK_EQ(invoke_type, kInterface); - ScopedObjectAccess soa(Thread::Current()); // Needed for the IMT index and class check below. - if (resolved_method->GetDeclaringClass()->IsObjectClass()) { - // If the resolved method is from j.l.Object, emit a virtual call instead. - // The IMT conflict stub only handles interface methods. - invoke = new (allocator_) HInvokeVirtual(allocator_, + invoke = new (allocator_) HInvokeInterface(allocator_, number_of_arguments, return_type, dex_pc, method_idx, resolved_method, - resolved_method->GetMethodIndex()); - } else { - DCHECK(resolved_method->GetDeclaringClass()->IsInterface()); - invoke = new (allocator_) HInvokeInterface(allocator_, - number_of_arguments, - return_type, - dex_pc, - method_idx, - resolved_method, - ImTable::GetImtIndex(resolved_method)); - } + /*imt_index=*/ target_method.index); } return HandleInvoke(invoke, operands, shorty, /* is_unresolved= */ false, clinit_check); } @@ -1075,7 +1091,7 @@ HNewInstance* HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, u HInstruction* cls = load_class; Handle<mirror::Class> klass = load_class->GetClass(); - if (!IsInitialized(klass)) { + if (!IsInitialized(klass.Get())) { cls = new (allocator_) HClinitCheck(load_class, dex_pc); AppendInstruction(cls); } @@ -1307,7 +1323,7 @@ static bool HasTrivialInitialization(ObjPtr<mirror::Class> cls, return true; } -bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const { +bool HInstructionBuilder::IsInitialized(ObjPtr<mirror::Class> cls) const { if (cls == nullptr) { return false; } @@ -1318,13 +1334,13 @@ bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const { if (runtime->IsAotCompiler()) { // Assume loaded only if klass is in the boot image. App classes cannot be assumed // loaded because we don't even know what class loader will be used to load them. - if (IsInBootImage(cls.Get(), code_generator_->GetCompilerOptions())) { + if (IsInBootImage(cls, code_generator_->GetCompilerOptions())) { return true; } } else { DCHECK(runtime->UseJitCompilation()); if (Runtime::Current()->GetJit()->CanAssumeInitialized( - cls.Get(), + cls, graph_->IsCompilingForSharedJitCode())) { // For JIT, the class cannot revert to an uninitialized state. return true; @@ -1343,7 +1359,7 @@ bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const { auto is_static_method_or_constructor_of_cls = [cls](const DexCompilationUnit& compilation_unit) REQUIRES_SHARED(Locks::mutator_lock_) { return (compilation_unit.GetAccessFlags() & (kAccStatic | kAccConstructor)) != 0u && - compilation_unit.GetCompilingClass().Get() == cls.Get(); + compilation_unit.GetCompilingClass().Get() == cls; }; if (is_static_method_or_constructor_of_cls(*outer_compilation_unit_) || // Check also the innermost method. Though excessive copies of ClinitCheck can be @@ -1374,12 +1390,12 @@ bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const { // TODO: We should walk over the entire inlined methods chain, but we don't pass that // information to the builder. (We could also check if we're guaranteed a non-null instance // of `cls` at this location but that's outside the scope of the instruction builder.) - bool is_subclass = IsSubClass(outer_compilation_unit_->GetCompilingClass().Get(), cls.Get()); + bool is_subclass = IsSubClass(outer_compilation_unit_->GetCompilingClass().Get(), cls); if (dex_compilation_unit_ != outer_compilation_unit_) { is_subclass = is_subclass || - IsSubClass(dex_compilation_unit_->GetCompilingClass().Get(), cls.Get()); + IsSubClass(dex_compilation_unit_->GetCompilingClass().Get(), cls); } - if (is_subclass && HasTrivialInitialization(cls.Get(), code_generator_->GetCompilerOptions())) { + if (is_subclass && HasTrivialInitialization(cls, code_generator_->GetCompilerOptions())) { return true; } @@ -1390,21 +1406,26 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( uint32_t dex_pc, ArtMethod* resolved_method, HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) { - Handle<mirror::Class> klass = handles_->NewHandle(resolved_method->GetDeclaringClass()); + ScopedObjectAccess soa(Thread::Current()); + ObjPtr<mirror::Class> klass = resolved_method->GetDeclaringClass(); HClinitCheck* clinit_check = nullptr; if (IsInitialized(klass)) { *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; } else { - HLoadClass* cls = BuildLoadClass(klass->GetDexTypeIndex(), - klass->GetDexFile(), - klass, + Handle<mirror::Class> h_klass = handles_->NewHandle(klass); + HLoadClass* cls = BuildLoadClass(h_klass->GetDexTypeIndex(), + h_klass->GetDexFile(), + h_klass, dex_pc, /* needs_access_check= */ false); if (cls != nullptr) { *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit; clinit_check = new (allocator_) HClinitCheck(cls, dex_pc); AppendInstruction(clinit_check); + } else { + // Let the invoke handle this with an implicit class initialization check. + *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit; } } return clinit_check; @@ -1736,7 +1757,7 @@ void HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, } HInstruction* cls = constant; - if (!IsInitialized(klass)) { + if (!IsInitialized(klass.Get())) { cls = new (allocator_) HClinitCheck(constant, dex_pc); AppendInstruction(cls); } @@ -1977,7 +1998,7 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint3 ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); Handle<mirror::Class> klass = ResolveClass(soa, type_index); - bool needs_access_check = LoadClassNeedsAccessCheck(klass); + bool needs_access_check = LoadClassNeedsAccessCheck(klass.Get()); return BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); } @@ -2040,14 +2061,14 @@ Handle<mirror::Class> HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa, return h_klass; } -bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) { +bool HInstructionBuilder::LoadClassNeedsAccessCheck(ObjPtr<mirror::Class> klass) { if (klass == nullptr) { return true; } else if (klass->IsPublic()) { return false; } else { ObjPtr<mirror::Class> compiling_class = dex_compilation_unit_->GetCompilingClass().Get(); - return compiling_class == nullptr || !compiling_class->CanAccess(klass.Get()); + return compiling_class == nullptr || !compiling_class->CanAccess(klass); } } @@ -2075,7 +2096,7 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); Handle<mirror::Class> klass = ResolveClass(soa, type_index); - bool needs_access_check = LoadClassNeedsAccessCheck(klass); + bool needs_access_check = LoadClassNeedsAccessCheck(klass.Get()); TypeCheckKind check_kind = HSharpening::ComputeTypeCheckKind( klass.Get(), code_generator_, needs_access_check); |