Index: runtime/vm/flow_graph_inliner.cc |
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc |
index 134184ef147bfcff5591de2a7ed0ffd992aa99f1..a4158007b89f5b78d5b6a18f0d7d807685e9636e 100644 |
--- a/runtime/vm/flow_graph_inliner.cc |
+++ b/runtime/vm/flow_graph_inliner.cc |
@@ -47,6 +47,54 @@ static bool IsCallRecursive(const Function& function, Definition* call) { |
} |
+// TODO(zerny): Remove the following classes once we have moved the label/join |
+// map for control flow out of the AST an into the flow graph builder. |
+ |
+// Default visitor to traverse child nodes. |
+class ChildrenVisitor : public AstNodeVisitor { |
+ public: |
+ ChildrenVisitor() { } |
+#define DEFINE_VISIT(type, name) \ |
+ virtual void Visit##type(type* node) { node->VisitChildren(this); } |
+ NODE_LIST(DEFINE_VISIT); |
+#undef DEFINE_VISIT |
+}; |
+ |
+ |
+// Visitor to clear each AST node containing source labels. |
+class SourceLabelResetter : public ChildrenVisitor { |
+ public: |
+ SourceLabelResetter() { } |
+ virtual void VisitSequenceNode(SequenceNode* node) { |
+ Reset(node, node->label()); |
+ } |
+ virtual void VisitCaseNode(CaseNode* node) { |
+ Reset(node, node->label()); |
+ } |
+ virtual void VisitSwitchNode(SwitchNode* node) { |
+ Reset(node, node->label()); |
+ } |
+ virtual void VisitWhileNode(WhileNode* node) { |
+ Reset(node, node->label()); |
+ } |
+ virtual void VisitDoWhileNode(DoWhileNode* node) { |
+ Reset(node, node->label()); |
+ } |
+ virtual void VisitForNode(ForNode* node) { |
+ Reset(node, node->label()); |
+ } |
+ virtual void VisitJumpNode(JumpNode* node) { |
+ Reset(node, node->label()); |
+ } |
+ void Reset(AstNode* node, SourceLabel* lbl) { |
+ node->VisitChildren(this); |
+ if (lbl == NULL) return; |
+ lbl->join_for_break_ = NULL; |
+ lbl->join_for_continue_ = NULL; |
+ } |
+}; |
+ |
+ |
// A collection of call sites to consider for inlining. |
class CallSites : public FlowGraphVisitor { |
public: |
@@ -123,7 +171,8 @@ class CallSiteInliner : public ValueObject { |
inlined_size_(0), |
inlining_depth_(1), |
collected_call_sites_(NULL), |
- inlining_call_sites_(NULL) { } |
+ inlining_call_sites_(NULL), |
+ function_cache() { } |
void InlineCalls() { |
// If inlining depth is less then one abort. |
@@ -219,9 +268,8 @@ class CallSiteInliner : public ValueObject { |
isolate->set_long_jump_base(&jump); |
if (setjmp(*jump.Set()) == 0) { |
// Parse the callee function. |
- ParsedFunction parsed_function(function); |
- Parser::ParseFunction(&parsed_function); |
- parsed_function.AllocateVariables(); |
+ bool in_cache; |
+ ParsedFunction* parsed_function = ParseFunction(function, &in_cache); |
// Load IC data for the callee. |
if (function.HasCode()) { |
@@ -231,7 +279,7 @@ class CallSiteInliner : public ValueObject { |
} |
// Build the callee graph. |
- FlowGraphBuilder builder(parsed_function); |
+ FlowGraphBuilder builder(*parsed_function); |
builder.SetInitialBlockId(caller_graph_->max_block_id()); |
FlowGraph* callee_graph = |
builder.BuildGraph(FlowGraphBuilder::kValueContext); |
@@ -257,7 +305,7 @@ class CallSiteInliner : public ValueObject { |
if (FLAG_trace_inlining && FLAG_print_flow_graph) { |
OS::Print("Callee graph for inlining %s\n", |
- parsed_function.function().ToFullyQualifiedCString()); |
+ function.ToFullyQualifiedCString()); |
FlowGraphPrinter printer(*callee_graph); |
printer.PrintBlocks(); |
} |
@@ -308,6 +356,9 @@ class CallSiteInliner : public ValueObject { |
TRACE_INLINING(OS::Print(" Success\n")); |
+ // Add the function to the cache. |
+ if (!in_cache) function_cache.Add(parsed_function); |
+ |
// Check that inlining maintains use lists. |
DEBUG_ASSERT(!FLAG_verify_compiler || caller_graph_->ValidateUseLists()); |
@@ -330,6 +381,26 @@ class CallSiteInliner : public ValueObject { |
} |
} |
+ // Parse a function reusing the cache if possible. Returns true if the |
+ // function was in the cache. |
+ ParsedFunction* ParseFunction(const Function& function, bool* in_cache) { |
+ // TODO(zerny): Use a hash map for the cache. |
+ for (intptr_t i = 0; i < function_cache.length(); ++i) { |
+ ParsedFunction* parsed_function = function_cache[i]; |
+ if (parsed_function->function().raw() == function.raw()) { |
+ *in_cache = true; |
+ SourceLabelResetter reset; |
+ parsed_function->node_sequence()->Visit(&reset); |
+ return parsed_function; |
+ } |
+ } |
+ *in_cache = false; |
+ ParsedFunction* parsed_function = new ParsedFunction(function); |
+ Parser::ParseFunction(parsed_function); |
+ parsed_function->AllocateVariables(); |
+ return parsed_function; |
+ } |
+ |
void InlineStaticCalls() { |
const GrowableArray<StaticCallInstr*>& calls = |
*inlining_call_sites_->static_calls(); |
@@ -376,9 +447,11 @@ class CallSiteInliner : public ValueObject { |
const ICData& ic_data = instr->ic_data(); |
const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); |
if (instr->with_checks()) { |
- TRACE_INLINING(OS::Print(" Bailout: %"Pd" checks target '%s'\n", |
- ic_data.NumberOfChecks(), |
- target.ToCString())); |
+ TRACE_INLINING(OS::Print( |
+ " => %s (deopt count %d)\n Bailout: %"Pd" checks\n", |
+ target.ToCString(), |
+ target.deoptimization_counter(), |
+ ic_data.NumberOfChecks())); |
continue; |
} |
GrowableArray<Value*> arguments(instr->ArgumentCount()); |
@@ -397,6 +470,7 @@ class CallSiteInliner : public ValueObject { |
intptr_t inlining_depth_; |
CallSites* collected_call_sites_; |
CallSites* inlining_call_sites_; |
+ GrowableArray<ParsedFunction*> function_cache; |
DISALLOW_COPY_AND_ASSIGN(CallSiteInliner); |
}; |