Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "src/compiler/ast-graph-builder.h" | |
| 6 #include "src/compiler/common-operator.h" | |
| 7 #include "src/compiler/generic-node-inl.h" | |
| 8 #include "src/compiler/graph-inl.h" | |
| 9 #include "src/compiler/graph-visualizer.h" | |
| 10 #include "src/compiler/js-inlining.h" | |
| 11 #include "src/compiler/js-operator.h" | |
| 12 #include "src/compiler/node-aux-data-inl.h" | |
| 13 #include "src/compiler/node-matchers.h" | |
| 14 #include "src/compiler/node-properties-inl.h" | |
| 15 #include "src/compiler/simplified-operator.h" | |
| 16 #include "src/compiler/typer.h" | |
| 17 #include "src/parser.h" | |
| 18 #include "src/rewriter.h" | |
| 19 #include "src/scopes.h" | |
| 20 | |
| 21 | |
| 22 namespace v8 { | |
| 23 namespace internal { | |
| 24 namespace compiler { | |
| 25 | |
| 26 // TODO(titzer): factor this out to a common routine with js-typed-lowering. | |
| 27 static void ReplaceEffectfulWithValue(Node* node, Node* value) { | |
|
titzer
2014/08/14 10:24:31
Can you find a place for this routine? I think thi
sigurds
2014/08/19 11:31:19
Done.
| |
| 28 Node* effect = NULL; | |
| 29 if (OperatorProperties::HasEffectInput(node->op())) { | |
| 30 effect = NodeProperties::GetEffectInput(node); | |
| 31 } | |
| 32 | |
| 33 // Requires distinguishing between value and effect edges. | |
| 34 UseIter iter = node->uses().begin(); | |
| 35 while (iter != node->uses().end()) { | |
| 36 if (NodeProperties::IsEffectEdge(iter.edge())) { | |
| 37 DCHECK_NE(NULL, effect); | |
| 38 iter = iter.UpdateToAndIncrement(effect); | |
| 39 } else { | |
| 40 iter = iter.UpdateToAndIncrement(value); | |
| 41 } | |
| 42 } | |
| 43 } | |
| 44 | |
| 45 | |
| 46 class InlinerVisitor : public NullNodeVisitor { | |
| 47 public: | |
| 48 explicit InlinerVisitor(JSInliner* spec) : spec_(spec) {} | |
|
Michael Starzinger
2014/08/14 10:36:22
nit: s/spec/inliner/
sigurds
2014/08/19 11:31:19
Done.
| |
| 49 | |
| 50 GenericGraphVisit::Control Post(Node* node) { | |
| 51 switch (node->opcode()) { | |
| 52 case IrOpcode::kJSCallFunction: | |
| 53 spec_->TryInlineCall(node); | |
| 54 break; | |
| 55 default: | |
| 56 break; | |
| 57 } | |
| 58 return GenericGraphVisit::CONTINUE; | |
| 59 } | |
| 60 | |
| 61 private: | |
| 62 JSInliner* spec_; | |
|
Michael Starzinger
2014/08/14 10:36:22
nit: s/spec_/inliner_/
sigurds
2014/08/19 11:31:19
Done.
| |
| 63 }; | |
| 64 | |
| 65 | |
| 66 void JSInliner::Inline() { | |
| 67 if (!info_->closure()->PassesFilter(FLAG_turbo_inlining_filter)) { | |
| 68 return; | |
| 69 } | |
| 70 InlinerVisitor visitor(this); | |
| 71 jsgraph_->graph()->VisitNodeInputsFromEnd(&visitor); | |
| 72 } | |
| 73 | |
| 74 | |
| 75 static void MoveWithDependencies(Graph* graph, Node* node, Node* old_block, | |
| 76 Node* new_block) { | |
| 77 if (OperatorProperties::HasControlInput(node->op())) { | |
| 78 // Check if we have left the old_block. | |
| 79 if (NodeProperties::GetControlInput(node) != old_block) return; | |
| 80 // If not, move this node to the new_block. | |
| 81 NodeProperties::ReplaceControlInput(node, new_block); | |
| 82 } | |
| 83 // Independent of whether a node has a control input or not, | |
| 84 // it might have a dependency that is pinned to old_block. | |
| 85 for (InputIter iter = node->inputs().begin(); iter != node->inputs().end(); | |
| 86 ++iter) { | |
| 87 if (NodeProperties::IsControlEdge(iter.edge())) continue; | |
| 88 MoveWithDependencies(graph, *iter, old_block, new_block); | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 | |
| 93 static void MoveAllControlNodes(Node* from, Node* to) { | |
| 94 for (UseIter iter = from->uses().begin(); iter != from->uses().end();) { | |
| 95 if (NodeProperties::IsControlEdge(iter.edge())) { | |
| 96 iter.UpdateToAndIncrement(to); | |
| 97 } else { | |
| 98 ++iter; | |
| 99 } | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 | |
| 104 // TODO(sigurds) Find a home for this function and reuse it everywhere (esp. in | |
| 105 // test cases, where similar code is currently duplicated). | |
| 106 static void Parse(Handle<JSFunction> function, CompilationInfoWithZone* info) { | |
| 107 CHECK(Parser::Parse(info)); | |
| 108 StrictMode strict_mode = info->function()->strict_mode(); | |
| 109 info->SetStrictMode(strict_mode); | |
| 110 info->SetOptimizing(BailoutId::None(), Handle<Code>(function->code())); | |
| 111 CHECK(Rewriter::Rewrite(info)); | |
| 112 CHECK(Scope::Analyze(info)); | |
| 113 CHECK_NE(NULL, info->scope()); | |
| 114 Handle<ScopeInfo> scope_info = ScopeInfo::Create(info->scope(), info->zone()); | |
| 115 info->shared_info()->set_scope_info(*scope_info); | |
| 116 } | |
| 117 | |
| 118 | |
| 119 // A facade on a JSFunction's graph to facilitate inlining. It assumes the | |
| 120 // that the function graph has only one return statement, and provides | |
| 121 // {UnifyReturn} to convert a function graph to that end. | |
| 122 // InlineAtCall will create some new nodes using {graph}'s builders (and hence | |
| 123 // those nodes will live in {graph}'s zone. | |
| 124 class Inlinee { | |
| 125 public: | |
| 126 explicit Inlinee(JSGraph* graph) : jsgraph_(graph) {} | |
| 127 | |
| 128 Graph* graph() { return jsgraph_->graph(); } | |
| 129 JSGraph* jsgraph() { return jsgraph_; } | |
| 130 | |
| 131 // Returns the last regular control node, that is | |
| 132 // the last control node before the end node. | |
| 133 Node* end_block() { return NodeProperties::GetControlInput(unique_return()); } | |
| 134 | |
| 135 // Return the effect output of the graph, | |
| 136 // that is the effect input of the return statement of the inlinee. | |
| 137 Node* effect_output() { | |
| 138 return NodeProperties::GetEffectInput(unique_return()); | |
| 139 } | |
| 140 // Return the value output of the graph, | |
| 141 // that is the value input of the return statement of the inlinee. | |
| 142 Node* value_output() { | |
| 143 return NodeProperties::GetValueInput(unique_return(), 0); | |
| 144 } | |
| 145 // Return the unique return statement of the graph. | |
| 146 Node* unique_return() { | |
| 147 Node* unique_return = | |
| 148 NodeProperties::GetControlInput(jsgraph_->graph()->end()); | |
| 149 DCHECK_EQ(IrOpcode::kReturn, unique_return->opcode()); | |
| 150 return unique_return; | |
| 151 } | |
| 152 // Inline this graph at {call}, use {jsgraph} and its zone to create | |
| 153 // any new nodes. | |
| 154 void InlineAtCall(JSGraph* jsgraph, Node* call); | |
| 155 // Ensure that only a single return reaches the end node. | |
| 156 void UnifyReturn(); | |
| 157 | |
| 158 private: | |
| 159 JSGraph* jsgraph_; | |
| 160 }; | |
| 161 | |
| 162 | |
| 163 void Inlinee::UnifyReturn() { | |
| 164 Graph* graph = jsgraph_->graph(); | |
| 165 | |
| 166 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0); | |
| 167 if (final_merge->opcode() == IrOpcode::kReturn) { | |
| 168 // nothing to do | |
| 169 return; | |
| 170 } | |
| 171 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode()); | |
| 172 | |
| 173 int predecessors = | |
| 174 OperatorProperties::GetControlInputCount(final_merge->op()); | |
| 175 Operator* op_phi = jsgraph_->common()->Phi(predecessors); | |
| 176 Operator* op_ephi = jsgraph_->common()->EffectPhi(predecessors); | |
| 177 | |
| 178 std::vector<Node*> values; | |
| 179 std::vector<Node*> effects; | |
| 180 // Iterate over all control flow predecessors, | |
| 181 // which must be return statements. | |
| 182 InputIter iter = final_merge->inputs().begin(); | |
| 183 while (iter != final_merge->inputs().end()) { | |
| 184 Node* input = *iter; | |
| 185 switch (input->opcode()) { | |
| 186 case IrOpcode::kReturn: | |
| 187 values.push_back(NodeProperties::GetValueInput(input, 0)); | |
| 188 effects.push_back(NodeProperties::GetEffectInput(input)); | |
| 189 iter.UpdateToAndIncrement(NodeProperties::GetControlInput(input)); | |
| 190 input->RemoveAllInputs(); | |
| 191 break; | |
| 192 default: | |
| 193 UNREACHABLE(); | |
| 194 ++iter; | |
| 195 break; | |
| 196 } | |
| 197 } | |
| 198 values.push_back(final_merge); | |
| 199 effects.push_back(final_merge); | |
| 200 Node* phi = graph->NewNode(op_phi, values.size(), values.data()); | |
| 201 Node* ephi = graph->NewNode(op_ephi, effects.size(), effects.data()); | |
| 202 Node* new_return = | |
| 203 graph->NewNode(jsgraph_->common()->Return(), phi, ephi, final_merge); | |
| 204 graph->end()->ReplaceInput(0, new_return); | |
| 205 } | |
| 206 | |
| 207 | |
| 208 void Inlinee::InlineAtCall(JSGraph* jsgraph, Node* call) { | |
| 209 MachineOperatorBuilder machine(jsgraph->zone()); | |
| 210 | |
| 211 Node* control = NodeProperties::GetControlInput(call); | |
| 212 // Move all the nodes to the end block. | |
| 213 MoveAllControlNodes(control, end_block()); | |
| 214 // Now move the ones the call depends on back up. | |
| 215 // We have to do this back-and-forth to treat the case where the call is | |
| 216 // pinned to the start block. | |
| 217 MoveWithDependencies(graph(), call, end_block(), control); | |
| 218 | |
| 219 // The inlinee uses the context from the JSFunction object. This will | |
| 220 // also be the effect dependency for the inlinee as it produces an effect. | |
| 221 Node* context = jsgraph->graph()->NewNode( | |
| 222 machine.Load(kMachineTagged), NodeProperties::GetValueInput(call, 0), | |
| 223 jsgraph->Int32Constant(JSFunction::kContextOffset - kHeapObjectTag), | |
| 224 NodeProperties::GetEffectInput(call)); | |
| 225 | |
| 226 // {inlinee_inputs} counts JSFunction, Receiver, arguments, context, | |
| 227 // but not effect, control. | |
| 228 int inlinee_inputs = graph()->start()->op()->OutputCount(); | |
| 229 // Context is last argument. | |
| 230 int inlinee_context_index = inlinee_inputs - 1; | |
| 231 // {inliner_inputs} counts JSFunction, Receiver, arguments, but not | |
| 232 // context, effect, control. | |
| 233 int inliner_inputs = OperatorProperties::GetValueInputCount(call->op()); | |
| 234 // Iterate over all uses of the start node. | |
| 235 UseIter iter = graph()->start()->uses().begin(); | |
| 236 while (iter != graph()->start()->uses().end()) { | |
| 237 Node* use = *iter; | |
| 238 switch (use->opcode()) { | |
| 239 case IrOpcode::kParameter: { | |
| 240 int index = 1 + static_cast<Operator1<int>*>(use->op())->parameter(); | |
| 241 if (index < inliner_inputs && index < inlinee_context_index) { | |
| 242 // There is an input from the call, and the index is a value | |
| 243 // projection but not the context, so rewire the input. | |
| 244 ReplaceEffectfulWithValue(*iter, call->InputAt(index)); | |
| 245 } else if (index == inlinee_context_index) { | |
| 246 // This is the context projection, rewire it to the context from the | |
| 247 // JSFunction object. | |
| 248 ReplaceEffectfulWithValue(*iter, context); | |
| 249 } else if (index < inlinee_context_index) { | |
| 250 // Call has fewer arguments than required, fill with undefined. | |
| 251 ReplaceEffectfulWithValue(*iter, jsgraph->UndefinedConstant()); | |
| 252 } else { | |
| 253 // We got too many arguments, discard for now. | |
| 254 // TODO(sigurds): Fix to treat arguments array correctly. | |
| 255 } | |
| 256 ++iter; | |
| 257 break; | |
| 258 } | |
| 259 default: | |
| 260 if (NodeProperties::IsEffectEdge(iter.edge())) { | |
| 261 iter.UpdateToAndIncrement(context); | |
| 262 } else if (NodeProperties::IsControlEdge(iter.edge())) { | |
| 263 iter.UpdateToAndIncrement(control); | |
| 264 } else { | |
| 265 UNREACHABLE(); | |
| 266 } | |
| 267 break; | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 // Iterate over all uses of the call node. | |
| 272 iter = call->uses().begin(); | |
| 273 while (iter != call->uses().end()) { | |
| 274 if (NodeProperties::IsEffectEdge(iter.edge())) { | |
| 275 iter.UpdateToAndIncrement(effect_output()); | |
| 276 } else if (NodeProperties::IsControlEdge(iter.edge())) { | |
| 277 UNREACHABLE(); | |
| 278 } else { | |
| 279 DCHECK(NodeProperties::IsValueEdge(iter.edge())); | |
| 280 iter.UpdateToAndIncrement(value_output()); | |
| 281 } | |
| 282 } | |
| 283 call->RemoveAllInputs(); | |
| 284 DCHECK_EQ(0, call->UseCount()); | |
| 285 // TODO(sigurds) Remove this once we copy. | |
| 286 unique_return()->RemoveAllInputs(); | |
| 287 } | |
| 288 | |
| 289 | |
| 290 void JSInliner::TryInlineCall(Node* node) { | |
| 291 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); | |
| 292 | |
| 293 ValueMatcher<Handle<JSFunction> > match(node->InputAt(0)); | |
| 294 if (!match.HasValue()) { | |
| 295 return; | |
| 296 } | |
| 297 | |
| 298 Handle<JSFunction> function = match.Value(); | |
| 299 | |
| 300 if (function->shared()->native()) { | |
| 301 if (FLAG_trace_turbo_inlining) { | |
| 302 SmartArrayPointer<char> name = | |
| 303 function->shared()->DebugName()->ToCString(); | |
| 304 PrintF("Not Inlining %s into %s because inlinee is native\n", name.get(), | |
| 305 info_->shared_info()->DebugName()->ToCString().get()); | |
| 306 } | |
| 307 return; | |
| 308 } | |
| 309 | |
| 310 CompilationInfoWithZone info(function); | |
| 311 Parse(function, &info); | |
| 312 | |
| 313 if (info.scope()->arguments() != NULL) { | |
| 314 // For now do not inline functions that use their arguments array. | |
| 315 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); | |
| 316 if (FLAG_trace_turbo_inlining) { | |
| 317 PrintF( | |
| 318 "Not Inlining %s into %s because inlinee uses arguments " | |
| 319 "array\n", | |
| 320 name.get(), info_->shared_info()->DebugName()->ToCString().get()); | |
| 321 } | |
| 322 return; | |
| 323 } | |
| 324 | |
| 325 if (FLAG_trace_turbo_inlining) { | |
| 326 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); | |
| 327 PrintF("Inlining %s into %s\n", name.get(), | |
| 328 info_->shared_info()->DebugName()->ToCString().get()); | |
| 329 } | |
| 330 | |
| 331 Graph graph(info_->zone()); | |
| 332 graph.SetNextNodeId(jsgraph_->graph()->NextNodeID()); | |
| 333 | |
| 334 Typer typer(info_->zone()); | |
| 335 CommonOperatorBuilder common(info_->zone()); | |
| 336 JSGraph jsgraph(&graph, &common, &typer); | |
| 337 | |
| 338 AstGraphBuilder graph_builder(&info, &jsgraph); | |
| 339 graph_builder.CreateGraph(); | |
| 340 | |
| 341 Inlinee inlinee(&jsgraph); | |
| 342 inlinee.UnifyReturn(); | |
| 343 inlinee.InlineAtCall(jsgraph_, node); | |
| 344 | |
| 345 jsgraph_->graph()->SetNextNodeId(inlinee.graph()->NextNodeID()); | |
| 346 } | |
| 347 } | |
| 348 } | |
| 349 } // namespace v8::internal::compiler | |
| OLD | NEW |