| 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 16 matching lines...) Expand all Loading... |
| 27 namespace v8 { | 27 namespace v8 { |
| 28 namespace internal { | 28 namespace internal { |
| 29 namespace compiler { | 29 namespace compiler { |
| 30 | 30 |
| 31 #define TRACE(...) \ | 31 #define TRACE(...) \ |
| 32 do { \ | 32 do { \ |
| 33 if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \ | 33 if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \ |
| 34 } while (false) | 34 } while (false) |
| 35 | 35 |
| 36 | 36 |
| 37 // Provides convenience accessors for calls to JS functions. | 37 // Provides convenience accessors for the common layout of nodes having either |
| 38 class JSCallFunctionAccessor { | 38 // the {JSCallFunction} or the {JSCallConstruct} operator. |
| 39 class JSCallAccessor { |
| 39 public: | 40 public: |
| 40 explicit JSCallFunctionAccessor(Node* call) : call_(call) { | 41 explicit JSCallAccessor(Node* call) : call_(call) { |
| 41 DCHECK_EQ(IrOpcode::kJSCallFunction, call->opcode()); | 42 DCHECK(call->opcode() == IrOpcode::kJSCallFunction || |
| 43 call->opcode() == IrOpcode::kJSCallConstruct); |
| 42 } | 44 } |
| 43 | 45 |
| 44 Node* jsfunction() { return call_->InputAt(0); } | 46 Node* target() { |
| 45 | 47 // Both, {JSCallFunction} and {JSCallConstruct}, have same layout here. |
| 46 Node* receiver() { return call_->InputAt(1); } | 48 return call_->InputAt(0); |
| 47 | |
| 48 Node* formal_argument(size_t index) { | |
| 49 DCHECK(index < formal_arguments()); | |
| 50 return call_->InputAt(static_cast<int>(2 + index)); | |
| 51 } | 49 } |
| 52 | 50 |
| 53 size_t formal_arguments() { | 51 Node* receiver() { |
| 54 // {value_inputs} includes jsfunction and receiver. | 52 DCHECK_EQ(IrOpcode::kJSCallFunction, call_->opcode()); |
| 55 size_t value_inputs = call_->op()->ValueInputCount(); | 53 return call_->InputAt(1); |
| 56 DCHECK_GE(call_->InputCount(), 2); | 54 } |
| 57 return value_inputs - 2; | 55 |
| 56 Node* original_constructor() { |
| 57 DCHECK_EQ(IrOpcode::kJSCallConstruct, call_->opcode()); |
| 58 return call_->InputAt(formal_arguments() + 1); |
| 58 } | 59 } |
| 59 | 60 |
| 60 Node* frame_state_before() { | 61 Node* frame_state_before() { |
| 62 DCHECK_EQ(IrOpcode::kJSCallFunction, call_->opcode()); |
| 61 return NodeProperties::GetFrameStateInput(call_, 1); | 63 return NodeProperties::GetFrameStateInput(call_, 1); |
| 62 } | 64 } |
| 65 |
| 63 Node* frame_state_after() { | 66 Node* frame_state_after() { |
| 67 // Both, {JSCallFunction} and {JSCallConstruct}, have frame state after. |
| 64 return NodeProperties::GetFrameStateInput(call_, 0); | 68 return NodeProperties::GetFrameStateInput(call_, 0); |
| 65 } | 69 } |
| 66 | 70 |
| 71 int formal_arguments() { |
| 72 // Both, {JSCallFunction} and {JSCallConstruct}, have two extra inputs: |
| 73 // - JSCallConstruct: Includes target function and original constructor. |
| 74 // - JSCallFunction: Includes target function and receiver. |
| 75 return call_->op()->ValueInputCount() - 2; |
| 76 } |
| 77 |
| 67 private: | 78 private: |
| 68 Node* call_; | 79 Node* call_; |
| 69 }; | 80 }; |
| 70 | 81 |
| 71 | 82 |
| 72 class CopyVisitor { | 83 class CopyVisitor { |
| 73 public: | 84 public: |
| 74 CopyVisitor(Graph* source_graph, Graph* target_graph, Zone* temp_zone) | 85 CopyVisitor(Graph* source_graph, Graph* target_graph, Zone* temp_zone) |
| 75 : sentinel_op_(IrOpcode::kDead, Operator::kNoProperties, "Sentinel", 0, 0, | 86 : sentinel_op_(IrOpcode::kDead, Operator::kNoProperties, "Sentinel", 0, 0, |
| 76 0, 0, 0, 0), | 87 0, 0, 0, 0), |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 static_cast<int>(effects.size()), &effects.front()); | 231 static_cast<int>(effects.size()), &effects.front()); |
| 221 ReplaceWithValue(call, value_output, effect_output, control_output); | 232 ReplaceWithValue(call, value_output, effect_output, control_output); |
| 222 return Changed(value_output); | 233 return Changed(value_output); |
| 223 } else { | 234 } else { |
| 224 ReplaceWithValue(call, call, call, jsgraph_->Dead()); | 235 ReplaceWithValue(call, call, call, jsgraph_->Dead()); |
| 225 return Changed(call); | 236 return Changed(call); |
| 226 } | 237 } |
| 227 } | 238 } |
| 228 | 239 |
| 229 | 240 |
| 230 Node* JSInliner::CreateArgumentsAdaptorFrameState( | 241 Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state, |
| 231 JSCallFunctionAccessor* call, Handle<SharedFunctionInfo> shared_info) { | 242 int parameter_count, |
| 243 FrameStateType frame_state_type, |
| 244 Handle<SharedFunctionInfo> shared) { |
| 232 const FrameStateFunctionInfo* state_info = | 245 const FrameStateFunctionInfo* state_info = |
| 233 jsgraph_->common()->CreateFrameStateFunctionInfo( | 246 jsgraph_->common()->CreateFrameStateFunctionInfo( |
| 234 FrameStateType::kArgumentsAdaptor, | 247 frame_state_type, parameter_count + 1, 0, shared, |
| 235 static_cast<int>(call->formal_arguments()) + 1, 0, shared_info, | |
| 236 CALL_MAINTAINS_NATIVE_CONTEXT); | 248 CALL_MAINTAINS_NATIVE_CONTEXT); |
| 237 | 249 |
| 238 const Operator* op = jsgraph_->common()->FrameState( | 250 const Operator* op = jsgraph_->common()->FrameState( |
| 239 BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info); | 251 BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info); |
| 240 const Operator* op0 = jsgraph_->common()->StateValues(0); | 252 const Operator* op0 = jsgraph_->common()->StateValues(0); |
| 241 Node* node0 = jsgraph_->graph()->NewNode(op0); | 253 Node* node0 = jsgraph_->graph()->NewNode(op0); |
| 242 NodeVector params(local_zone_); | 254 NodeVector params(local_zone_); |
| 243 params.push_back(call->receiver()); | 255 for (int parameter = 0; parameter < parameter_count + 1; ++parameter) { |
| 244 for (size_t argument = 0; argument != call->formal_arguments(); ++argument) { | 256 params.push_back(node->InputAt(1 + parameter)); |
| 245 params.push_back(call->formal_argument(argument)); | |
| 246 } | 257 } |
| 247 const Operator* op_param = | 258 const Operator* op_param = |
| 248 jsgraph_->common()->StateValues(static_cast<int>(params.size())); | 259 jsgraph_->common()->StateValues(static_cast<int>(params.size())); |
| 249 Node* params_node = jsgraph_->graph()->NewNode( | 260 Node* params_node = jsgraph_->graph()->NewNode( |
| 250 op_param, static_cast<int>(params.size()), ¶ms.front()); | 261 op_param, static_cast<int>(params.size()), ¶ms.front()); |
| 251 return jsgraph_->graph()->NewNode( | 262 return jsgraph_->graph()->NewNode(op, params_node, node0, node0, |
| 252 op, params_node, node0, node0, jsgraph_->UndefinedConstant(), | 263 jsgraph_->UndefinedConstant(), |
| 253 call->jsfunction(), call->frame_state_after()); | 264 node->InputAt(0), outer_frame_state); |
| 254 } | 265 } |
| 255 | 266 |
| 256 | 267 |
| 257 Reduction JSInliner::Reduce(Node* node) { | 268 Reduction JSInliner::Reduce(Node* node) { |
| 258 if (node->opcode() != IrOpcode::kJSCallFunction) return NoChange(); | 269 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); |
| 259 | 270 |
| 260 JSCallFunctionAccessor call(node); | 271 // This reducer can handle both normal function calls as well a constructor |
| 261 HeapObjectMatcher match(call.jsfunction()); | 272 // calls whenever the target is a constant function object, as follows: |
| 273 // - JSCallFunction(target:constant, receiver, args...) |
| 274 // - JSCallConstruct(target:constant, args..., new.target) |
| 275 HeapObjectMatcher match(node->InputAt(0)); |
| 262 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); | 276 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); |
| 263 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); | 277 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); |
| 264 | 278 |
| 265 return ReduceJSCallFunction(node, function); | 279 return ReduceJSCall(node, function); |
| 266 } | 280 } |
| 267 | 281 |
| 268 | 282 |
| 269 Reduction JSInliner::ReduceJSCallFunction(Node* node, | 283 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { |
| 270 Handle<JSFunction> function) { | 284 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); |
| 271 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); | 285 JSCallAccessor call(node); |
| 272 JSCallFunctionAccessor call(node); | |
| 273 | 286 |
| 274 if (!function->shared()->IsInlineable()) { | 287 if (!function->shared()->IsInlineable()) { |
| 275 // Function must be inlineable. | 288 // Function must be inlineable. |
| 276 TRACE("Not inlining %s into %s because callee is not inlineable\n", | 289 TRACE("Not inlining %s into %s because callee is not inlineable\n", |
| 277 function->shared()->DebugName()->ToCString().get(), | 290 function->shared()->DebugName()->ToCString().get(), |
| 278 info_->shared_info()->DebugName()->ToCString().get()); | 291 info_->shared_info()->DebugName()->ToCString().get()); |
| 279 return NoChange(); | 292 return NoChange(); |
| 280 } | 293 } |
| 281 | 294 |
| 282 // Class constructors are callable, but [[Call]] will raise an exception. | 295 // Class constructors are callable, but [[Call]] will raise an exception. |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 function->shared()->DebugName()->ToCString().get(), | 365 function->shared()->DebugName()->ToCString().get(), |
| 353 info_->shared_info()->DebugName()->ToCString().get()); | 366 info_->shared_info()->DebugName()->ToCString().get()); |
| 354 if (info_->isolate()->has_pending_exception()) { | 367 if (info_->isolate()->has_pending_exception()) { |
| 355 info_->isolate()->clear_pending_exception(); | 368 info_->isolate()->clear_pending_exception(); |
| 356 } | 369 } |
| 357 return NoChange(); | 370 return NoChange(); |
| 358 } | 371 } |
| 359 | 372 |
| 360 // In strong mode, in case of too few arguments we need to throw a TypeError | 373 // In strong mode, in case of too few arguments we need to throw a TypeError |
| 361 // so we must not inline this call. | 374 // so we must not inline this call. |
| 362 size_t parameter_count = info.literal()->parameter_count(); | 375 int parameter_count = info.literal()->parameter_count(); |
| 363 if (is_strong(info.language_mode()) && | 376 if (is_strong(info.language_mode()) && |
| 364 call.formal_arguments() < parameter_count) { | 377 call.formal_arguments() < parameter_count) { |
| 365 TRACE("Not inlining %s into %s because too few arguments for strong mode\n", | 378 TRACE("Not inlining %s into %s because too few arguments for strong mode\n", |
| 366 function->shared()->DebugName()->ToCString().get(), | 379 function->shared()->DebugName()->ToCString().get(), |
| 367 info_->shared_info()->DebugName()->ToCString().get()); | 380 info_->shared_info()->DebugName()->ToCString().get()); |
| 368 return NoChange(); | 381 return NoChange(); |
| 369 } | 382 } |
| 370 | 383 |
| 371 if (!Compiler::EnsureDeoptimizationSupport(&info)) { | 384 if (!Compiler::EnsureDeoptimizationSupport(&info)) { |
| 372 TRACE("Not inlining %s into %s because deoptimization support failed\n", | 385 TRACE("Not inlining %s into %s because deoptimization support failed\n", |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 418 : JSNativeContextSpecialization::kNoFlags, | 431 : JSNativeContextSpecialization::kNoFlags, |
| 419 handle(info.global_object()->native_context(), info.isolate()), | 432 handle(info.global_object()->native_context(), info.isolate()), |
| 420 info_->dependencies(), local_zone_); | 433 info_->dependencies(), local_zone_); |
| 421 graph_reducer.AddReducer(&dead_code_elimination); | 434 graph_reducer.AddReducer(&dead_code_elimination); |
| 422 graph_reducer.AddReducer(&common_reducer); | 435 graph_reducer.AddReducer(&common_reducer); |
| 423 graph_reducer.AddReducer(&global_object_specialization); | 436 graph_reducer.AddReducer(&global_object_specialization); |
| 424 graph_reducer.AddReducer(&native_context_specialization); | 437 graph_reducer.AddReducer(&native_context_specialization); |
| 425 graph_reducer.ReduceGraph(); | 438 graph_reducer.ReduceGraph(); |
| 426 } | 439 } |
| 427 | 440 |
| 428 // The inlinee specializes to the context from the JSFunction object. | |
| 429 // TODO(turbofan): We might want to load the context from the JSFunction at | |
| 430 // runtime in case we only know the SharedFunctionInfo once we have dynamic | |
| 431 // type feedback in the compiler. | |
| 432 Node* context = jsgraph_->Constant(handle(function->context())); | |
| 433 | |
| 434 CopyVisitor visitor(&graph, jsgraph_->graph(), &zone); | 441 CopyVisitor visitor(&graph, jsgraph_->graph(), &zone); |
| 435 visitor.CopyGraph(); | 442 visitor.CopyGraph(); |
| 436 | 443 |
| 437 Node* start = visitor.GetCopy(graph.start()); | 444 Node* start = visitor.GetCopy(graph.start()); |
| 438 Node* end = visitor.GetCopy(graph.end()); | 445 Node* end = visitor.GetCopy(graph.end()); |
| 439 Node* frame_state = call.frame_state_after(); | 446 Node* frame_state = call.frame_state_after(); |
| 440 | 447 |
| 448 // Insert nodes around the call that model the behavior required for a |
| 449 // constructor dispatch and turn the constructor call into a regular call. |
| 450 // This models the behavior usually accomplished by our {JSConstructStub}. |
| 451 // Note that the context has to be the callers context (input to call node). |
| 452 if (node->opcode() == IrOpcode::kJSCallConstruct) { |
| 453 Node* effect = NodeProperties::GetEffectInput(node); |
| 454 Node* context = NodeProperties::GetContextInput(node); |
| 455 Node* create = jsgraph_->graph()->NewNode(jsgraph_->javascript()->Create(), |
| 456 call.target(), context, effect); |
| 457 NodeProperties::ReplaceEffectInput(node, create); |
| 458 // TODO(4544): For now {JSCreate} requires the actual constructor to |
| 459 // coincide with the original constructor. Adapt JSGenericLowering to fix. |
| 460 // Also Runtime_GetOriginalConstructor depends on this for now. Fix as well! |
| 461 CHECK_EQ(call.target(), call.original_constructor()); |
| 462 // TODO(4544): For derived constructors we should not allocate an implicit |
| 463 // receiver and also the return value should not be checked afterwards. |
| 464 CHECK(!IsClassConstructor(function->shared()->kind())); |
| 465 // Swizzle the inputs of the {JSCallConstruct} node to look like inputs to |
| 466 // any {JSCallFunction} node so that the rest of the inlining machinery |
| 467 // behaves as if we were dealing with a regular function invocation. |
| 468 node->RemoveInput(call.formal_arguments() + 1); // Drop new.target. |
| 469 node->InsertInput(jsgraph_->graph()->zone(), 1, create); |
| 470 // Insert a check of the return value to determine whether the return value |
| 471 // or the implicit receiver should be selected as a result of the call. |
| 472 Node* check = jsgraph_->graph()->NewNode( |
| 473 jsgraph_->javascript()->CallRuntime(Runtime::kInlineIsSpecObject, 1), |
| 474 node, context, node, start); |
| 475 Node* select = jsgraph_->graph()->NewNode( |
| 476 jsgraph_->common()->Select(kMachAnyTagged), check, node, create); |
| 477 NodeProperties::ReplaceUses(node, select, check, node, node); |
| 478 NodeProperties::ReplaceValueInput(select, node, 1); |
| 479 NodeProperties::ReplaceValueInput(check, node, 0); |
| 480 NodeProperties::ReplaceEffectInput(check, node); |
| 481 // Insert a construct stub frame into the chain of frame states. This will |
| 482 // reconstruct the proper frame when deoptimizing within the constructor. |
| 483 frame_state = CreateArtificialFrameState( |
| 484 node, frame_state, call.formal_arguments(), |
| 485 FrameStateType::kConstructStub, info.shared_info()); |
| 486 } |
| 487 |
| 488 // The inlinee specializes to the context from the JSFunction object. |
| 489 // TODO(turbofan): We might want to load the context from the JSFunction at |
| 490 // runtime in case we only know the SharedFunctionInfo once we have dynamic |
| 491 // type feedback in the compiler. |
| 492 Node* context = jsgraph_->Constant(handle(function->context())); |
| 493 |
| 441 // Insert a JSConvertReceiver node for sloppy callees. Note that the context | 494 // Insert a JSConvertReceiver node for sloppy callees. Note that the context |
| 442 // passed into this node has to be the callees context (loaded above). Note | 495 // passed into this node has to be the callees context (loaded above). Note |
| 443 // that the frame state passed to the JSConvertReceiver must be the frame | 496 // that the frame state passed to the JSConvertReceiver must be the frame |
| 444 // state _before_ the call; it is not necessary to fiddle with the receiver | 497 // state _before_ the call; it is not necessary to fiddle with the receiver |
| 445 // in that frame state tho, as the conversion of the receiver can be repeated | 498 // in that frame state tho, as the conversion of the receiver can be repeated |
| 446 // any number of times, it's not observable. | 499 // any number of times, it's not observable. |
| 447 if (is_sloppy(info.language_mode()) && !function->shared()->native()) { | 500 if (node->opcode() == IrOpcode::kJSCallFunction && |
| 501 is_sloppy(info.language_mode()) && !function->shared()->native()) { |
| 448 const CallFunctionParameters& p = CallFunctionParametersOf(node->op()); | 502 const CallFunctionParameters& p = CallFunctionParametersOf(node->op()); |
| 449 Node* effect = NodeProperties::GetEffectInput(node); | 503 Node* effect = NodeProperties::GetEffectInput(node); |
| 450 Node* convert = jsgraph_->graph()->NewNode( | 504 Node* convert = jsgraph_->graph()->NewNode( |
| 451 jsgraph_->javascript()->ConvertReceiver(p.convert_mode()), | 505 jsgraph_->javascript()->ConvertReceiver(p.convert_mode()), |
| 452 call.receiver(), context, call.frame_state_before(), effect, start); | 506 call.receiver(), context, call.frame_state_before(), effect, start); |
| 453 NodeProperties::ReplaceValueInput(node, convert, 1); | 507 NodeProperties::ReplaceValueInput(node, convert, 1); |
| 454 NodeProperties::ReplaceEffectInput(node, convert); | 508 NodeProperties::ReplaceEffectInput(node, convert); |
| 455 } | 509 } |
| 456 | 510 |
| 457 // Insert argument adaptor frame if required. The callees formal parameter | 511 // Insert argument adaptor frame if required. The callees formal parameter |
| 458 // count (i.e. value outputs of start node minus target, receiver, num args | 512 // count (i.e. value outputs of start node minus target, receiver, num args |
| 459 // and context) have to match the number of arguments passed to the call. | 513 // and context) have to match the number of arguments passed to the call. |
| 460 DCHECK_EQ(static_cast<int>(parameter_count), | 514 DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 4); |
| 461 start->op()->ValueOutputCount() - 4); | |
| 462 if (call.formal_arguments() != parameter_count) { | 515 if (call.formal_arguments() != parameter_count) { |
| 463 frame_state = CreateArgumentsAdaptorFrameState(&call, info.shared_info()); | 516 frame_state = CreateArtificialFrameState( |
| 517 node, frame_state, call.formal_arguments(), |
| 518 FrameStateType::kArgumentsAdaptor, info.shared_info()); |
| 464 } | 519 } |
| 465 | 520 |
| 466 return InlineCall(node, context, frame_state, start, end); | 521 return InlineCall(node, context, frame_state, start, end); |
| 467 } | 522 } |
| 468 | 523 |
| 469 } // namespace compiler | 524 } // namespace compiler |
| 470 } // namespace internal | 525 } // namespace internal |
| 471 } // namespace v8 | 526 } // namespace v8 |
| OLD | NEW |