[turbofan] First rudimentary inlining heuristic.

This is a first prototype for a rudimentary inlining heuristic allowing
enabling of general inlining based existing budget flags. Also note that
this approach does not yet work for multi-level inlining, for now the
list of candidates is processed exactly once.

R=bmeurer@chromium.org

Review URL: https://codereview.chromium.org/1406543002

Cr-Commit-Position: refs/heads/master@{#31249}
This commit is contained in:
mstarzinger 2015-10-14 01:37:06 -07:00 committed by Commit bot
parent 6628b77d57
commit 8ff6a0c005
3 changed files with 105 additions and 5 deletions

View File

@ -4,6 +4,7 @@
#include "src/compiler/js-inlining-heuristic.h"
#include "src/compiler/dead-code-elimination.h" // TODO(mstarzinger): Remove!
#include "src/compiler/node-matchers.h"
#include "src/objects-inl.h"
@ -24,9 +25,88 @@ Reduction JSInliningHeuristic::Reduce(Node* node) {
return inliner_.ReduceJSCallFunction(node, function);
}
// All other functions are only handled with general inlining.
if (mode_ == kRestrictedInlining) return NoChange();
return inliner_.ReduceJSCallFunction(node, function);
// Handling of special inlining modes right away:
// - For restricted inlining: stop all handling at this point.
// - For stressing inlining: immediately handle all functions.
switch (mode_) {
case kRestrictedInlining:
return NoChange();
case kStressInlining:
return inliner_.ReduceJSCallFunction(node, function);
case kGeneralInlining:
break;
}
// ---------------------------------------------------------------------------
// Everything below this line is part of the inlining heuristic.
// ---------------------------------------------------------------------------
// Built-in functions are handled by the JSBuiltinReducer.
if (function->shared()->HasBuiltinFunctionId()) return NoChange();
// Quick check on source code length to avoid parsing large candidate.
if (function->shared()->SourceSize() > FLAG_max_inlined_source_size) {
return NoChange();
}
// Quick check on the size of the AST to avoid parsing large candidate.
if (function->shared()->ast_node_count() > FLAG_max_inlined_nodes) {
return NoChange();
}
// Gather feedback on how often this call site has been hit before.
CallFunctionParameters p = CallFunctionParametersOf(node->op());
CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
int calls = nexus.ExtractCallCount();
// ---------------------------------------------------------------------------
// Everything above this line is part of the inlining heuristic.
// ---------------------------------------------------------------------------
// In the general case we remember the candidate for later.
candidates_.push_back({function, node, calls});
return NoChange();
}
void JSInliningHeuristic::ProcessCandidates() {
if (candidates_.empty()) return; // Nothing to do without candidates.
std::sort(candidates_.begin(), candidates_.end(), Compare);
if (FLAG_trace_turbo_inlining) PrintCandidates();
int cumulative_count = 0;
for (const Candidate& candidate : candidates_) {
if (cumulative_count > FLAG_max_inlined_nodes_cumulative) break;
inliner_.ReduceJSCallFunction(candidate.node, candidate.function);
cumulative_count += candidate.function->shared()->ast_node_count();
}
// TODO(mstarzinger): Temporary workaround to eliminate dead control from the
// graph being introduced by the inliner. Make this part of the pipeline.
GraphReducer graph_reducer(local_zone_, jsgraph_->graph(), jsgraph_->Dead());
DeadCodeElimination dead_code_elimination(&graph_reducer, jsgraph_->graph(),
jsgraph_->common());
graph_reducer.AddReducer(&dead_code_elimination);
graph_reducer.ReduceGraph();
}
// static
bool JSInliningHeuristic::Compare(const Candidate& left,
const Candidate& right) {
return left.calls > right.calls;
}
void JSInliningHeuristic::PrintCandidates() {
PrintF("Candidates for inlining (size=%zu):\n", candidates_.size());
for (const Candidate& candidate : candidates_) {
PrintF(" id:%d, calls:%d, size[source]:%d, size[ast]:%d / %s\n",
candidate.node->id(), candidate.calls,
candidate.function->shared()->SourceSize(),
candidate.function->shared()->ast_node_count(),
candidate.function->shared()->DebugName()->ToCString().get());
}
}
} // namespace compiler

View File

@ -13,18 +13,37 @@ namespace compiler {
class JSInliningHeuristic final : public AdvancedReducer {
public:
enum Mode { kRestrictedInlining, kGeneralInlining };
enum Mode { kGeneralInlining, kRestrictedInlining, kStressInlining };
JSInliningHeuristic(Editor* editor, Mode mode, Zone* local_zone,
CompilationInfo* info, JSGraph* jsgraph)
: AdvancedReducer(editor),
mode_(mode),
inliner_(editor, local_zone, info, jsgraph) {}
local_zone_(local_zone),
jsgraph_(jsgraph),
inliner_(editor, local_zone, info, jsgraph),
candidates_(local_zone) {}
Reduction Reduce(Node* node) final;
// Processes the list of candidates gathered while the reducer was running,
// and inlines call sites that the heuristic determines to be important.
void ProcessCandidates();
private:
struct Candidate {
Handle<JSFunction> function; // The call target being inlined.
Node* node; // The call site at which to inline.
int calls; // Number of times the call site was hit.
};
static bool Compare(const Candidate& left, const Candidate& right);
void PrintCandidates();
Mode const mode_;
Zone* local_zone_;
JSGraph* jsgraph_;
JSInliner inliner_;
ZoneVector<Candidate> candidates_;
};
} // namespace compiler

View File

@ -542,6 +542,7 @@ struct InliningPhase {
AddReducer(data, &graph_reducer, &context_specialization);
AddReducer(data, &graph_reducer, &inlining);
graph_reducer.ReduceGraph();
inlining.ProcessCandidates();
}
};