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