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