Chromium Code Reviews| 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/type-hint-analyzer.h" | 20 #include "src/compiler/type-hint-analyzer.h" |
| 20 #include "src/isolate-inl.h" | 21 #include "src/isolate-inl.h" |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 64 // Both, {JSCallFunction} and {JSCallConstruct}, have two extra inputs: | 65 // Both, {JSCallFunction} and {JSCallConstruct}, have two extra inputs: |
| 65 // - JSCallConstruct: Includes target function and new target. | 66 // - JSCallConstruct: Includes target function and new target. |
| 66 // - JSCallFunction: Includes target function and receiver. | 67 // - JSCallFunction: Includes target function and receiver. |
| 67 return call_->op()->ValueInputCount() - 2; | 68 return call_->op()->ValueInputCount() - 2; |
| 68 } | 69 } |
| 69 | 70 |
| 70 private: | 71 private: |
| 71 Node* call_; | 72 Node* call_; |
| 72 }; | 73 }; |
| 73 | 74 |
| 74 | |
| 75 Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context, | 75 Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context, |
| 76 Node* frame_state, Node* start, Node* end) { | 76 Node* frame_state, Node* start, Node* end, |
| 77 Node* exceptionTarget, | |
| 78 NodeVector uncaught_subcalls) { | |
| 77 // The scheduler is smart enough to place our code; we just ensure {control} | 79 // The scheduler is smart enough to place our code; we just ensure {control} |
| 78 // becomes the control input of the start of the inlinee, and {effect} becomes | 80 // becomes the control input of the start of the inlinee, and {effect} becomes |
| 79 // the effect input of the start of the inlinee. | 81 // the effect input of the start of the inlinee. |
| 80 Node* control = NodeProperties::GetControlInput(call); | 82 Node* control = NodeProperties::GetControlInput(call); |
| 81 Node* effect = NodeProperties::GetEffectInput(call); | 83 Node* effect = NodeProperties::GetEffectInput(call); |
| 82 | 84 |
| 83 int const inlinee_new_target_index = | 85 int const inlinee_new_target_index = |
| 84 static_cast<int>(start->op()->ValueOutputCount()) - 3; | 86 static_cast<int>(start->op()->ValueOutputCount()) - 3; |
| 85 int const inlinee_arity_index = | 87 int const inlinee_arity_index = |
| 86 static_cast<int>(start->op()->ValueOutputCount()) - 2; | 88 static_cast<int>(start->op()->ValueOutputCount()) - 2; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 123 edge.UpdateTo(control); | 125 edge.UpdateTo(control); |
| 124 } else if (NodeProperties::IsFrameStateEdge(edge)) { | 126 } else if (NodeProperties::IsFrameStateEdge(edge)) { |
| 125 edge.UpdateTo(frame_state); | 127 edge.UpdateTo(frame_state); |
| 126 } else { | 128 } else { |
| 127 UNREACHABLE(); | 129 UNREACHABLE(); |
| 128 } | 130 } |
| 129 break; | 131 break; |
| 130 } | 132 } |
| 131 } | 133 } |
| 132 | 134 |
| 135 if (exceptionTarget != nullptr) { | |
| 136 // Link uncaught calls in the inlinee to {exceptionTarget} | |
| 137 int subcall_count = static_cast<int>(uncaught_subcalls.size()); | |
| 138 if (uncaught_subcalls.size() > 0) { | |
| 139 TRACE( | |
| 140 "Inlinee contains %d calls without IfException; " | |
| 141 "linking to existing IfException\n", | |
| 142 subcall_count); | |
| 143 } | |
| 144 NodeVector on_exception_nodes(local_zone_); | |
| 145 for (Node* subcall : uncaught_subcalls) { | |
| 146 const Operator* op = common()->IfException(); | |
|
Jarin
2016/08/05 14:04:48
Nit: no need for separate variable, just inline in
bgeron
2016/08/08 09:30:06
Done.
| |
| 147 | |
| 148 Node* on_exception = graph()->NewNode(op, 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(exceptionTarget, value_output, effect_output, | |
| 167 control_output); | |
| 168 } else { | |
| 169 ReplaceWithValue(exceptionTarget, exceptionTarget, exceptionTarget, | |
| 170 jsgraph()->Dead()); | |
| 171 } | |
| 172 } | |
| 173 | |
| 133 NodeVector values(local_zone_); | 174 NodeVector values(local_zone_); |
| 134 NodeVector effects(local_zone_); | 175 NodeVector effects(local_zone_); |
| 135 NodeVector controls(local_zone_); | 176 NodeVector controls(local_zone_); |
| 136 for (Node* const input : end->inputs()) { | 177 for (Node* const input : end->inputs()) { |
| 137 switch (input->opcode()) { | 178 switch (input->opcode()) { |
| 138 case IrOpcode::kReturn: | 179 case IrOpcode::kReturn: |
| 139 values.push_back(NodeProperties::GetValueInput(input, 0)); | 180 values.push_back(NodeProperties::GetValueInput(input, 0)); |
| 140 effects.push_back(NodeProperties::GetEffectInput(input)); | 181 effects.push_back(NodeProperties::GetEffectInput(input)); |
| 141 controls.push_back(NodeProperties::GetControlInput(input)); | 182 controls.push_back(NodeProperties::GetControlInput(input)); |
| 142 break; | 183 break; |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 262 // calls whenever the target is a constant function object, as follows: | 303 // calls whenever the target is a constant function object, as follows: |
| 263 // - JSCallFunction(target:constant, receiver, args...) | 304 // - JSCallFunction(target:constant, receiver, args...) |
| 264 // - JSCallConstruct(target:constant, args..., new.target) | 305 // - JSCallConstruct(target:constant, args..., new.target) |
| 265 HeapObjectMatcher match(node->InputAt(0)); | 306 HeapObjectMatcher match(node->InputAt(0)); |
| 266 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); | 307 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); |
| 267 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); | 308 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); |
| 268 | 309 |
| 269 return ReduceJSCall(node, function); | 310 return ReduceJSCall(node, function); |
| 270 } | 311 } |
| 271 | 312 |
| 272 | |
| 273 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { | 313 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { |
| 274 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); | 314 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); |
| 275 JSCallAccessor call(node); | 315 JSCallAccessor call(node); |
| 276 Handle<SharedFunctionInfo> shared_info(function->shared()); | 316 Handle<SharedFunctionInfo> shared_info(function->shared()); |
| 277 | 317 |
| 278 // Function must be inlineable. | 318 // Function must be inlineable. |
| 279 if (!shared_info->IsInlineable()) { | 319 if (!shared_info->IsInlineable()) { |
| 280 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", |
| 281 shared_info->DebugName()->ToCString().get(), | 321 shared_info->DebugName()->ToCString().get(), |
| 282 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... | |
| 336 Handle<SharedFunctionInfo> frame_shared_info; | 376 Handle<SharedFunctionInfo> frame_shared_info; |
| 337 if (frame_info.shared_info().ToHandle(&frame_shared_info) && | 377 if (frame_info.shared_info().ToHandle(&frame_shared_info) && |
| 338 *frame_shared_info == *shared_info) { | 378 *frame_shared_info == *shared_info) { |
| 339 TRACE("Not inlining %s into %s because call is recursive\n", | 379 TRACE("Not inlining %s into %s because call is recursive\n", |
| 340 shared_info->DebugName()->ToCString().get(), | 380 shared_info->DebugName()->ToCString().get(), |
| 341 info_->shared_info()->DebugName()->ToCString().get()); | 381 info_->shared_info()->DebugName()->ToCString().get()); |
| 342 return NoChange(); | 382 return NoChange(); |
| 343 } | 383 } |
| 344 } | 384 } |
| 345 | 385 |
| 346 // TODO(turbofan): Inlining into a try-block is not yet supported. | 386 // Find the IfException node, if any. |
| 347 if (NodeProperties::IsExceptionalCall(node)) { | 387 Node* exceptionTarget = nullptr; |
|
Jarin
2016/08/05 14:04:48
exceptionTarget -> exception_target
bgeron
2016/08/08 09:30:06
Done.
| |
| 348 TRACE("Not inlining %s into %s because of surrounding try-block\n", | 388 for (Edge edge : node->use_edges()) { |
| 349 shared_info->DebugName()->ToCString().get(), | 389 if (NodeProperties::IsControlEdge(edge) && |
| 350 info_->shared_info()->DebugName()->ToCString().get()); | 390 edge.from()->opcode() == IrOpcode::kIfException) { |
| 351 return NoChange(); | 391 DCHECK_NULL(exceptionTarget); |
| 392 exceptionTarget = edge.from(); | |
| 393 } | |
| 394 } | |
| 395 | |
| 396 NodeVector uncaught_subcalls(local_zone_); | |
| 397 | |
| 398 if (exceptionTarget != nullptr) { | |
| 399 TRACE( | |
| 400 "Inlining %s into %s regardless of surrounding try-block to catcher " | |
| 401 "#%d:%s\n", | |
| 402 shared_info->DebugName()->ToCString().get(), | |
| 403 info_->shared_info()->DebugName()->ToCString().get(), | |
| 404 exceptionTarget->id(), exceptionTarget->op()->mnemonic()); | |
| 352 } | 405 } |
| 353 | 406 |
| 354 Zone zone(info_->isolate()->allocator()); | 407 Zone zone(info_->isolate()->allocator()); |
| 355 ParseInfo parse_info(&zone, function); | 408 ParseInfo parse_info(&zone, function); |
| 356 CompilationInfo info(&parse_info, function); | 409 CompilationInfo info(&parse_info, function); |
| 357 if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled(); | 410 if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled(); |
| 358 if (info_->is_type_feedback_enabled()) info.MarkAsTypeFeedbackEnabled(); | 411 if (info_->is_type_feedback_enabled()) info.MarkAsTypeFeedbackEnabled(); |
| 359 | 412 |
| 360 if (!Compiler::ParseAndAnalyze(info.parse_info())) { | 413 if (!Compiler::ParseAndAnalyze(info.parse_info())) { |
| 361 TRACE("Not inlining %s into %s because parsing failed\n", | 414 TRACE("Not inlining %s into %s because parsing failed\n", |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 408 Graph::SubgraphScope scope(graph()); | 461 Graph::SubgraphScope scope(graph()); |
| 409 AstGraphBuilder graph_builder(&zone, &info, jsgraph(), loop_assignment, | 462 AstGraphBuilder graph_builder(&zone, &info, jsgraph(), loop_assignment, |
| 410 type_hint_analysis); | 463 type_hint_analysis); |
| 411 graph_builder.CreateGraph(false); | 464 graph_builder.CreateGraph(false); |
| 412 | 465 |
| 413 // Extract the inlinee start/end nodes. | 466 // Extract the inlinee start/end nodes. |
| 414 start = graph()->start(); | 467 start = graph()->start(); |
| 415 end = graph()->end(); | 468 end = graph()->end(); |
| 416 } | 469 } |
| 417 | 470 |
| 471 // Find all uncaught calls in the inlinee. | |
| 472 AllNodes inlined_nodes(local_zone_, end, graph()); | |
| 473 for (Node* subcall : inlined_nodes.live) { | |
| 474 if (subcall->opcode() == IrOpcode::kJSCallFunction || | |
| 475 subcall->opcode() == IrOpcode::kJSCallConstruct || | |
| 476 subcall->opcode() == IrOpcode::kJSCallRuntime) { | |
|
Jarin
2016/08/05 14:04:48
As discussed offline, you need a more general way:
bgeron
2016/08/08 09:30:06
Done.
| |
| 477 bool hasIfException = false; | |
| 478 for (Node* use : subcall->uses()) { | |
| 479 if (use->opcode() == IrOpcode::kIfException) { | |
| 480 hasIfException = true; | |
| 481 std::ostringstream s; | |
| 482 s << "found exception node #" << use->id(); | |
|
Jarin
2016/08/05 14:04:48
Remove the logging.
bgeron
2016/08/08 09:30:06
Done.
| |
| 483 break; | |
| 484 } | |
| 485 } | |
| 486 if (!hasIfException) { | |
| 487 uncaught_subcalls.push_back(subcall); | |
| 488 } | |
| 489 } | |
| 490 } | |
| 491 | |
| 418 Node* frame_state = call.frame_state(); | 492 Node* frame_state = call.frame_state(); |
| 419 Node* new_target = jsgraph()->UndefinedConstant(); | 493 Node* new_target = jsgraph()->UndefinedConstant(); |
| 420 | 494 |
| 421 // Inline {JSCallConstruct} requires some additional magic. | 495 // Inline {JSCallConstruct} requires some additional magic. |
| 422 if (node->opcode() == IrOpcode::kJSCallConstruct) { | 496 if (node->opcode() == IrOpcode::kJSCallConstruct) { |
| 423 // Insert nodes around the call that model the behavior required for a | 497 // Insert nodes around the call that model the behavior required for a |
| 424 // constructor dispatch (allocate implicit receiver and check return value). | 498 // constructor dispatch (allocate implicit receiver and check return value). |
| 425 // This models the behavior usually accomplished by our {JSConstructStub}. | 499 // This models the behavior usually accomplished by our {JSConstructStub}. |
| 426 // Note that the context has to be the callers context (input to call node). | 500 // Note that the context has to be the callers context (input to call node). |
| 427 Node* receiver = jsgraph()->UndefinedConstant(); // Implicit receiver. | 501 Node* receiver = jsgraph()->UndefinedConstant(); // Implicit receiver. |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 506 // arguments count and context) have to match the number of arguments passed | 580 // arguments count and context) have to match the number of arguments passed |
| 507 // to the call. | 581 // to the call. |
| 508 int parameter_count = info.literal()->parameter_count(); | 582 int parameter_count = info.literal()->parameter_count(); |
| 509 DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5); | 583 DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5); |
| 510 if (call.formal_arguments() != parameter_count) { | 584 if (call.formal_arguments() != parameter_count) { |
| 511 frame_state = CreateArtificialFrameState( | 585 frame_state = CreateArtificialFrameState( |
| 512 node, frame_state, call.formal_arguments(), | 586 node, frame_state, call.formal_arguments(), |
| 513 FrameStateType::kArgumentsAdaptor, shared_info); | 587 FrameStateType::kArgumentsAdaptor, shared_info); |
| 514 } | 588 } |
| 515 | 589 |
| 516 return InlineCall(node, new_target, context, frame_state, start, end); | 590 return InlineCall(node, new_target, context, frame_state, start, end, |
| 591 exceptionTarget, uncaught_subcalls); | |
| 517 } | 592 } |
| 518 | 593 |
| 519 Graph* JSInliner::graph() const { return jsgraph()->graph(); } | 594 Graph* JSInliner::graph() const { return jsgraph()->graph(); } |
| 520 | 595 |
| 521 JSOperatorBuilder* JSInliner::javascript() const { | 596 JSOperatorBuilder* JSInliner::javascript() const { |
| 522 return jsgraph()->javascript(); | 597 return jsgraph()->javascript(); |
| 523 } | 598 } |
| 524 | 599 |
| 525 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } | 600 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } |
| 526 | 601 |
| 527 } // namespace compiler | 602 } // namespace compiler |
| 528 } // namespace internal | 603 } // namespace internal |
| 529 } // namespace v8 | 604 } // namespace v8 |
| OLD | NEW |