OSDN Git Service

Be more flexible on the code unit size when inlining.
authorNicolas Geoffray <ngeoffray@google.com>
Wed, 12 Aug 2015 03:03:09 +0000 (20:03 -0700)
committerNicolas Geoffray <ngeoffray@google.com>
Thu, 20 Aug 2015 10:00:12 +0000 (11:00 +0100)
This change increases the maximum code unit size, and fold
parameters in the inlinee in the hope to reduce the overall
size of the graph. We then make sure we don't inline methods
that have more than N HInstructions.

Also, remove the kAccDontInline flag on ArtMethod. The compiler
does not need it anymore.

Change-Id: I4cd3da40e551f30ba83b8b274728b87e67f6812e

compiler/driver/compiler_options.h
compiler/optimizing/dead_code_elimination.cc
compiler/optimizing/inliner.cc
compiler/optimizing/inliner.h
compiler/optimizing/nodes.h
compiler/optimizing/reference_type_propagation.cc
runtime/art_method.h
runtime/modifiers.h
test/441-checker-inliner/src/Main.java

index d2a90ec..18f215d 100644 (file)
@@ -52,7 +52,7 @@ class CompilerOptions FINAL {
   static const bool kDefaultGenerateDebugInfo = kIsDebugBuild;
   static const bool kDefaultIncludePatchInformation = false;
   static const size_t kDefaultInlineDepthLimit = 3;
-  static const size_t kDefaultInlineMaxCodeUnits = 18;
+  static const size_t kDefaultInlineMaxCodeUnits = 20;
 
   // Default inlining settings when the space filter is used.
   static constexpr size_t kSpaceFilterInlineDepthLimit = 3;
index 78470db..50cbf5c 100644 (file)
@@ -133,6 +133,7 @@ void HDeadCodeElimination::RemoveDeadInstructions() {
           && !inst->IsSuspendCheck()
           // If we added an explicit barrier then we should keep it.
           && !inst->IsMemoryBarrier()
+          && !inst->IsParameterValue()
           && !inst->HasUses()) {
         block->RemoveInstruction(inst);
         MaybeRecordStat(MethodCompilationStat::kRemovedDeadInstruction);
index 202f3f0..ff90f32 100644 (file)
@@ -40,6 +40,8 @@
 
 namespace art {
 
+static constexpr size_t kMaximumNumberOfHInstructions = 12;
+
 void HInliner::Run() {
   if (graph_->IsDebuggable()) {
     // For simplicity, we currently never inline when the graph is debuggable. This avoids
@@ -169,7 +171,7 @@ static uint32_t FindMethodIndexIn(ArtMethod* method,
   }
 }
 
-bool HInliner::TryInline(HInvoke* invoke_instruction) const {
+bool HInliner::TryInline(HInvoke* invoke_instruction) {
   uint32_t method_index = invoke_instruction->GetDexMethodIndex();
   ScopedObjectAccess soa(Thread::Current());
   const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
@@ -244,12 +246,6 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) const {
     return false;
   }
 
-  if (resolved_method->ShouldNotInline()) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
-                   << " was already flagged as non inlineable";
-    return false;
-  }
-
   if (invoke_instruction->IsInvokeStaticOrDirect() &&
       invoke_instruction->AsInvokeStaticOrDirect()->IsStaticWithImplicitClinitCheck()) {
     // Case of a static method that cannot be inlined because it implicitly
@@ -271,7 +267,7 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) const {
 
 bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
                                  HInvoke* invoke_instruction,
-                                 bool same_dex_file) const {
+                                 bool same_dex_file) {
   ScopedObjectAccess soa(Thread::Current());
   const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
   const DexFile& callee_dex_file = *resolved_method->GetDexFile();
@@ -335,9 +331,6 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
   if (!builder.BuildGraph(*code_item)) {
     VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
                    << " could not be built, so cannot be inlined";
-    // There could be multiple reasons why the graph could not be built, including
-    // unaccessible methods/fields due to using a different dex cache. We do not mark
-    // the method as non-inlineable so that other callers can still try to inline it.
     return false;
   }
 
@@ -345,17 +338,41 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
                                                   compiler_driver_->GetInstructionSet())) {
     VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
                    << " cannot be inlined because of the register allocator";
-    resolved_method->SetShouldNotInline();
     return false;
   }
 
   if (!callee_graph->TryBuildingSsa()) {
     VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
                    << " could not be transformed to SSA";
-    resolved_method->SetShouldNotInline();
     return false;
   }
 
+  size_t parameter_index = 0;
+  for (HInstructionIterator instructions(callee_graph->GetEntryBlock()->GetInstructions());
+       !instructions.Done();
+       instructions.Advance()) {
+    HInstruction* current = instructions.Current();
+    if (current->IsParameterValue()) {
+      HInstruction* argument = invoke_instruction->InputAt(parameter_index++);
+      if (argument->IsNullConstant()) {
+        current->ReplaceWith(callee_graph->GetNullConstant());
+      } else if (argument->IsIntConstant()) {
+        current->ReplaceWith(callee_graph->GetIntConstant(argument->AsIntConstant()->GetValue()));
+      } else if (argument->IsLongConstant()) {
+        current->ReplaceWith(callee_graph->GetLongConstant(argument->AsLongConstant()->GetValue()));
+      } else if (argument->IsFloatConstant()) {
+        current->ReplaceWith(
+            callee_graph->GetFloatConstant(argument->AsFloatConstant()->GetValue()));
+      } else if (argument->IsDoubleConstant()) {
+        current->ReplaceWith(
+            callee_graph->GetDoubleConstant(argument->AsDoubleConstant()->GetValue()));
+      } else if (argument->GetType() == Primitive::kPrimNot) {
+        current->SetReferenceTypeInfo(argument->GetReferenceTypeInfo());
+        current->AsParameterValue()->SetCanBeNull(argument->CanBeNull());
+      }
+    }
+  }
+
   // Run simple optimizations on the graph.
   HDeadCodeElimination dce(callee_graph, stats_);
   HConstantFolding fold(callee_graph);
@@ -365,10 +382,10 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
 
   HOptimization* optimizations[] = {
     &intrinsics,
-    &dce,
-    &fold,
     &type_propagation,
     &simplify,
+    &dce,
+    &fold,
   };
 
   for (size_t i = 0; i < arraysize(optimizations); ++i) {
@@ -376,6 +393,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
     optimization->Run();
   }
 
+  size_t number_of_instructions_budget = kMaximumNumberOfHInstructions;
   if (depth_ + 1 < compiler_driver_->GetCompilerOptions().GetInlineDepthLimit()) {
     HInliner inliner(callee_graph,
                      outer_compilation_unit_,
@@ -385,6 +403,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
                      stats_,
                      depth_ + 1);
     inliner.Run();
+    number_of_instructions_budget += inliner.number_of_inlined_instructions_;
   }
 
   // TODO: We should abort only if all predecessors throw. However,
@@ -394,7 +413,6 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
   if (exit_block == nullptr) {
     VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
                    << " could not be inlined because it has an infinite loop";
-    resolved_method->SetShouldNotInline();
     return false;
   }
 
@@ -408,24 +426,28 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
   if (has_throw_predecessor) {
     VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
                    << " could not be inlined because one branch always throws";
-    resolved_method->SetShouldNotInline();
     return false;
   }
 
   HReversePostOrderIterator it(*callee_graph);
   it.Advance();  // Past the entry block, it does not contain instructions that prevent inlining.
+  size_t number_of_instructions = 0;
   for (; !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
     if (block->IsLoopHeader()) {
       VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
                      << " could not be inlined because it contains a loop";
-      resolved_method->SetShouldNotInline();
       return false;
     }
 
     for (HInstructionIterator instr_it(block->GetInstructions());
          !instr_it.Done();
          instr_it.Advance()) {
+      if (number_of_instructions++ ==  number_of_instructions_budget) {
+        VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
+                       << " could not be inlined because it is too big.";
+        return false;
+      }
       HInstruction* current = instr_it.Current();
 
       if (current->IsInvokeInterface()) {
@@ -433,7 +455,6 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
         // resolution conflict is currently too high.
         VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
                        << " could not be inlined because it has an interface call.";
-        resolved_method->SetShouldNotInline();
         return false;
       }
 
@@ -448,12 +469,11 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
         VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
                        << " could not be inlined because " << current->DebugName()
                        << " it is in a different dex file and requires access to the dex cache";
-        // Do not flag the method as not-inlineable. A caller within the same
-        // dex file could still successfully inline it.
         return false;
       }
     }
   }
+  number_of_inlined_instructions_ += number_of_instructions;
 
   HInstruction* return_replacement = callee_graph->InlineInto(graph_, invoke_instruction);
 
index 9062e1a..bce5915 100644 (file)
@@ -42,6 +42,7 @@ class HInliner : public HOptimization {
         caller_compilation_unit_(caller_compilation_unit),
         compiler_driver_(compiler_driver),
         depth_(depth),
+        number_of_inlined_instructions_(0),
         handles_(handles) {}
 
   void Run() OVERRIDE;
@@ -49,15 +50,16 @@ class HInliner : public HOptimization {
   static constexpr const char* kInlinerPassName = "inliner";
 
  private:
-  bool TryInline(HInvoke* invoke_instruction) const;
+  bool TryInline(HInvoke* invoke_instruction);
   bool TryBuildAndInline(ArtMethod* resolved_method,
                          HInvoke* invoke_instruction,
-                         bool same_dex_file) const;
+                         bool same_dex_file);
 
   const DexCompilationUnit& outer_compilation_unit_;
   const DexCompilationUnit& caller_compilation_unit_;
   CompilerDriver* const compiler_driver_;
   const size_t depth_;
+  size_t number_of_inlined_instructions_;
   StackHandleScopeCollection* const handles_;
 
   DISALLOW_COPY_AND_ASSIGN(HInliner);
index 851dd4f..f2db330 100644 (file)
@@ -3775,11 +3775,15 @@ class HXor : public HBinaryOperation {
 class HParameterValue : public HExpression<0> {
  public:
   HParameterValue(uint8_t index, Primitive::Type parameter_type, bool is_this = false)
-      : HExpression(parameter_type, SideEffects::None()), index_(index), is_this_(is_this) {}
+      : HExpression(parameter_type, SideEffects::None()),
+        index_(index),
+        is_this_(is_this),
+        can_be_null_(!is_this) {}
 
   uint8_t GetIndex() const { return index_; }
 
-  bool CanBeNull() const OVERRIDE { return !is_this_; }
+  bool CanBeNull() const OVERRIDE { return can_be_null_; }
+  void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; }
 
   bool IsThis() const { return is_this_; }
 
@@ -3793,6 +3797,8 @@ class HParameterValue : public HExpression<0> {
   // Whether or not the parameter value corresponds to 'this' argument.
   const bool is_this_;
 
+  bool can_be_null_;
+
   DISALLOW_COPY_AND_ASSIGN(HParameterValue);
 };
 
@@ -4444,6 +4450,7 @@ class HLoadString : public HExpression<1> {
   // TODO: Can we deopt or debug when we resolve a string?
   bool NeedsEnvironment() const OVERRIDE { return false; }
   bool NeedsDexCache() const OVERRIDE { return true; }
+  bool CanBeNull() const OVERRIDE { return false; }
 
   static SideEffects SideEffectsForArchRuntimeCalls() {
     return SideEffects::CanTriggerGC();
index 5d02948..45b3df0 100644 (file)
@@ -414,7 +414,9 @@ void RTPVisitor::VisitNewArray(HNewArray* instr) {
 }
 
 void RTPVisitor::VisitParameterValue(HParameterValue* instr) {
-  if (instr->GetType() == Primitive::kPrimNot) {
+  ScopedObjectAccess soa(Thread::Current());
+  // We check if the existing type is valid: the inliner may have set it.
+  if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) {
     // TODO: parse the signature and add precise types for the parameters.
     SetClassAsTypeInfo(instr, nullptr, /* is_exact */ false);
   }
index cec1837..6cdc4a6 100644 (file)
@@ -135,14 +135,6 @@ class ArtMethod FINAL {
     return (GetAccessFlags() & kAccNative) != 0;
   }
 
-  bool ShouldNotInline() SHARED_REQUIRES(Locks::mutator_lock_) {
-    return (GetAccessFlags() & kAccDontInline) != 0;
-  }
-
-  void SetShouldNotInline() SHARED_REQUIRES(Locks::mutator_lock_) {
-    SetAccessFlags(GetAccessFlags() | kAccDontInline);
-  }
-
   bool IsFastNative() SHARED_REQUIRES(Locks::mutator_lock_) {
     uint32_t mask = kAccFastNative | kAccNative;
     return (GetAccessFlags() & mask) == mask;
index 8b363a6..0d9ec29 100644 (file)
@@ -48,10 +48,6 @@ static constexpr uint32_t kAccPreverified =          0x00080000;  // class (runt
 static constexpr uint32_t kAccFastNative =           0x00080000;  // method (dex only)
 static constexpr uint32_t kAccMiranda =              0x00200000;  // method (dex only)
 
-// Flag is set if the compiler decides it is not worth trying
-// to inline the method. This avoids other callers to try it again and again.
-static constexpr uint32_t kAccDontInline =           0x00400000;  // method (dex only)
-
 // Special runtime-only flags.
 // Note: if only kAccClassIsReference is set, we have a soft reference.
 
index c108a90..96302fb 100644 (file)
@@ -99,10 +99,8 @@ public class Main {
   /// CHECK-DAG:                     Return [<<Result>>]
 
   /// CHECK-START: int Main.InlineAdd() inliner (after)
-  /// CHECK-DAG:     <<Const3:i\d+>> IntConstant 3
-  /// CHECK-DAG:     <<Const5:i\d+>> IntConstant 5
-  /// CHECK-DAG:     <<Add:i\d+>>    Add [<<Const3>>,<<Const5>>]
-  /// CHECK-DAG:                     Return [<<Add>>]
+  /// CHECK-DAG:     <<Const8:i\d+>> IntConstant 8
+  /// CHECK-DAG:                     Return [<<Const8>>]
 
   public static int InlineAdd() {
     return returnAdd(3, 5);
@@ -136,12 +134,9 @@ public class Main {
   /// CHECK-DAG:                     Return [<<Phi>>]
 
   /// CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (after)
-  /// CHECK-DAG:     <<Const1:i\d+>> IntConstant 1
-  /// CHECK-DAG:     <<Const3:i\d+>> IntConstant 3
-  /// CHECK-DAG:     <<Const5:i\d+>> IntConstant 5
-  /// CHECK-DAG:     <<Add:i\d+>>    Add [<<Const1>>,<<Const3>>]
-  /// CHECK-DAG:     <<Sub:i\d+>>    Sub [<<Const5>>,<<Const3>>]
-  /// CHECK-DAG:     <<Phi:i\d+>>    Phi [<<Add>>,<<Sub>>]
+  /// CHECK-DAG:     <<Const4:i\d+>> IntConstant 4
+  /// CHECK-DAG:     <<Const2:i\d+>> IntConstant 2
+  /// CHECK-DAG:     <<Phi:i\d+>>    Phi [<<Const4>>,<<Const2>>]
   /// CHECK-DAG:                     Return [<<Phi>>]
 
   public static int InlineWithControlFlow(boolean cond) {