From 2f40d24aea4b9b2726c994de71b17ae2f82e9238 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 8 Apr 2020 12:56:45 +0100 Subject: Small cleanup in InstructionBuilder. Refactor BuildInvoke() to reduce runtime state transitions (fewer ScopedObjectAccess objects) and separate the class init check for static methods from the instruction creation in preparation for allocating replacement instructions for intrinsics such as Math.abs(). Delay Handle<> creation in ProcessClinitCheckForInvoke until it's actually needed. Change function parameters to ObjPtr<> instead of Handle<> if they cannot cause thread suspension. Test: aosp_taimen-userdebug boots. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I8d5ebf5db76f0c8b1fec790a2f8621818d64b4dc --- compiler/optimizing/instruction_builder.cc | 161 ++++++++++++++++------------- 1 file changed, 91 insertions(+), 70 deletions(-) (limited to 'compiler/optimizing/instruction_builder.cc') 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 class_loader = dex_compilation_unit_->GetClassLoader(); + ClassLinker* class_linker = dex_compilation_unit.GetClassLinker(); + Handle class_loader = dex_compilation_unit.GetClassLoader(); ArtMethod* resolved_method = class_linker->ResolveMethod( 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 compiling_class = dex_compilation_unit_->GetCompilingClass().Get(); + if (*invoke_type == kSuper) { + ObjPtr 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 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. 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(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 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 cls, return true; } -bool HInstructionBuilder::IsInitialized(Handle cls) const { +bool HInstructionBuilder::IsInitialized(ObjPtr cls) const { if (cls == nullptr) { return false; } @@ -1318,13 +1334,13 @@ bool HInstructionBuilder::IsInitialized(Handle 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 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 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 klass = handles_->NewHandle(resolved_method->GetDeclaringClass()); + ScopedObjectAccess soa(Thread::Current()); + ObjPtr 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 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 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 HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa, return h_klass; } -bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle klass) { +bool HInstructionBuilder::LoadClassNeedsAccessCheck(ObjPtr klass) { if (klass == nullptr) { return true; } else if (klass->IsPublic()) { return false; } else { ObjPtr 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 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); -- cgit v1.2.3-59-g8ed1b