OLD | NEW |
1 // Copyright 2014 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include "src/compiler/js-inlining.h" |
| 6 |
5 #include "src/ast.h" | 7 #include "src/ast.h" |
6 #include "src/ast-numbering.h" | 8 #include "src/ast-numbering.h" |
7 #include "src/compiler/access-builder.h" | 9 #include "src/compiler/access-builder.h" |
8 #include "src/compiler/ast-graph-builder.h" | 10 #include "src/compiler/ast-graph-builder.h" |
9 #include "src/compiler/common-operator.h" | 11 #include "src/compiler/common-operator.h" |
10 #include "src/compiler/graph-inl.h" | 12 #include "src/compiler/graph-inl.h" |
11 #include "src/compiler/graph-visualizer.h" | 13 #include "src/compiler/js-graph.h" |
12 #include "src/compiler/js-inlining.h" | |
13 #include "src/compiler/js-intrinsic-builder.h" | 14 #include "src/compiler/js-intrinsic-builder.h" |
14 #include "src/compiler/js-operator.h" | 15 #include "src/compiler/js-operator.h" |
15 #include "src/compiler/node-aux-data-inl.h" | |
16 #include "src/compiler/node-matchers.h" | 16 #include "src/compiler/node-matchers.h" |
17 #include "src/compiler/node-properties-inl.h" | 17 #include "src/compiler/node-properties-inl.h" |
18 #include "src/compiler/simplified-operator.h" | 18 #include "src/compiler/simplified-operator.h" |
19 #include "src/compiler/typer.h" | |
20 #include "src/full-codegen.h" | |
21 #include "src/parser.h" | 19 #include "src/parser.h" |
22 #include "src/rewriter.h" | 20 #include "src/rewriter.h" |
23 #include "src/scopes.h" | 21 #include "src/scopes.h" |
24 | 22 |
25 | 23 |
26 namespace v8 { | 24 namespace v8 { |
27 namespace internal { | 25 namespace internal { |
28 namespace compiler { | 26 namespace compiler { |
29 | 27 |
30 class InlinerVisitor : public NullNodeVisitor { | 28 class InlinerVisitor : public NullNodeVisitor { |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 | 98 |
101 // Ensure that only a single return reaches the end node. | 99 // Ensure that only a single return reaches the end node. |
102 static void UnifyReturn(JSGraph* jsgraph); | 100 static void UnifyReturn(JSGraph* jsgraph); |
103 | 101 |
104 private: | 102 private: |
105 Node* start_; | 103 Node* start_; |
106 Node* end_; | 104 Node* end_; |
107 }; | 105 }; |
108 | 106 |
109 | 107 |
| 108 // static |
110 void Inlinee::UnifyReturn(JSGraph* jsgraph) { | 109 void Inlinee::UnifyReturn(JSGraph* jsgraph) { |
111 Graph* graph = jsgraph->graph(); | 110 Graph* graph = jsgraph->graph(); |
112 | 111 |
113 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0); | 112 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0); |
114 if (final_merge->opcode() == IrOpcode::kReturn) { | 113 if (final_merge->opcode() == IrOpcode::kReturn) { |
115 // nothing to do | 114 // nothing to do |
116 return; | 115 return; |
117 } | 116 } |
118 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode()); | 117 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode()); |
119 | 118 |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
267 } else if (NodeProperties::IsControlEdge(edge)) { | 266 } else if (NodeProperties::IsControlEdge(edge)) { |
268 edge.UpdateTo(control); | 267 edge.UpdateTo(control); |
269 } else { | 268 } else { |
270 UNREACHABLE(); | 269 UNREACHABLE(); |
271 } | 270 } |
272 break; | 271 break; |
273 } | 272 } |
274 } | 273 } |
275 | 274 |
276 NodeProperties::ReplaceWithValue(call, value_output(), effect_output()); | 275 NodeProperties::ReplaceWithValue(call, value_output(), effect_output()); |
277 call->RemoveAllInputs(); | 276 call->Kill(); |
278 DCHECK_EQ(0, call->UseCount()); | |
279 } | 277 } |
280 | 278 |
281 | 279 |
282 // TODO(turbofan) Provide such accessors for every node, possibly even | 280 template <typename InputIterator> |
283 // generate them. | 281 Node* JSInliner::CreateArgumentsAdaptorFrameState( |
284 class JSCallFunctionAccessor { | 282 const Handle<JSFunction>& function, InputIterator first, InputIterator last, |
285 public: | 283 Node* frame_state) { |
286 explicit JSCallFunctionAccessor(Node* call) : call_(call) { | 284 NodeVector parameters(zone()); |
287 DCHECK_EQ(IrOpcode::kJSCallFunction, call->opcode()); | 285 parameters.insert(parameters.begin(), first, last); |
288 } | 286 int const num_parameters = static_cast<int>(parameters.size()); |
289 | 287 Node* state_values0 = graph()->NewNode(common()->StateValues(0)); |
290 Node* jsfunction() { return call_->InputAt(0); } | 288 Node* state_values = graph()->NewNode(common()->StateValues(num_parameters), |
291 | 289 num_parameters, ¶meters.front()); |
292 Node* receiver() { return call_->InputAt(1); } | 290 return graph()->NewNode( |
293 | 291 common()->FrameState(FrameStateType::ARGUMENTS_ADAPTOR, BailoutId(-1), |
294 Node* formal_argument(size_t index) { | 292 OutputFrameStateCombine::Ignore(), function), |
295 DCHECK(index < formal_arguments()); | 293 state_values, state_values0, state_values0, |
296 return call_->InputAt(static_cast<int>(2 + index)); | 294 jsgraph()->UndefinedConstant(), frame_state); |
297 } | |
298 | |
299 size_t formal_arguments() { | |
300 // {value_inputs} includes jsfunction and receiver. | |
301 size_t value_inputs = call_->op()->ValueInputCount(); | |
302 DCHECK_GE(call_->InputCount(), 2); | |
303 return value_inputs - 2; | |
304 } | |
305 | |
306 Node* frame_state() { return NodeProperties::GetFrameStateInput(call_); } | |
307 | |
308 private: | |
309 Node* call_; | |
310 }; | |
311 | |
312 | |
313 void JSInliner::AddClosureToFrameState(Node* frame_state, | |
314 Handle<JSFunction> jsfunction) { | |
315 FrameStateCallInfo call_info = OpParameter<FrameStateCallInfo>(frame_state); | |
316 const Operator* op = jsgraph_->common()->FrameState( | |
317 FrameStateType::JS_FRAME, call_info.bailout_id(), | |
318 call_info.state_combine(), jsfunction); | |
319 frame_state->set_op(op); | |
320 } | 295 } |
321 | 296 |
322 | 297 |
323 Node* JSInliner::CreateArgumentsAdaptorFrameState(JSCallFunctionAccessor* call, | 298 void JSInliner::TryInlineJSCall(Node* node) { |
324 Handle<JSFunction> jsfunction, | 299 JSCallFunctionMatcher const m(node); |
325 Zone* temp_zone) { | 300 if (!m.function().HasValue()) return; |
326 const Operator* op = jsgraph_->common()->FrameState( | 301 Handle<JSFunction> const function = m.function().Value().handle(); |
327 FrameStateType::ARGUMENTS_ADAPTOR, BailoutId(-1), | |
328 OutputFrameStateCombine::Ignore(), jsfunction); | |
329 const Operator* op0 = jsgraph_->common()->StateValues(0); | |
330 Node* node0 = jsgraph_->graph()->NewNode(op0); | |
331 NodeVector params(temp_zone); | |
332 params.push_back(call->receiver()); | |
333 for (size_t argument = 0; argument != call->formal_arguments(); ++argument) { | |
334 params.push_back(call->formal_argument(argument)); | |
335 } | |
336 const Operator* op_param = | |
337 jsgraph_->common()->StateValues(static_cast<int>(params.size())); | |
338 Node* params_node = jsgraph_->graph()->NewNode( | |
339 op_param, static_cast<int>(params.size()), ¶ms.front()); | |
340 return jsgraph_->graph()->NewNode(op, params_node, node0, node0, | |
341 jsgraph_->UndefinedConstant(), | |
342 call->frame_state()); | |
343 } | |
344 | |
345 | |
346 void JSInliner::TryInlineJSCall(Node* call_node) { | |
347 JSCallFunctionAccessor call(call_node); | |
348 | |
349 HeapObjectMatcher<JSFunction> match(call.jsfunction()); | |
350 if (!match.HasValue()) { | |
351 return; | |
352 } | |
353 | |
354 Handle<JSFunction> function = match.Value().handle(); | |
355 | 302 |
356 if (function->shared()->native()) { | 303 if (function->shared()->native()) { |
357 if (FLAG_trace_turbo_inlining) { | 304 if (FLAG_trace_turbo_inlining) { |
358 SmartArrayPointer<char> name = | 305 SmartArrayPointer<char> name = |
359 function->shared()->DebugName()->ToCString(); | 306 function->shared()->DebugName()->ToCString(); |
360 PrintF("Not Inlining %s into %s because inlinee is native\n", name.get(), | 307 PrintF("Not Inlining %s into %s because inlinee is native\n", name.get(), |
361 info_->shared_info()->DebugName()->ToCString().get()); | 308 info_->shared_info()->DebugName()->ToCString().get()); |
362 } | 309 } |
363 return; | 310 return; |
364 } | 311 } |
365 | 312 |
366 CompilationInfoWithZone info(function); | 313 CompilationInfoWithZone info(function); |
367 // TODO(wingo): ParseAndAnalyze can fail due to stack overflow. | 314 // TODO(wingo): ParseAndAnalyze can fail due to stack overflow. |
368 CHECK(Compiler::ParseAndAnalyze(&info)); | 315 CHECK(Compiler::ParseAndAnalyze(&info)); |
369 CHECK(Compiler::EnsureDeoptimizationSupport(&info)); | 316 CHECK(Compiler::EnsureDeoptimizationSupport(&info)); |
370 | 317 |
371 if (info.scope()->arguments() != NULL && info.strict_mode() != STRICT) { | 318 if (info.scope()->arguments() && info.strict_mode() != STRICT) { |
372 // For now do not inline functions that use their arguments array. | 319 // For now do not inline functions that use their arguments array. |
373 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); | 320 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); |
374 if (FLAG_trace_turbo_inlining) { | 321 if (FLAG_trace_turbo_inlining) { |
375 PrintF( | 322 PrintF( |
376 "Not Inlining %s into %s because inlinee uses arguments " | 323 "Not Inlining %s into %s because inlinee uses arguments " |
377 "array\n", | 324 "array\n", |
378 name.get(), info_->shared_info()->DebugName()->ToCString().get()); | 325 name.get(), info_->shared_info()->DebugName()->ToCString().get()); |
379 } | 326 } |
380 return; | 327 return; |
381 } | 328 } |
382 | 329 |
383 if (FLAG_trace_turbo_inlining) { | 330 if (FLAG_trace_turbo_inlining) { |
384 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); | 331 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); |
385 PrintF("Inlining %s into %s\n", name.get(), | 332 PrintF("Inlining %s into %s\n", name.get(), |
386 info_->shared_info()->DebugName()->ToCString().get()); | 333 info_->shared_info()->DebugName()->ToCString().get()); |
387 } | 334 } |
388 | 335 |
389 Graph graph(info.zone()); | 336 Graph inlinee_graph(info.zone()); |
390 JSGraph jsgraph(&graph, jsgraph_->common(), jsgraph_->javascript(), | 337 JSGraph inlinee_jsgraph(&inlinee_graph, common(), javascript(), machine()); |
391 jsgraph_->machine()); | |
392 | 338 |
393 AstGraphBuilder graph_builder(local_zone_, &info, &jsgraph); | 339 AstGraphBuilder graph_builder(zone(), &info, &inlinee_jsgraph); |
394 graph_builder.CreateGraph(); | 340 graph_builder.CreateGraph(); |
395 Inlinee::UnifyReturn(&jsgraph); | 341 Inlinee::UnifyReturn(&inlinee_jsgraph); |
396 | 342 |
397 CopyVisitor visitor(&graph, jsgraph_->graph(), info.zone()); | 343 CopyVisitor visitor(&inlinee_graph, graph(), info.zone()); |
398 visitor.CopyGraph(); | 344 visitor.CopyGraph(); |
399 | 345 |
400 Inlinee inlinee(visitor.GetCopy(graph.start()), visitor.GetCopy(graph.end())); | 346 Inlinee inlinee(visitor.GetCopy(inlinee_graph.start()), |
| 347 visitor.GetCopy(inlinee_graph.end())); |
401 | 348 |
402 if (FLAG_turbo_deoptimization) { | 349 if (FLAG_turbo_deoptimization) { |
403 Node* outer_frame_state = call.frame_state(); | 350 Node* outer_frame_state = m.frame_state(); |
404 // Insert argument adaptor frame if required. | 351 // Insert argument adaptor frame if required. |
405 if (call.formal_arguments() != inlinee.formal_parameters()) { | 352 if (m.size() != inlinee.formal_parameters()) { |
406 outer_frame_state = | 353 outer_frame_state = CreateArgumentsAdaptorFrameState( |
407 CreateArgumentsAdaptorFrameState(&call, function, info.zone()); | 354 function, m.begin(), m.end(), outer_frame_state); |
408 } | 355 } |
409 | 356 |
410 for (NodeVectorConstIter it = visitor.copies().begin(); | 357 for (auto frame_state : visitor.copies()) { |
411 it != visitor.copies().end(); ++it) { | 358 if (frame_state && frame_state->opcode() == IrOpcode::kFrameState) { |
412 Node* node = *it; | 359 FrameStateCallInfo info = OpParameter<FrameStateCallInfo>(frame_state); |
413 if (node != NULL && node->opcode() == IrOpcode::kFrameState) { | 360 frame_state->set_op( |
414 AddClosureToFrameState(node, function); | 361 common()->FrameState(FrameStateType::JS_FRAME, info.bailout_id(), |
415 NodeProperties::ReplaceFrameStateInput(node, outer_frame_state); | 362 info.state_combine(), function)); |
| 363 NodeProperties::ReplaceFrameStateInput(frame_state, outer_frame_state); |
416 } | 364 } |
417 } | 365 } |
418 } | 366 } |
419 | 367 |
420 inlinee.InlineAtCall(jsgraph_, call_node); | 368 inlinee.InlineAtCall(jsgraph(), node); |
421 } | 369 } |
422 | 370 |
423 | 371 |
424 class JSCallRuntimeAccessor { | 372 void JSInliner::TryInlineRuntimeCall(Node* node) { |
425 public: | 373 JSCallRuntimeMatcher m(node); |
426 explicit JSCallRuntimeAccessor(Node* call) : call_(call) { | 374 const Runtime::Function* f = m.function(); |
427 DCHECK_EQ(IrOpcode::kJSCallRuntime, call->opcode()); | 375 if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) return; |
428 } | |
429 | 376 |
430 Node* formal_argument(size_t index) { | 377 JSIntrinsicBuilder intrinsic_builder(jsgraph()); |
431 DCHECK(index < formal_arguments()); | |
432 return call_->InputAt(static_cast<int>(index)); | |
433 } | |
434 | 378 |
435 size_t formal_arguments() { | 379 // TODO(turbofan): Looks like no one is verifying these arguments in the |
436 size_t value_inputs = call_->op()->ValueInputCount(); | 380 // IntrinsicsBuilder? Do we plan to do it there, or should be add code to |
437 return value_inputs; | 381 // validate the arguments here? |
438 } | 382 NodeVector arguments(zone()); |
439 | 383 arguments.insert(arguments.begin(), node->inputs().begin(), |
440 Node* frame_state() const { | 384 node->inputs().end()); |
441 return NodeProperties::GetFrameStateInput(call_); | 385 ResultAndEffect r = |
442 } | 386 intrinsic_builder.BuildGraphFor(f->function_id, arguments); |
443 Node* context() const { return NodeProperties::GetContextInput(call_); } | 387 if (r.first) { |
444 Node* control() const { return NodeProperties::GetControlInput(call_); } | |
445 Node* effect() const { return NodeProperties::GetEffectInput(call_); } | |
446 | |
447 const Runtime::Function* function() const { | |
448 return Runtime::FunctionForId(CallRuntimeParametersOf(call_->op()).id()); | |
449 } | |
450 | |
451 NodeVector inputs(Zone* zone) const { | |
452 NodeVector inputs(zone); | |
453 for (Node* const node : call_->inputs()) { | |
454 inputs.push_back(node); | |
455 } | |
456 return inputs; | |
457 } | |
458 | |
459 private: | |
460 Node* call_; | |
461 }; | |
462 | |
463 | |
464 void JSInliner::TryInlineRuntimeCall(Node* call_node) { | |
465 JSCallRuntimeAccessor call(call_node); | |
466 const Runtime::Function* f = call.function(); | |
467 | |
468 if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) { | |
469 return; | |
470 } | |
471 | |
472 JSIntrinsicBuilder intrinsic_builder(jsgraph_); | |
473 | |
474 ResultAndEffect r = intrinsic_builder.BuildGraphFor( | |
475 f->function_id, call.inputs(jsgraph_->zone())); | |
476 | |
477 if (r.first != NULL) { | |
478 if (FLAG_trace_turbo_inlining) { | 388 if (FLAG_trace_turbo_inlining) { |
479 PrintF("Inlining %s into %s\n", f->name, | 389 PrintF("Inlining %s into %s\n", f->name, |
480 info_->shared_info()->DebugName()->ToCString().get()); | 390 info_->shared_info()->DebugName()->ToCString().get()); |
481 } | 391 } |
482 NodeProperties::ReplaceWithValue(call_node, r.first, r.second); | 392 NodeProperties::ReplaceWithValue(node, r.first, r.second); |
483 call_node->RemoveAllInputs(); | 393 node->Kill(); |
484 DCHECK_EQ(0, call_node->UseCount()); | |
485 } | 394 } |
486 } | 395 } |
| 396 |
| 397 |
| 398 Graph* JSInliner::graph() const { return jsgraph()->graph(); } |
| 399 |
| 400 |
| 401 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } |
| 402 |
| 403 |
| 404 JSOperatorBuilder* JSInliner::javascript() const { |
| 405 return jsgraph()->javascript(); |
487 } | 406 } |
| 407 |
| 408 |
| 409 MachineOperatorBuilder* JSInliner::machine() const { |
| 410 return jsgraph()->machine(); |
488 } | 411 } |
489 } // namespace v8::internal::compiler | 412 |
| 413 } // namespace compiler |
| 414 } // namespace internal |
| 415 } // namespace v8 |
OLD | NEW |