| 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.h" | 7 #include "src/ast.h" |
| 8 #include "src/ast-numbering.h" | 8 #include "src/ast-numbering.h" |
| 9 #include "src/compiler.h" | 9 #include "src/compiler.h" |
| 10 #include "src/compiler/all-nodes.h" | 10 #include "src/compiler/all-nodes.h" |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 257 const Operator* op_param = | 257 const Operator* op_param = |
| 258 jsgraph_->common()->StateValues(static_cast<int>(params.size())); | 258 jsgraph_->common()->StateValues(static_cast<int>(params.size())); |
| 259 Node* params_node = jsgraph_->graph()->NewNode( | 259 Node* params_node = jsgraph_->graph()->NewNode( |
| 260 op_param, static_cast<int>(params.size()), ¶ms.front()); | 260 op_param, static_cast<int>(params.size()), ¶ms.front()); |
| 261 return jsgraph_->graph()->NewNode(op, params_node, node0, node0, | 261 return jsgraph_->graph()->NewNode(op, params_node, node0, node0, |
| 262 jsgraph_->UndefinedConstant(), | 262 jsgraph_->UndefinedConstant(), |
| 263 node->InputAt(0), outer_frame_state); | 263 node->InputAt(0), outer_frame_state); |
| 264 } | 264 } |
| 265 | 265 |
| 266 | 266 |
| 267 namespace { |
| 268 |
| 269 // TODO(mstarzinger,verwaest): Move this predicate onto SharedFunctionInfo? |
| 270 bool NeedsImplicitReceiver(Handle<JSFunction> function, Isolate* isolate) { |
| 271 Code* construct_stub = function->shared()->construct_stub(); |
| 272 return construct_stub != *isolate->builtins()->JSBuiltinsConstructStub() && |
| 273 construct_stub != *isolate->builtins()->ConstructedNonConstructable(); |
| 274 } |
| 275 |
| 276 } // namespace |
| 277 |
| 278 |
| 267 Reduction JSInliner::Reduce(Node* node) { | 279 Reduction JSInliner::Reduce(Node* node) { |
| 268 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); | 280 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); |
| 269 | 281 |
| 270 // This reducer can handle both normal function calls as well a constructor | 282 // This reducer can handle both normal function calls as well a constructor |
| 271 // calls whenever the target is a constant function object, as follows: | 283 // calls whenever the target is a constant function object, as follows: |
| 272 // - JSCallFunction(target:constant, receiver, args...) | 284 // - JSCallFunction(target:constant, receiver, args...) |
| 273 // - JSCallConstruct(target:constant, args..., new.target) | 285 // - JSCallConstruct(target:constant, args..., new.target) |
| 274 HeapObjectMatcher match(node->InputAt(0)); | 286 HeapObjectMatcher match(node->InputAt(0)); |
| 275 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); | 287 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); |
| 276 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); | 288 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); |
| 277 | 289 |
| 278 return ReduceJSCall(node, function); | 290 return ReduceJSCall(node, function); |
| 279 } | 291 } |
| 280 | 292 |
| 281 | 293 |
| 282 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { | 294 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { |
| 283 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); | 295 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); |
| 284 JSCallAccessor call(node); | 296 JSCallAccessor call(node); |
| 285 | 297 |
| 298 // Function must be inlineable. |
| 286 if (!function->shared()->IsInlineable()) { | 299 if (!function->shared()->IsInlineable()) { |
| 287 // Function must be inlineable. | |
| 288 TRACE("Not inlining %s into %s because callee is not inlineable\n", | 300 TRACE("Not inlining %s into %s because callee is not inlineable\n", |
| 289 function->shared()->DebugName()->ToCString().get(), | 301 function->shared()->DebugName()->ToCString().get(), |
| 290 info_->shared_info()->DebugName()->ToCString().get()); | 302 info_->shared_info()->DebugName()->ToCString().get()); |
| 291 return NoChange(); | 303 return NoChange(); |
| 292 } | 304 } |
| 293 | 305 |
| 306 // Constructor must be constructable. |
| 294 if (node->opcode() == IrOpcode::kJSCallConstruct && | 307 if (node->opcode() == IrOpcode::kJSCallConstruct && |
| 295 !function->IsConstructor()) { | 308 !function->IsConstructor()) { |
| 296 // Constructor must be constructable. | 309 TRACE("Not inlining %s into %s because constructor is not constructable.\n", |
| 297 TRACE("Not inlining %s into %s since constructor is not constructable.\n", | |
| 298 function->shared()->DebugName()->ToCString().get(), | 310 function->shared()->DebugName()->ToCString().get(), |
| 299 info_->shared_info()->DebugName()->ToCString().get()); | 311 info_->shared_info()->DebugName()->ToCString().get()); |
| 300 return NoChange(); | 312 return NoChange(); |
| 301 } | 313 } |
| 302 | 314 |
| 303 // Class constructors are callable, but [[Call]] will raise an exception. | 315 // Class constructors are callable, but [[Call]] will raise an exception. |
| 304 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). | 316 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). |
| 305 if (IsClassConstructor(function->shared()->kind())) { | 317 if (node->opcode() == IrOpcode::kJSCallFunction && |
| 306 TRACE("Not inlining %s into %s because callee is classConstructor\n", | 318 IsClassConstructor(function->shared()->kind())) { |
| 319 TRACE("Not inlining %s into %s because callee is a class constructor.\n", |
| 307 function->shared()->DebugName()->ToCString().get(), | 320 function->shared()->DebugName()->ToCString().get(), |
| 308 info_->shared_info()->DebugName()->ToCString().get()); | 321 info_->shared_info()->DebugName()->ToCString().get()); |
| 309 return NoChange(); | 322 return NoChange(); |
| 310 } | 323 } |
| 311 | 324 |
| 325 // Function contains break points. |
| 312 if (function->shared()->HasDebugInfo()) { | 326 if (function->shared()->HasDebugInfo()) { |
| 313 // Function contains break points. | |
| 314 TRACE("Not inlining %s into %s because callee may contain break points\n", | 327 TRACE("Not inlining %s into %s because callee may contain break points\n", |
| 315 function->shared()->DebugName()->ToCString().get(), | 328 function->shared()->DebugName()->ToCString().get(), |
| 316 info_->shared_info()->DebugName()->ToCString().get()); | 329 info_->shared_info()->DebugName()->ToCString().get()); |
| 317 return NoChange(); | 330 return NoChange(); |
| 318 } | 331 } |
| 319 | 332 |
| 320 // Disallow cross native-context inlining for now. This means that all parts | 333 // Disallow cross native-context inlining for now. This means that all parts |
| 321 // of the resulting code will operate on the same global object. | 334 // of the resulting code will operate on the same global object. |
| 322 // This also prevents cross context leaks for asm.js code, where we could | 335 // This also prevents cross context leaks for asm.js code, where we could |
| 323 // inline functions from a different context and hold on to that context (and | 336 // inline functions from a different context and hold on to that context (and |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 417 | 430 |
| 418 CopyVisitor visitor(&graph, jsgraph_->graph(), &zone); | 431 CopyVisitor visitor(&graph, jsgraph_->graph(), &zone); |
| 419 visitor.CopyGraph(); | 432 visitor.CopyGraph(); |
| 420 | 433 |
| 421 Node* start = visitor.GetCopy(graph.start()); | 434 Node* start = visitor.GetCopy(graph.start()); |
| 422 Node* end = visitor.GetCopy(graph.end()); | 435 Node* end = visitor.GetCopy(graph.end()); |
| 423 Node* frame_state = call.frame_state_after(); | 436 Node* frame_state = call.frame_state_after(); |
| 424 Node* new_target = jsgraph_->UndefinedConstant(); | 437 Node* new_target = jsgraph_->UndefinedConstant(); |
| 425 | 438 |
| 426 // Insert nodes around the call that model the behavior required for a | 439 // Insert nodes around the call that model the behavior required for a |
| 427 // constructor dispatch and turn the constructor call into a regular call. | 440 // constructor dispatch (allocate implicit receiver and check return value). |
| 428 // This models the behavior usually accomplished by our {JSConstructStub}. | 441 // This models the behavior usually accomplished by our {JSConstructStub}. |
| 429 // Note that the context has to be the callers context (input to call node). | 442 // Note that the context has to be the callers context (input to call node). |
| 430 // TODO(4544): Once we support inlining builtins, make sure no implicit | 443 Node* receiver = jsgraph_->UndefinedConstant(); // Implicit receiver. |
| 431 // receiver is created for builtins that don't expect any. | 444 if (node->opcode() == IrOpcode::kJSCallConstruct && |
| 432 if (node->opcode() == IrOpcode::kJSCallConstruct) { | 445 NeedsImplicitReceiver(function, info_->isolate())) { |
| 433 Node* effect = NodeProperties::GetEffectInput(node); | 446 Node* effect = NodeProperties::GetEffectInput(node); |
| 434 Node* context = NodeProperties::GetContextInput(node); | 447 Node* context = NodeProperties::GetContextInput(node); |
| 435 Node* create = jsgraph_->graph()->NewNode(jsgraph_->javascript()->Create(), | 448 Node* create = jsgraph_->graph()->NewNode(jsgraph_->javascript()->Create(), |
| 436 call.target(), call.new_target(), | 449 call.target(), call.new_target(), |
| 437 context, frame_state, effect); | 450 context, frame_state, effect); |
| 438 NodeProperties::ReplaceEffectInput(node, create); | 451 NodeProperties::ReplaceEffectInput(node, create); |
| 439 // TODO(4544): For derived constructors we should not allocate an implicit | |
| 440 // receiver and also the return value should not be checked afterwards. | |
| 441 CHECK(!IsClassConstructor(function->shared()->kind())); | |
| 442 // Swizzle the inputs of the {JSCallConstruct} node to look like inputs to | |
| 443 // any {JSCallFunction} node so that the rest of the inlining machinery | |
| 444 // behaves as if we were dealing with a regular function invocation. | |
| 445 new_target = call.new_target(); // Retrieve new target value input. | |
| 446 node->RemoveInput(call.formal_arguments() + 1); // Drop new target. | |
| 447 node->InsertInput(jsgraph_->graph()->zone(), 1, create); | |
| 448 // Insert a check of the return value to determine whether the return value | 452 // Insert a check of the return value to determine whether the return value |
| 449 // or the implicit receiver should be selected as a result of the call. | 453 // or the implicit receiver should be selected as a result of the call. |
| 450 Node* check = jsgraph_->graph()->NewNode( | 454 Node* check = jsgraph_->graph()->NewNode( |
| 451 jsgraph_->javascript()->CallRuntime(Runtime::kInlineIsSpecObject, 1), | 455 jsgraph_->javascript()->CallRuntime(Runtime::kInlineIsSpecObject, 1), |
| 452 node, context, node, start); | 456 node, context, node, start); |
| 453 Node* select = jsgraph_->graph()->NewNode( | 457 Node* select = jsgraph_->graph()->NewNode( |
| 454 jsgraph_->common()->Select(kMachAnyTagged), check, node, create); | 458 jsgraph_->common()->Select(kMachAnyTagged), check, node, create); |
| 455 NodeProperties::ReplaceUses(node, select, check, node, node); | 459 NodeProperties::ReplaceUses(node, select, check, node, node); |
| 456 NodeProperties::ReplaceValueInput(select, node, 1); | 460 NodeProperties::ReplaceValueInput(select, node, 1); |
| 457 NodeProperties::ReplaceValueInput(check, node, 0); | 461 NodeProperties::ReplaceValueInput(check, node, 0); |
| 458 NodeProperties::ReplaceEffectInput(check, node); | 462 NodeProperties::ReplaceEffectInput(check, node); |
| 463 receiver = create; // The implicit receiver. |
| 464 } |
| 465 |
| 466 // Swizzle the inputs of the {JSCallConstruct} node to look like inputs to a |
| 467 // normal {JSCallFunction} node so that the rest of the inlining machinery |
| 468 // behaves as if we were dealing with a regular function invocation. |
| 469 if (node->opcode() == IrOpcode::kJSCallConstruct) { |
| 470 new_target = call.new_target(); // Retrieve new target value input. |
| 471 node->RemoveInput(call.formal_arguments() + 1); // Drop new target. |
| 472 node->InsertInput(jsgraph_->graph()->zone(), 1, receiver); |
| 459 // Insert a construct stub frame into the chain of frame states. This will | 473 // Insert a construct stub frame into the chain of frame states. This will |
| 460 // reconstruct the proper frame when deoptimizing within the constructor. | 474 // reconstruct the proper frame when deoptimizing within the constructor. |
| 461 frame_state = CreateArtificialFrameState( | 475 frame_state = CreateArtificialFrameState( |
| 462 node, frame_state, call.formal_arguments(), | 476 node, frame_state, call.formal_arguments(), |
| 463 FrameStateType::kConstructStub, info.shared_info()); | 477 FrameStateType::kConstructStub, info.shared_info()); |
| 464 } | 478 } |
| 465 | 479 |
| 466 // The inlinee specializes to the context from the JSFunction object. | 480 // The inlinee specializes to the context from the JSFunction object. |
| 467 // TODO(turbofan): We might want to load the context from the JSFunction at | 481 // TODO(turbofan): We might want to load the context from the JSFunction at |
| 468 // runtime in case we only know the SharedFunctionInfo once we have dynamic | 482 // runtime in case we only know the SharedFunctionInfo once we have dynamic |
| (...skipping 27 matching lines...) Expand all Loading... |
| 496 node, frame_state, call.formal_arguments(), | 510 node, frame_state, call.formal_arguments(), |
| 497 FrameStateType::kArgumentsAdaptor, info.shared_info()); | 511 FrameStateType::kArgumentsAdaptor, info.shared_info()); |
| 498 } | 512 } |
| 499 | 513 |
| 500 return InlineCall(node, new_target, context, frame_state, start, end); | 514 return InlineCall(node, new_target, context, frame_state, start, end); |
| 501 } | 515 } |
| 502 | 516 |
| 503 } // namespace compiler | 517 } // namespace compiler |
| 504 } // namespace internal | 518 } // namespace internal |
| 505 } // namespace v8 | 519 } // namespace v8 |
| OLD | NEW |