Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(166)

Side by Side Diff: src/compiler/js-inlining.cc

Issue 1435873002: [turbofan] Initial support for constructor call inlining. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Rebased. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/compiler/js-inlining.h ('k') | src/compiler/js-inlining-heuristic.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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()), &params.front()); 261 op_param, static_cast<int>(params.size()), &params.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
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
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
OLDNEW
« no previous file with comments | « src/compiler/js-inlining.h ('k') | src/compiler/js-inlining-heuristic.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698