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" | 5 #include "src/compiler/js-inlining.h" |
6 | 6 |
7 #include "src/ast/ast-numbering.h" | 7 #include "src/ast/ast-numbering.h" |
8 #include "src/ast/ast.h" | 8 #include "src/ast/ast.h" |
9 #include "src/ast/scopes.h" | 9 #include "src/ast/scopes.h" |
10 #include "src/compiler.h" | 10 #include "src/compiler.h" |
| 11 #include "src/compiler/all-nodes.h" |
11 #include "src/compiler/ast-graph-builder.h" | 12 #include "src/compiler/ast-graph-builder.h" |
12 #include "src/compiler/ast-loop-assignment-analyzer.h" | 13 #include "src/compiler/ast-loop-assignment-analyzer.h" |
13 #include "src/compiler/common-operator.h" | 14 #include "src/compiler/common-operator.h" |
14 #include "src/compiler/graph-reducer.h" | 15 #include "src/compiler/graph-reducer.h" |
15 #include "src/compiler/js-operator.h" | 16 #include "src/compiler/js-operator.h" |
16 #include "src/compiler/node-matchers.h" | 17 #include "src/compiler/node-matchers.h" |
17 #include "src/compiler/node-properties.h" | 18 #include "src/compiler/node-properties.h" |
18 #include "src/compiler/operator-properties.h" | 19 #include "src/compiler/operator-properties.h" |
19 #include "src/compiler/simplified-operator.h" | 20 #include "src/compiler/simplified-operator.h" |
20 #include "src/compiler/type-hint-analyzer.h" | 21 #include "src/compiler/type-hint-analyzer.h" |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
65 // Both, {JSCallFunction} and {JSCallConstruct}, have two extra inputs: | 66 // Both, {JSCallFunction} and {JSCallConstruct}, have two extra inputs: |
66 // - JSCallConstruct: Includes target function and new target. | 67 // - JSCallConstruct: Includes target function and new target. |
67 // - JSCallFunction: Includes target function and receiver. | 68 // - JSCallFunction: Includes target function and receiver. |
68 return call_->op()->ValueInputCount() - 2; | 69 return call_->op()->ValueInputCount() - 2; |
69 } | 70 } |
70 | 71 |
71 private: | 72 private: |
72 Node* call_; | 73 Node* call_; |
73 }; | 74 }; |
74 | 75 |
75 | |
76 Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context, | 76 Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context, |
77 Node* frame_state, Node* start, Node* end) { | 77 Node* frame_state, Node* start, Node* end, |
| 78 Node* exception_target, |
| 79 const NodeVector& uncaught_subcalls) { |
78 // The scheduler is smart enough to place our code; we just ensure {control} | 80 // The scheduler is smart enough to place our code; we just ensure {control} |
79 // becomes the control input of the start of the inlinee, and {effect} becomes | 81 // becomes the control input of the start of the inlinee, and {effect} becomes |
80 // the effect input of the start of the inlinee. | 82 // the effect input of the start of the inlinee. |
81 Node* control = NodeProperties::GetControlInput(call); | 83 Node* control = NodeProperties::GetControlInput(call); |
82 Node* effect = NodeProperties::GetEffectInput(call); | 84 Node* effect = NodeProperties::GetEffectInput(call); |
83 | 85 |
84 int const inlinee_new_target_index = | 86 int const inlinee_new_target_index = |
85 static_cast<int>(start->op()->ValueOutputCount()) - 3; | 87 static_cast<int>(start->op()->ValueOutputCount()) - 3; |
86 int const inlinee_arity_index = | 88 int const inlinee_arity_index = |
87 static_cast<int>(start->op()->ValueOutputCount()) - 2; | 89 static_cast<int>(start->op()->ValueOutputCount()) - 2; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 edge.UpdateTo(control); | 126 edge.UpdateTo(control); |
125 } else if (NodeProperties::IsFrameStateEdge(edge)) { | 127 } else if (NodeProperties::IsFrameStateEdge(edge)) { |
126 edge.UpdateTo(frame_state); | 128 edge.UpdateTo(frame_state); |
127 } else { | 129 } else { |
128 UNREACHABLE(); | 130 UNREACHABLE(); |
129 } | 131 } |
130 break; | 132 break; |
131 } | 133 } |
132 } | 134 } |
133 | 135 |
| 136 if (exception_target != nullptr) { |
| 137 // Link uncaught calls in the inlinee to {exception_target} |
| 138 int subcall_count = static_cast<int>(uncaught_subcalls.size()); |
| 139 if (subcall_count > 0) { |
| 140 TRACE( |
| 141 "Inlinee contains %d calls without IfException; " |
| 142 "linking to existing IfException\n", |
| 143 subcall_count); |
| 144 } |
| 145 NodeVector on_exception_nodes(local_zone_); |
| 146 for (Node* subcall : uncaught_subcalls) { |
| 147 Node* on_exception = |
| 148 graph()->NewNode(common()->IfException(), subcall, subcall); |
| 149 on_exception_nodes.push_back(on_exception); |
| 150 } |
| 151 |
| 152 DCHECK_EQ(subcall_count, static_cast<int>(on_exception_nodes.size())); |
| 153 if (subcall_count > 0) { |
| 154 Node* control_output = |
| 155 graph()->NewNode(common()->Merge(subcall_count), subcall_count, |
| 156 &on_exception_nodes.front()); |
| 157 NodeVector values_effects(local_zone_); |
| 158 values_effects = on_exception_nodes; |
| 159 values_effects.push_back(control_output); |
| 160 Node* value_output = graph()->NewNode( |
| 161 common()->Phi(MachineRepresentation::kTagged, subcall_count), |
| 162 subcall_count + 1, &values_effects.front()); |
| 163 Node* effect_output = |
| 164 graph()->NewNode(common()->EffectPhi(subcall_count), |
| 165 subcall_count + 1, &values_effects.front()); |
| 166 ReplaceWithValue(exception_target, value_output, effect_output, |
| 167 control_output); |
| 168 } else { |
| 169 ReplaceWithValue(exception_target, exception_target, exception_target, |
| 170 jsgraph()->Dead()); |
| 171 } |
| 172 } |
| 173 |
134 NodeVector values(local_zone_); | 174 NodeVector values(local_zone_); |
135 NodeVector effects(local_zone_); | 175 NodeVector effects(local_zone_); |
136 NodeVector controls(local_zone_); | 176 NodeVector controls(local_zone_); |
137 for (Node* const input : end->inputs()) { | 177 for (Node* const input : end->inputs()) { |
138 switch (input->opcode()) { | 178 switch (input->opcode()) { |
139 case IrOpcode::kReturn: | 179 case IrOpcode::kReturn: |
140 values.push_back(NodeProperties::GetValueInput(input, 0)); | 180 values.push_back(NodeProperties::GetValueInput(input, 0)); |
141 effects.push_back(NodeProperties::GetEffectInput(input)); | 181 effects.push_back(NodeProperties::GetEffectInput(input)); |
142 controls.push_back(NodeProperties::GetControlInput(input)); | 182 controls.push_back(NodeProperties::GetControlInput(input)); |
143 break; | 183 break; |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 // calls whenever the target is a constant function object, as follows: | 303 // calls whenever the target is a constant function object, as follows: |
264 // - JSCallFunction(target:constant, receiver, args...) | 304 // - JSCallFunction(target:constant, receiver, args...) |
265 // - JSCallConstruct(target:constant, args..., new.target) | 305 // - JSCallConstruct(target:constant, args..., new.target) |
266 HeapObjectMatcher match(node->InputAt(0)); | 306 HeapObjectMatcher match(node->InputAt(0)); |
267 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); | 307 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); |
268 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); | 308 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); |
269 | 309 |
270 return ReduceJSCall(node, function); | 310 return ReduceJSCall(node, function); |
271 } | 311 } |
272 | 312 |
273 | |
274 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { | 313 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { |
275 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); | 314 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); |
276 JSCallAccessor call(node); | 315 JSCallAccessor call(node); |
277 Handle<SharedFunctionInfo> shared_info(function->shared()); | 316 Handle<SharedFunctionInfo> shared_info(function->shared()); |
278 | 317 |
279 // Function must be inlineable. | 318 // Function must be inlineable. |
280 if (!shared_info->IsInlineable()) { | 319 if (!shared_info->IsInlineable()) { |
281 TRACE("Not inlining %s into %s because callee is not inlineable\n", | 320 TRACE("Not inlining %s into %s because callee is not inlineable\n", |
282 shared_info->DebugName()->ToCString().get(), | 321 shared_info->DebugName()->ToCString().get(), |
283 info_->shared_info()->DebugName()->ToCString().get()); | 322 info_->shared_info()->DebugName()->ToCString().get()); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 Handle<SharedFunctionInfo> frame_shared_info; | 376 Handle<SharedFunctionInfo> frame_shared_info; |
338 if (frame_info.shared_info().ToHandle(&frame_shared_info) && | 377 if (frame_info.shared_info().ToHandle(&frame_shared_info) && |
339 *frame_shared_info == *shared_info) { | 378 *frame_shared_info == *shared_info) { |
340 TRACE("Not inlining %s into %s because call is recursive\n", | 379 TRACE("Not inlining %s into %s because call is recursive\n", |
341 shared_info->DebugName()->ToCString().get(), | 380 shared_info->DebugName()->ToCString().get(), |
342 info_->shared_info()->DebugName()->ToCString().get()); | 381 info_->shared_info()->DebugName()->ToCString().get()); |
343 return NoChange(); | 382 return NoChange(); |
344 } | 383 } |
345 } | 384 } |
346 | 385 |
347 // TODO(turbofan): Inlining into a try-block is not yet supported. | 386 // Find the IfException node, if any. |
348 if (NodeProperties::IsExceptionalCall(node)) { | 387 Node* exception_target = nullptr; |
349 TRACE("Not inlining %s into %s because of surrounding try-block\n", | 388 for (Edge edge : node->use_edges()) { |
| 389 if (NodeProperties::IsControlEdge(edge) && |
| 390 edge.from()->opcode() == IrOpcode::kIfException) { |
| 391 DCHECK_NULL(exception_target); |
| 392 exception_target = edge.from(); |
| 393 } |
| 394 } |
| 395 |
| 396 NodeVector uncaught_subcalls(local_zone_); |
| 397 |
| 398 if (exception_target != nullptr) { |
| 399 if (!FLAG_inline_into_try) { |
| 400 TRACE( |
| 401 "Try block surrounds #%d:%s and --no-inline-into-try active, so not " |
| 402 "inlining %s into %s.\n", |
| 403 exception_target->id(), exception_target->op()->mnemonic(), |
350 shared_info->DebugName()->ToCString().get(), | 404 shared_info->DebugName()->ToCString().get(), |
351 info_->shared_info()->DebugName()->ToCString().get()); | 405 info_->shared_info()->DebugName()->ToCString().get()); |
352 return NoChange(); | 406 return NoChange(); |
| 407 } else { |
| 408 TRACE( |
| 409 "Inlining %s into %s regardless of surrounding try-block to catcher " |
| 410 "#%d:%s\n", |
| 411 shared_info->DebugName()->ToCString().get(), |
| 412 info_->shared_info()->DebugName()->ToCString().get(), |
| 413 exception_target->id(), exception_target->op()->mnemonic()); |
| 414 } |
353 } | 415 } |
354 | 416 |
355 Zone zone(info_->isolate()->allocator()); | 417 Zone zone(info_->isolate()->allocator()); |
356 ParseInfo parse_info(&zone, function); | 418 ParseInfo parse_info(&zone, function); |
357 CompilationInfo info(&parse_info, function); | 419 CompilationInfo info(&parse_info, function); |
358 if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled(); | 420 if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled(); |
359 if (info_->is_type_feedback_enabled()) info.MarkAsTypeFeedbackEnabled(); | 421 if (info_->is_type_feedback_enabled()) info.MarkAsTypeFeedbackEnabled(); |
360 | 422 |
361 if (!Compiler::ParseAndAnalyze(info.parse_info())) { | 423 if (!Compiler::ParseAndAnalyze(info.parse_info())) { |
362 TRACE("Not inlining %s into %s because parsing failed\n", | 424 TRACE("Not inlining %s into %s because parsing failed\n", |
(...skipping 18 matching lines...) Expand all Loading... |
381 info_->AddInlinedFunction(shared_info); | 443 info_->AddInlinedFunction(shared_info); |
382 | 444 |
383 // ---------------------------------------------------------------- | 445 // ---------------------------------------------------------------- |
384 // After this point, we've made a decision to inline this function. | 446 // After this point, we've made a decision to inline this function. |
385 // We shall not bailout from inlining if we got here. | 447 // We shall not bailout from inlining if we got here. |
386 | 448 |
387 TRACE("Inlining %s into %s\n", | 449 TRACE("Inlining %s into %s\n", |
388 shared_info->DebugName()->ToCString().get(), | 450 shared_info->DebugName()->ToCString().get(), |
389 info_->shared_info()->DebugName()->ToCString().get()); | 451 info_->shared_info()->DebugName()->ToCString().get()); |
390 | 452 |
391 // If function was lazily compiled, it's literals array may not yet be set up. | 453 // If function was lazily compiled, its literals array may not yet be set up. |
392 JSFunction::EnsureLiterals(function); | 454 JSFunction::EnsureLiterals(function); |
393 | 455 |
394 // Create the subgraph for the inlinee. | 456 // Create the subgraph for the inlinee. |
395 Node* start; | 457 Node* start; |
396 Node* end; | 458 Node* end; |
397 { | 459 { |
398 // Run the loop assignment analyzer on the inlinee. | 460 // Run the loop assignment analyzer on the inlinee. |
399 AstLoopAssignmentAnalyzer loop_assignment_analyzer(&zone, &info); | 461 AstLoopAssignmentAnalyzer loop_assignment_analyzer(&zone, &info); |
400 LoopAssignmentAnalysis* loop_assignment = | 462 LoopAssignmentAnalysis* loop_assignment = |
401 loop_assignment_analyzer.Analyze(); | 463 loop_assignment_analyzer.Analyze(); |
402 | 464 |
403 // Run the type hint analyzer on the inlinee. | 465 // Run the type hint analyzer on the inlinee. |
404 TypeHintAnalyzer type_hint_analyzer(&zone); | 466 TypeHintAnalyzer type_hint_analyzer(&zone); |
405 TypeHintAnalysis* type_hint_analysis = | 467 TypeHintAnalysis* type_hint_analysis = |
406 type_hint_analyzer.Analyze(handle(shared_info->code(), info.isolate())); | 468 type_hint_analyzer.Analyze(handle(shared_info->code(), info.isolate())); |
407 | 469 |
408 // Run the AstGraphBuilder to create the subgraph. | 470 // Run the AstGraphBuilder to create the subgraph. |
409 Graph::SubgraphScope scope(graph()); | 471 Graph::SubgraphScope scope(graph()); |
410 AstGraphBuilder graph_builder(&zone, &info, jsgraph(), loop_assignment, | 472 AstGraphBuilder graph_builder(&zone, &info, jsgraph(), loop_assignment, |
411 type_hint_analysis); | 473 type_hint_analysis); |
412 graph_builder.CreateGraph(false); | 474 graph_builder.CreateGraph(false); |
413 | 475 |
414 // Extract the inlinee start/end nodes. | 476 // Extract the inlinee start/end nodes. |
415 start = graph()->start(); | 477 start = graph()->start(); |
416 end = graph()->end(); | 478 end = graph()->end(); |
417 } | 479 } |
418 | 480 |
| 481 if (exception_target != nullptr) { |
| 482 // Find all uncaught 'calls' in the inlinee. |
| 483 AllNodes inlined_nodes(local_zone_, end, graph()); |
| 484 for (Node* subnode : inlined_nodes.reachable) { |
| 485 // Every possibly throwing node with an IfSuccess should get an |
| 486 // IfException. |
| 487 if (subnode->op()->HasProperty(Operator::kNoThrow)) { |
| 488 continue; |
| 489 } |
| 490 bool hasIfException = false; |
| 491 for (Node* use : subnode->uses()) { |
| 492 if (use->opcode() == IrOpcode::kIfException) { |
| 493 hasIfException = true; |
| 494 break; |
| 495 } |
| 496 } |
| 497 if (!hasIfException) { |
| 498 DCHECK_EQ(2, subnode->op()->ControlOutputCount()); |
| 499 uncaught_subcalls.push_back(subnode); |
| 500 } |
| 501 } |
| 502 } |
| 503 |
419 Node* frame_state = call.frame_state(); | 504 Node* frame_state = call.frame_state(); |
420 Node* new_target = jsgraph()->UndefinedConstant(); | 505 Node* new_target = jsgraph()->UndefinedConstant(); |
421 | 506 |
422 // Inline {JSCallConstruct} requires some additional magic. | 507 // Inline {JSCallConstruct} requires some additional magic. |
423 if (node->opcode() == IrOpcode::kJSCallConstruct) { | 508 if (node->opcode() == IrOpcode::kJSCallConstruct) { |
424 // Insert nodes around the call that model the behavior required for a | 509 // Insert nodes around the call that model the behavior required for a |
425 // constructor dispatch (allocate implicit receiver and check return value). | 510 // constructor dispatch (allocate implicit receiver and check return value). |
426 // This models the behavior usually accomplished by our {JSConstructStub}. | 511 // This models the behavior usually accomplished by our {JSConstructStub}. |
427 // Note that the context has to be the callers context (input to call node). | 512 // Note that the context has to be the callers context (input to call node). |
428 Node* receiver = jsgraph()->UndefinedConstant(); // Implicit receiver. | 513 Node* receiver = jsgraph()->UndefinedConstant(); // Implicit receiver. |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
505 // arguments count and context) have to match the number of arguments passed | 590 // arguments count and context) have to match the number of arguments passed |
506 // to the call. | 591 // to the call. |
507 int parameter_count = info.literal()->parameter_count(); | 592 int parameter_count = info.literal()->parameter_count(); |
508 DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5); | 593 DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5); |
509 if (call.formal_arguments() != parameter_count) { | 594 if (call.formal_arguments() != parameter_count) { |
510 frame_state = CreateArtificialFrameState( | 595 frame_state = CreateArtificialFrameState( |
511 node, frame_state, call.formal_arguments(), | 596 node, frame_state, call.formal_arguments(), |
512 FrameStateType::kArgumentsAdaptor, shared_info); | 597 FrameStateType::kArgumentsAdaptor, shared_info); |
513 } | 598 } |
514 | 599 |
515 return InlineCall(node, new_target, context, frame_state, start, end); | 600 return InlineCall(node, new_target, context, frame_state, start, end, |
| 601 exception_target, uncaught_subcalls); |
516 } | 602 } |
517 | 603 |
518 Graph* JSInliner::graph() const { return jsgraph()->graph(); } | 604 Graph* JSInliner::graph() const { return jsgraph()->graph(); } |
519 | 605 |
520 JSOperatorBuilder* JSInliner::javascript() const { | 606 JSOperatorBuilder* JSInliner::javascript() const { |
521 return jsgraph()->javascript(); | 607 return jsgraph()->javascript(); |
522 } | 608 } |
523 | 609 |
524 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } | 610 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } |
525 | 611 |
526 SimplifiedOperatorBuilder* JSInliner::simplified() const { | 612 SimplifiedOperatorBuilder* JSInliner::simplified() const { |
527 return jsgraph()->simplified(); | 613 return jsgraph()->simplified(); |
528 } | 614 } |
529 | 615 |
530 } // namespace compiler | 616 } // namespace compiler |
531 } // namespace internal | 617 } // namespace internal |
532 } // namespace v8 | 618 } // namespace v8 |
OLD | NEW |