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* exception_target, | |
| 78 NodeVector uncaught_subcalls) { | |
|
Jarin
2016/08/10 12:02:52
You should pass the uncought_subcalls as a pointer
bgeron
2016/08/10 14:37:26
Agree, done.
| |
| 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 (exception_target != nullptr) { | |
| 136 // Link uncaught calls in the inlinee to {exception_target} | |
| 137 int subcall_count = static_cast<int>(uncaught_subcalls.size()); | |
| 138 if (uncaught_subcalls.size() > 0) { | |
|
Jarin
2016/08/10 12:02:52
Nit: why don't you use subcall_count that you defi
bgeron
2016/08/10 14:37:26
Done.
| |
| 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 Node* on_exception = | |
| 147 graph()->NewNode(common()->IfException(), subcall, subcall); | |
| 148 on_exception_nodes.push_back(on_exception); | |
| 149 } | |
| 150 | |
| 151 DCHECK_EQ(subcall_count, static_cast<int>(on_exception_nodes.size())); | |
| 152 if (subcall_count > 0) { | |
| 153 Node* control_output = | |
| 154 graph()->NewNode(common()->Merge(subcall_count), subcall_count, | |
| 155 &on_exception_nodes.front()); | |
| 156 NodeVector values_effects(local_zone_); | |
| 157 values_effects = on_exception_nodes; | |
| 158 values_effects.push_back(control_output); | |
| 159 Node* value_output = graph()->NewNode( | |
| 160 common()->Phi(MachineRepresentation::kTagged, subcall_count), | |
| 161 subcall_count + 1, &values_effects.front()); | |
| 162 Node* effect_output = | |
| 163 graph()->NewNode(common()->EffectPhi(subcall_count), | |
| 164 subcall_count + 1, &values_effects.front()); | |
| 165 ReplaceWithValue(exception_target, value_output, effect_output, | |
| 166 control_output); | |
| 167 } else { | |
| 168 ReplaceWithValue(exception_target, exception_target, exception_target, | |
| 169 jsgraph()->Dead()); | |
| 170 } | |
| 171 } | |
| 172 | |
| 133 NodeVector values(local_zone_); | 173 NodeVector values(local_zone_); |
| 134 NodeVector effects(local_zone_); | 174 NodeVector effects(local_zone_); |
| 135 NodeVector controls(local_zone_); | 175 NodeVector controls(local_zone_); |
| 136 for (Node* const input : end->inputs()) { | 176 for (Node* const input : end->inputs()) { |
| 137 switch (input->opcode()) { | 177 switch (input->opcode()) { |
| 138 case IrOpcode::kReturn: | 178 case IrOpcode::kReturn: |
| 139 values.push_back(NodeProperties::GetValueInput(input, 0)); | 179 values.push_back(NodeProperties::GetValueInput(input, 0)); |
| 140 effects.push_back(NodeProperties::GetEffectInput(input)); | 180 effects.push_back(NodeProperties::GetEffectInput(input)); |
| 141 controls.push_back(NodeProperties::GetControlInput(input)); | 181 controls.push_back(NodeProperties::GetControlInput(input)); |
| 142 break; | 182 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: | 302 // calls whenever the target is a constant function object, as follows: |
| 263 // - JSCallFunction(target:constant, receiver, args...) | 303 // - JSCallFunction(target:constant, receiver, args...) |
| 264 // - JSCallConstruct(target:constant, args..., new.target) | 304 // - JSCallConstruct(target:constant, args..., new.target) |
| 265 HeapObjectMatcher match(node->InputAt(0)); | 305 HeapObjectMatcher match(node->InputAt(0)); |
| 266 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); | 306 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); |
| 267 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); | 307 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); |
| 268 | 308 |
| 269 return ReduceJSCall(node, function); | 309 return ReduceJSCall(node, function); |
| 270 } | 310 } |
| 271 | 311 |
| 272 | |
| 273 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { | 312 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { |
| 274 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); | 313 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); |
| 275 JSCallAccessor call(node); | 314 JSCallAccessor call(node); |
| 276 Handle<SharedFunctionInfo> shared_info(function->shared()); | 315 Handle<SharedFunctionInfo> shared_info(function->shared()); |
| 277 | 316 |
| 278 // Function must be inlineable. | 317 // Function must be inlineable. |
| 279 if (!shared_info->IsInlineable()) { | 318 if (!shared_info->IsInlineable()) { |
| 280 TRACE("Not inlining %s into %s because callee is not inlineable\n", | 319 TRACE("Not inlining %s into %s because callee is not inlineable\n", |
| 281 shared_info->DebugName()->ToCString().get(), | 320 shared_info->DebugName()->ToCString().get(), |
| 282 info_->shared_info()->DebugName()->ToCString().get()); | 321 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; | 375 Handle<SharedFunctionInfo> frame_shared_info; |
| 337 if (frame_info.shared_info().ToHandle(&frame_shared_info) && | 376 if (frame_info.shared_info().ToHandle(&frame_shared_info) && |
| 338 *frame_shared_info == *shared_info) { | 377 *frame_shared_info == *shared_info) { |
| 339 TRACE("Not inlining %s into %s because call is recursive\n", | 378 TRACE("Not inlining %s into %s because call is recursive\n", |
| 340 shared_info->DebugName()->ToCString().get(), | 379 shared_info->DebugName()->ToCString().get(), |
| 341 info_->shared_info()->DebugName()->ToCString().get()); | 380 info_->shared_info()->DebugName()->ToCString().get()); |
| 342 return NoChange(); | 381 return NoChange(); |
| 343 } | 382 } |
| 344 } | 383 } |
| 345 | 384 |
| 346 // TODO(turbofan): Inlining into a try-block is not yet supported. | 385 // Find the IfException node, if any. |
| 347 if (NodeProperties::IsExceptionalCall(node)) { | 386 Node* exception_target = nullptr; |
| 348 TRACE("Not inlining %s into %s because of surrounding try-block\n", | 387 for (Edge edge : node->use_edges()) { |
| 349 shared_info->DebugName()->ToCString().get(), | 388 if (NodeProperties::IsControlEdge(edge) && |
| 350 info_->shared_info()->DebugName()->ToCString().get()); | 389 edge.from()->opcode() == IrOpcode::kIfException) { |
| 351 return NoChange(); | 390 DCHECK_NULL(exception_target); |
| 391 exception_target = edge.from(); | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 NodeVector uncaught_subcalls(local_zone_); | |
| 396 | |
| 397 if (exception_target != nullptr) { | |
| 398 TRACE( | |
| 399 "Inlining %s into %s regardless of surrounding try-block to catcher " | |
| 400 "#%d:%s\n", | |
| 401 shared_info->DebugName()->ToCString().get(), | |
| 402 info_->shared_info()->DebugName()->ToCString().get(), | |
| 403 exception_target->id(), exception_target->op()->mnemonic()); | |
| 352 } | 404 } |
| 353 | 405 |
| 354 Zone zone(info_->isolate()->allocator()); | 406 Zone zone(info_->isolate()->allocator()); |
| 355 ParseInfo parse_info(&zone, function); | 407 ParseInfo parse_info(&zone, function); |
| 356 CompilationInfo info(&parse_info, function); | 408 CompilationInfo info(&parse_info, function); |
| 357 if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled(); | 409 if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled(); |
| 358 if (info_->is_type_feedback_enabled()) info.MarkAsTypeFeedbackEnabled(); | 410 if (info_->is_type_feedback_enabled()) info.MarkAsTypeFeedbackEnabled(); |
| 359 | 411 |
| 360 if (!Compiler::ParseAndAnalyze(info.parse_info())) { | 412 if (!Compiler::ParseAndAnalyze(info.parse_info())) { |
| 361 TRACE("Not inlining %s into %s because parsing failed\n", | 413 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()); | 460 Graph::SubgraphScope scope(graph()); |
| 409 AstGraphBuilder graph_builder(&zone, &info, jsgraph(), loop_assignment, | 461 AstGraphBuilder graph_builder(&zone, &info, jsgraph(), loop_assignment, |
| 410 type_hint_analysis); | 462 type_hint_analysis); |
| 411 graph_builder.CreateGraph(false); | 463 graph_builder.CreateGraph(false); |
| 412 | 464 |
| 413 // Extract the inlinee start/end nodes. | 465 // Extract the inlinee start/end nodes. |
| 414 start = graph()->start(); | 466 start = graph()->start(); |
| 415 end = graph()->end(); | 467 end = graph()->end(); |
| 416 } | 468 } |
| 417 | 469 |
| 470 if (exception_target != nullptr) { | |
| 471 // Find all uncaught 'calls' in the inlinee. | |
| 472 AllNodes inlined_nodes(local_zone_, end, graph()); | |
| 473 for (Node* subnode : inlined_nodes.live) { | |
| 474 // Every possibly throwing node with an IfSuccess should get an | |
| 475 // IfException. | |
| 476 if (subnode->op()->HasProperty(Operator::kNoThrow)) { | |
| 477 continue; | |
| 478 } | |
| 479 bool hasIfSuccess = false; | |
| 480 bool hasIfException = false; | |
| 481 for (Node* use : subnode->uses()) { | |
| 482 if (use->opcode() == IrOpcode::kIfSuccess) { | |
|
Jarin
2016/08/10 12:02:52
I assume this won't be needed once every no-no-thr
bgeron
2016/08/10 14:37:26
Hm, I fixed some things to make them saner, but cu
Jarin
2016/08/11 09:54:23
Actually, if Throw does not have IfSuccess, this c
bgeron
2016/08/11 18:55:55
Removed that condition.
To make this work, I had
| |
| 483 hasIfSuccess = true; | |
| 484 } else if (use->opcode() == IrOpcode::kIfException) { | |
| 485 hasIfException = true; | |
| 486 } | |
| 487 } | |
| 488 if (hasIfSuccess && !hasIfException) { | |
| 489 DCHECK_EQ(2, subnode->op()->ControlOutputCount()); | |
| 490 uncaught_subcalls.push_back(subnode); | |
| 491 } | |
| 492 } | |
| 493 } | |
| 494 | |
| 418 Node* frame_state = call.frame_state(); | 495 Node* frame_state = call.frame_state(); |
| 419 Node* new_target = jsgraph()->UndefinedConstant(); | 496 Node* new_target = jsgraph()->UndefinedConstant(); |
| 420 | 497 |
| 421 // Inline {JSCallConstruct} requires some additional magic. | 498 // Inline {JSCallConstruct} requires some additional magic. |
| 422 if (node->opcode() == IrOpcode::kJSCallConstruct) { | 499 if (node->opcode() == IrOpcode::kJSCallConstruct) { |
| 423 // Insert nodes around the call that model the behavior required for a | 500 // Insert nodes around the call that model the behavior required for a |
| 424 // constructor dispatch (allocate implicit receiver and check return value). | 501 // constructor dispatch (allocate implicit receiver and check return value). |
| 425 // This models the behavior usually accomplished by our {JSConstructStub}. | 502 // This models the behavior usually accomplished by our {JSConstructStub}. |
| 426 // Note that the context has to be the callers context (input to call node). | 503 // Note that the context has to be the callers context (input to call node). |
| 427 Node* receiver = jsgraph()->UndefinedConstant(); // Implicit receiver. | 504 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 | 583 // arguments count and context) have to match the number of arguments passed |
| 507 // to the call. | 584 // to the call. |
| 508 int parameter_count = info.literal()->parameter_count(); | 585 int parameter_count = info.literal()->parameter_count(); |
| 509 DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5); | 586 DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5); |
| 510 if (call.formal_arguments() != parameter_count) { | 587 if (call.formal_arguments() != parameter_count) { |
| 511 frame_state = CreateArtificialFrameState( | 588 frame_state = CreateArtificialFrameState( |
| 512 node, frame_state, call.formal_arguments(), | 589 node, frame_state, call.formal_arguments(), |
| 513 FrameStateType::kArgumentsAdaptor, shared_info); | 590 FrameStateType::kArgumentsAdaptor, shared_info); |
| 514 } | 591 } |
| 515 | 592 |
| 516 return InlineCall(node, new_target, context, frame_state, start, end); | 593 return InlineCall(node, new_target, context, frame_state, start, end, |
| 594 exception_target, uncaught_subcalls); | |
| 517 } | 595 } |
| 518 | 596 |
| 519 Graph* JSInliner::graph() const { return jsgraph()->graph(); } | 597 Graph* JSInliner::graph() const { return jsgraph()->graph(); } |
| 520 | 598 |
| 521 JSOperatorBuilder* JSInliner::javascript() const { | 599 JSOperatorBuilder* JSInliner::javascript() const { |
| 522 return jsgraph()->javascript(); | 600 return jsgraph()->javascript(); |
| 523 } | 601 } |
| 524 | 602 |
| 525 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } | 603 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } |
| 526 | 604 |
| 527 } // namespace compiler | 605 } // namespace compiler |
| 528 } // namespace internal | 606 } // namespace internal |
| 529 } // namespace v8 | 607 } // namespace v8 |
| OLD | NEW |