| 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 |