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
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 332aa26..aff059f 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -819,19 +819,28 @@
   }
 }
 
-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 @@
 
   // 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 @@
   // 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 @@
           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 @@
     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 @@
     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 @@
   }
 
   // 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 @@
   }
 
   // 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 @@
                                                     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 @@
   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 @@
   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 @@
     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 @@
   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 @@
   // 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 @@
     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 @@
   }
 
   HInstruction* cls = constant;
-  if (!IsInitialized(klass)) {
+  if (!IsInitialized(klass.Get())) {
     cls = new (allocator_) HClinitCheck(constant, dex_pc);
     AppendInstruction(cls);
   }
@@ -1977,7 +1998,7 @@
   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 @@
   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 @@
   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);