diff options
| -rw-r--r-- | compiler/driver/compiler_options.h | 2 | ||||
| -rw-r--r-- | compiler/optimizing/dead_code_elimination.cc | 1 | ||||
| -rw-r--r-- | compiler/optimizing/inliner.cc | 62 | ||||
| -rw-r--r-- | compiler/optimizing/inliner.h | 6 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 11 | ||||
| -rw-r--r-- | compiler/optimizing/reference_type_propagation.cc | 4 | ||||
| -rw-r--r-- | runtime/art_method.h | 8 | ||||
| -rw-r--r-- | runtime/modifiers.h | 4 | ||||
| -rw-r--r-- | test/441-checker-inliner/src/Main.java | 15 |
9 files changed, 64 insertions, 49 deletions
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index d2a90ec87f..18f215d165 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -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; diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index 78470db834..50cbf5ca77 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -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); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 202f3f074d..ff90f32754 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -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); diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 9062e1ab00..bce5915219 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -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); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 851dd4ff5e..f2db33086c 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -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(); diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 5d029488fd..45b3df008b 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -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); } diff --git a/runtime/art_method.h b/runtime/art_method.h index cec183789e..6cdc4a6bb0 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -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; diff --git a/runtime/modifiers.h b/runtime/modifiers.h index 8b363a686f..0d9ec29db3 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -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. diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java index c108a900e2..96302fb106 100644 --- a/test/441-checker-inliner/src/Main.java +++ b/test/441-checker-inliner/src/Main.java @@ -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) { |