in compiler/optimizing/inliner.cc [1028:1275]
bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
bool same_dex_file,
HInstruction** return_replacement) {
ScopedObjectAccess soa(Thread::Current());
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& callee_dex_file = *resolved_method->GetDexFile();
uint32_t method_index = resolved_method->GetDexMethodIndex();
ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(handles_->NewHandle(
resolved_method->GetDeclaringClass()->GetClassLoader()));
DexCompilationUnit dex_compilation_unit(
class_loader.ToJObject(),
class_linker,
callee_dex_file,
code_item,
resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
method_index,
resolved_method->GetAccessFlags(),
/* verified_method */ nullptr,
dex_cache);
bool requires_ctor_barrier = false;
if (dex_compilation_unit.IsConstructor()) {
// If it's a super invocation and we already generate a barrier there's no need
// to generate another one.
// We identify super calls by looking at the "this" pointer. If its value is the
// same as the local "this" pointer then we must have a super invocation.
bool is_super_invocation = invoke_instruction->InputAt(0)->IsParameterValue()
&& invoke_instruction->InputAt(0)->AsParameterValue()->IsThis();
if (is_super_invocation && graph_->ShouldGenerateConstructorBarrier()) {
requires_ctor_barrier = false;
} else {
Thread* self = Thread::Current();
requires_ctor_barrier = compiler_driver_->RequiresConstructorBarrier(self,
dex_compilation_unit.GetDexFile(),
dex_compilation_unit.GetClassDefIndex());
}
}
InvokeType invoke_type = invoke_instruction->GetOriginalInvokeType();
if (invoke_type == kInterface) {
// We have statically resolved the dispatch. To please the class linker
// at runtime, we change this call as if it was a virtual call.
invoke_type = kVirtual;
}
const int32_t caller_instruction_counter = graph_->GetCurrentInstructionId();
HGraph* callee_graph = new (graph_->GetArena()) HGraph(
graph_->GetArena(),
callee_dex_file,
method_index,
requires_ctor_barrier,
compiler_driver_->GetInstructionSet(),
invoke_type,
graph_->IsDebuggable(),
/* osr */ false,
caller_instruction_counter);
callee_graph->SetArtMethod(resolved_method);
// When they are needed, allocate `inline_stats` on the heap instead
// of on the stack, as Clang might produce a stack frame too large
// for this function, that would not fit the requirements of the
// `-Wframe-larger-than` option.
std::unique_ptr<OptimizingCompilerStats> inline_stats =
(stats_ == nullptr) ? nullptr : MakeUnique<OptimizingCompilerStats>();
HGraphBuilder builder(callee_graph,
&dex_compilation_unit,
&outer_compilation_unit_,
resolved_method->GetDexFile(),
*code_item,
compiler_driver_,
inline_stats.get(),
resolved_method->GetQuickenedInfo(),
dex_cache,
handles_);
if (builder.BuildGraph() != kAnalysisSuccess) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be built, so cannot be inlined";
return false;
}
if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph,
compiler_driver_->GetInstructionSet())) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " cannot be inlined because of the register allocator";
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());
}
}
}
size_t number_of_instructions_budget = kMaximumNumberOfHInstructions;
size_t number_of_inlined_instructions =
RunOptimizations(callee_graph, code_item, dex_compilation_unit);
number_of_instructions_budget += number_of_inlined_instructions;
// TODO: We should abort only if all predecessors throw. However,
// HGraph::InlineInto currently does not handle an exit block with
// a throw predecessor.
HBasicBlock* exit_block = callee_graph->GetExitBlock();
if (exit_block == nullptr) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be inlined because it has an infinite loop";
return false;
}
bool has_throw_predecessor = false;
for (HBasicBlock* predecessor : exit_block->GetPredecessors()) {
if (predecessor->GetLastInstruction()->IsThrow()) {
has_throw_predecessor = true;
break;
}
}
if (has_throw_predecessor) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be inlined because one branch always throws";
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;
bool can_inline_environment =
total_number_of_dex_registers_ < kMaximumNumberOfCumulatedDexRegisters;
for (; !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
if (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible()) {
// Don't inline methods with irreducible loops, they could prevent some
// optimizations to run.
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be inlined because it contains an irreducible loop";
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)
<< " is not inlined because its caller has reached"
<< " its instruction budget limit.";
return false;
}
HInstruction* current = instr_it.Current();
if (!can_inline_environment && current->NeedsEnvironment()) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " is not inlined because its caller has reached"
<< " its environment budget limit.";
return false;
}
if (current->IsInvokeInterface()) {
// Disable inlining of interface calls. The cost in case of entering the
// 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.";
return false;
}
if (!same_dex_file && current->NeedsEnvironment()) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be inlined because " << current->DebugName()
<< " needs an environment and is in a different dex file";
return false;
}
if (!same_dex_file && current->NeedsDexCacheOfDeclaringClass()) {
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";
return false;
}
if (current->IsNewInstance() &&
(current->AsNewInstance()->GetEntrypoint() == kQuickAllocObjectWithAccessCheck)) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be inlined because it is using an entrypoint"
<< " with access checks";
// Allocation entrypoint does not handle inlined frames.
return false;
}
if (current->IsNewArray() &&
(current->AsNewArray()->GetEntrypoint() == kQuickAllocArrayWithAccessCheck)) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be inlined because it is using an entrypoint"
<< " with access checks";
// Allocation entrypoint does not handle inlined frames.
return false;
}
if (current->IsUnresolvedStaticFieldGet() ||
current->IsUnresolvedInstanceFieldGet() ||
current->IsUnresolvedStaticFieldSet() ||
current->IsUnresolvedInstanceFieldSet()) {
// Entrypoint for unresolved fields does not handle inlined frames.
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be inlined because it is using an unresolved"
<< " entrypoint";
return false;
}
}
}
number_of_inlined_instructions_ += number_of_instructions;
DCHECK_EQ(caller_instruction_counter, graph_->GetCurrentInstructionId())
<< "No instructions can be added to the outer graph while inner graph is being built";
const int32_t callee_instruction_counter = callee_graph->GetCurrentInstructionId();
graph_->SetCurrentInstructionId(callee_instruction_counter);
*return_replacement = callee_graph->InlineInto(graph_, invoke_instruction);
DCHECK_EQ(callee_instruction_counter, callee_graph->GetCurrentInstructionId())
<< "No instructions can be added to the inner graph during inlining into the outer graph";
return true;
}