Merge "Be more flexible on the code unit size when inlining."
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index d2a90ec..18f215d 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -52,7 +52,7 @@
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 78470db..50cbf5c 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -133,6 +133,7 @@
&& !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 202f3f0..ff90f32 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 @@
}
}
-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 @@
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::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 @@
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 @@
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 @@
HOptimization* optimizations[] = {
&intrinsics,
- &dce,
- &fold,
&type_propagation,
&simplify,
+ &dce,
+ &fold,
};
for (size_t i = 0; i < arraysize(optimizations); ++i) {
@@ -376,6 +393,7 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 9062e1a..bce5915 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -42,6 +42,7 @@
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 @@
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 851dd4f..f2db330 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3775,11 +3775,15 @@
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 @@
// 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 @@
// 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 5d02948..45b3df0 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -414,7 +414,9 @@
}
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 cec1837..6cdc4a6 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -135,14 +135,6 @@
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 8b363a6..0d9ec29 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -48,10 +48,6 @@
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 c108a90..96302fb 100644
--- a/test/441-checker-inliner/src/Main.java
+++ b/test/441-checker-inliner/src/Main.java
@@ -99,10 +99,8 @@
/// 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 @@
/// 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) {