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.h" | 7 #include "src/ast/ast.h" |
8 #include "src/ast/ast-numbering.h" | 8 #include "src/ast/ast-numbering.h" |
9 #include "src/ast/scopes.h" | 9 #include "src/ast/scopes.h" |
10 #include "src/compiler.h" | 10 #include "src/compiler.h" |
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 op_param, static_cast<int>(params.size()), ¶ms.front()); | 261 op_param, static_cast<int>(params.size()), ¶ms.front()); |
262 return jsgraph_->graph()->NewNode(op, params_node, node0, node0, | 262 return jsgraph_->graph()->NewNode(op, params_node, node0, node0, |
263 jsgraph_->UndefinedConstant(), | 263 jsgraph_->UndefinedConstant(), |
264 node->InputAt(0), outer_frame_state); | 264 node->InputAt(0), outer_frame_state); |
265 } | 265 } |
266 | 266 |
267 | 267 |
268 namespace { | 268 namespace { |
269 | 269 |
270 // TODO(mstarzinger,verwaest): Move this predicate onto SharedFunctionInfo? | 270 // TODO(mstarzinger,verwaest): Move this predicate onto SharedFunctionInfo? |
271 bool NeedsImplicitReceiver(Handle<JSFunction> function, Isolate* isolate) { | 271 bool NeedsImplicitReceiver(Handle<SharedFunctionInfo> shared_info) { |
272 Code* construct_stub = function->shared()->construct_stub(); | 272 DisallowHeapAllocation no_gc; |
273 return construct_stub != *isolate->builtins()->JSBuiltinsConstructStub() && | 273 Isolate* const isolate = shared_info->GetIsolate(); |
274 construct_stub != *isolate->builtins()->ConstructedNonConstructable(); | 274 Code* const construct_stub = shared_info->construct_stub(); |
| 275 return construct_stub != *isolate->builtins()->JSBuiltinsConstructStub(); |
| 276 } |
| 277 |
| 278 bool IsNonConstructible(Handle<SharedFunctionInfo> shared_info) { |
| 279 DisallowHeapAllocation no_gc; |
| 280 Isolate* const isolate = shared_info->GetIsolate(); |
| 281 Code* const construct_stub = shared_info->construct_stub(); |
| 282 return construct_stub == *isolate->builtins()->ConstructedNonConstructable(); |
275 } | 283 } |
276 | 284 |
277 } // namespace | 285 } // namespace |
278 | 286 |
279 | 287 |
280 Reduction JSInliner::Reduce(Node* node) { | 288 Reduction JSInliner::Reduce(Node* node) { |
281 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); | 289 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); |
282 | 290 |
283 // This reducer can handle both normal function calls as well a constructor | 291 // This reducer can handle both normal function calls as well a constructor |
284 // calls whenever the target is a constant function object, as follows: | 292 // calls whenever the target is a constant function object, as follows: |
285 // - JSCallFunction(target:constant, receiver, args...) | 293 // - JSCallFunction(target:constant, receiver, args...) |
286 // - JSCallConstruct(target:constant, args..., new.target) | 294 // - JSCallConstruct(target:constant, args..., new.target) |
287 HeapObjectMatcher match(node->InputAt(0)); | 295 HeapObjectMatcher match(node->InputAt(0)); |
288 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); | 296 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); |
289 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); | 297 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); |
290 | 298 |
291 return ReduceJSCall(node, function); | 299 return ReduceJSCall(node, function); |
292 } | 300 } |
293 | 301 |
294 | 302 |
295 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { | 303 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { |
296 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); | 304 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); |
297 JSCallAccessor call(node); | 305 JSCallAccessor call(node); |
| 306 Handle<SharedFunctionInfo> shared_info(function->shared()); |
298 | 307 |
299 // Function must be inlineable. | 308 // Function must be inlineable. |
300 if (!function->shared()->IsInlineable()) { | 309 if (!shared_info->IsInlineable()) { |
301 TRACE("Not inlining %s into %s because callee is not inlineable\n", | 310 TRACE("Not inlining %s into %s because callee is not inlineable\n", |
302 function->shared()->DebugName()->ToCString().get(), | 311 shared_info->DebugName()->ToCString().get(), |
303 info_->shared_info()->DebugName()->ToCString().get()); | 312 info_->shared_info()->DebugName()->ToCString().get()); |
304 return NoChange(); | 313 return NoChange(); |
305 } | 314 } |
306 | 315 |
307 // Constructor must be constructable. | 316 // Constructor must be constructable. |
308 if (node->opcode() == IrOpcode::kJSCallConstruct && | 317 if (node->opcode() == IrOpcode::kJSCallConstruct && |
309 !function->IsConstructor()) { | 318 IsNonConstructible(shared_info)) { |
310 TRACE("Not inlining %s into %s because constructor is not constructable.\n", | 319 TRACE("Not inlining %s into %s because constructor is not constructable.\n", |
311 function->shared()->DebugName()->ToCString().get(), | 320 shared_info->DebugName()->ToCString().get(), |
312 info_->shared_info()->DebugName()->ToCString().get()); | 321 info_->shared_info()->DebugName()->ToCString().get()); |
313 return NoChange(); | 322 return NoChange(); |
314 } | 323 } |
315 | 324 |
316 // Class constructors are callable, but [[Call]] will raise an exception. | 325 // Class constructors are callable, but [[Call]] will raise an exception. |
317 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). | 326 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). |
318 if (node->opcode() == IrOpcode::kJSCallFunction && | 327 if (node->opcode() == IrOpcode::kJSCallFunction && |
319 IsClassConstructor(function->shared()->kind())) { | 328 IsClassConstructor(shared_info->kind())) { |
320 TRACE("Not inlining %s into %s because callee is a class constructor.\n", | 329 TRACE("Not inlining %s into %s because callee is a class constructor.\n", |
321 function->shared()->DebugName()->ToCString().get(), | 330 shared_info->DebugName()->ToCString().get(), |
322 info_->shared_info()->DebugName()->ToCString().get()); | 331 info_->shared_info()->DebugName()->ToCString().get()); |
323 return NoChange(); | 332 return NoChange(); |
324 } | 333 } |
325 | 334 |
326 // Function contains break points. | 335 // Function contains break points. |
327 if (function->shared()->HasDebugInfo()) { | 336 if (shared_info->HasDebugInfo()) { |
328 TRACE("Not inlining %s into %s because callee may contain break points\n", | 337 TRACE("Not inlining %s into %s because callee may contain break points\n", |
329 function->shared()->DebugName()->ToCString().get(), | 338 shared_info->DebugName()->ToCString().get(), |
330 info_->shared_info()->DebugName()->ToCString().get()); | 339 info_->shared_info()->DebugName()->ToCString().get()); |
331 return NoChange(); | 340 return NoChange(); |
332 } | 341 } |
333 | 342 |
334 // Disallow cross native-context inlining for now. This means that all parts | 343 // Disallow cross native-context inlining for now. This means that all parts |
335 // of the resulting code will operate on the same global object. | 344 // of the resulting code will operate on the same global object. |
336 // This also prevents cross context leaks for asm.js code, where we could | 345 // This also prevents cross context leaks for asm.js code, where we could |
337 // inline functions from a different context and hold on to that context (and | 346 // inline functions from a different context and hold on to that context (and |
338 // closure) from the code object. | 347 // closure) from the code object. |
339 // TODO(turbofan): We might want to revisit this restriction later when we | 348 // TODO(turbofan): We might want to revisit this restriction later when we |
340 // have a need for this, and we know how to model different native contexts | 349 // have a need for this, and we know how to model different native contexts |
341 // in the same graph in a compositional way. | 350 // in the same graph in a compositional way. |
342 if (function->context()->native_context() != | 351 if (function->context()->native_context() != |
343 info_->context()->native_context()) { | 352 info_->context()->native_context()) { |
344 TRACE("Not inlining %s into %s because of different native contexts\n", | 353 TRACE("Not inlining %s into %s because of different native contexts\n", |
345 function->shared()->DebugName()->ToCString().get(), | 354 shared_info->DebugName()->ToCString().get(), |
346 info_->shared_info()->DebugName()->ToCString().get()); | 355 info_->shared_info()->DebugName()->ToCString().get()); |
347 return NoChange(); | 356 return NoChange(); |
348 } | 357 } |
349 | 358 |
350 // TODO(turbofan): TranslatedState::GetAdaptedArguments() currently relies on | 359 // TODO(turbofan): TranslatedState::GetAdaptedArguments() currently relies on |
351 // not inlining recursive functions. We might want to relax that at some | 360 // not inlining recursive functions. We might want to relax that at some |
352 // point. | 361 // point. |
353 for (Node* frame_state = call.frame_state_after(); | 362 for (Node* frame_state = call.frame_state_after(); |
354 frame_state->opcode() == IrOpcode::kFrameState; | 363 frame_state->opcode() == IrOpcode::kFrameState; |
355 frame_state = frame_state->InputAt(kFrameStateOuterStateInput)) { | 364 frame_state = frame_state->InputAt(kFrameStateOuterStateInput)) { |
356 FrameStateInfo const& info = OpParameter<FrameStateInfo>(frame_state); | 365 FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state); |
357 Handle<SharedFunctionInfo> shared_info; | 366 Handle<SharedFunctionInfo> frame_shared_info; |
358 if (info.shared_info().ToHandle(&shared_info) && | 367 if (frame_info.shared_info().ToHandle(&frame_shared_info) && |
359 *shared_info == function->shared()) { | 368 *frame_shared_info == *shared_info) { |
360 TRACE("Not inlining %s into %s because call is recursive\n", | 369 TRACE("Not inlining %s into %s because call is recursive\n", |
361 function->shared()->DebugName()->ToCString().get(), | 370 shared_info->DebugName()->ToCString().get(), |
362 info_->shared_info()->DebugName()->ToCString().get()); | 371 info_->shared_info()->DebugName()->ToCString().get()); |
363 return NoChange(); | 372 return NoChange(); |
364 } | 373 } |
365 } | 374 } |
366 | 375 |
367 // TODO(turbofan): Inlining into a try-block is not yet supported. | 376 // TODO(turbofan): Inlining into a try-block is not yet supported. |
368 if (NodeProperties::IsExceptionalCall(node)) { | 377 if (NodeProperties::IsExceptionalCall(node)) { |
369 TRACE("Not inlining %s into %s because of surrounding try-block\n", | 378 TRACE("Not inlining %s into %s because of surrounding try-block\n", |
370 function->shared()->DebugName()->ToCString().get(), | 379 shared_info->DebugName()->ToCString().get(), |
371 info_->shared_info()->DebugName()->ToCString().get()); | 380 info_->shared_info()->DebugName()->ToCString().get()); |
372 return NoChange(); | 381 return NoChange(); |
373 } | 382 } |
374 | 383 |
375 Zone zone; | 384 Zone zone; |
376 ParseInfo parse_info(&zone, function); | 385 ParseInfo parse_info(&zone, function); |
377 CompilationInfo info(&parse_info); | 386 CompilationInfo info(&parse_info); |
378 if (info_->is_deoptimization_enabled()) { | 387 if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled(); |
379 info.MarkAsDeoptimizationEnabled(); | |
380 } | |
381 | 388 |
382 if (!Compiler::ParseAndAnalyze(info.parse_info())) { | 389 if (!Compiler::ParseAndAnalyze(info.parse_info())) { |
383 TRACE("Not inlining %s into %s because parsing failed\n", | 390 TRACE("Not inlining %s into %s because parsing failed\n", |
384 function->shared()->DebugName()->ToCString().get(), | 391 shared_info->DebugName()->ToCString().get(), |
385 info_->shared_info()->DebugName()->ToCString().get()); | 392 info_->shared_info()->DebugName()->ToCString().get()); |
386 if (info_->isolate()->has_pending_exception()) { | 393 if (info_->isolate()->has_pending_exception()) { |
387 info_->isolate()->clear_pending_exception(); | 394 info_->isolate()->clear_pending_exception(); |
388 } | 395 } |
389 return NoChange(); | 396 return NoChange(); |
390 } | 397 } |
391 | 398 |
392 // In strong mode, in case of too few arguments we need to throw a TypeError | 399 // In strong mode, in case of too few arguments we need to throw a TypeError |
393 // so we must not inline this call. | 400 // so we must not inline this call. |
394 int parameter_count = info.literal()->parameter_count(); | 401 int parameter_count = info.literal()->parameter_count(); |
395 if (is_strong(info.language_mode()) && | 402 if (is_strong(info.language_mode()) && |
396 call.formal_arguments() < parameter_count) { | 403 call.formal_arguments() < parameter_count) { |
397 TRACE("Not inlining %s into %s because too few arguments for strong mode\n", | 404 TRACE("Not inlining %s into %s because too few arguments for strong mode\n", |
398 function->shared()->DebugName()->ToCString().get(), | 405 shared_info->DebugName()->ToCString().get(), |
399 info_->shared_info()->DebugName()->ToCString().get()); | 406 info_->shared_info()->DebugName()->ToCString().get()); |
400 return NoChange(); | 407 return NoChange(); |
401 } | 408 } |
402 | 409 |
403 if (!Compiler::EnsureDeoptimizationSupport(&info)) { | 410 if (!Compiler::EnsureDeoptimizationSupport(&info)) { |
404 TRACE("Not inlining %s into %s because deoptimization support failed\n", | 411 TRACE("Not inlining %s into %s because deoptimization support failed\n", |
405 function->shared()->DebugName()->ToCString().get(), | 412 shared_info->DebugName()->ToCString().get(), |
406 info_->shared_info()->DebugName()->ToCString().get()); | 413 info_->shared_info()->DebugName()->ToCString().get()); |
407 return NoChange(); | 414 return NoChange(); |
408 } | 415 } |
409 // Remember that we inlined this function. This needs to be called right | 416 // Remember that we inlined this function. This needs to be called right |
410 // after we ensure deoptimization support so that the code flusher | 417 // after we ensure deoptimization support so that the code flusher |
411 // does not remove the code with the deoptimization support. | 418 // does not remove the code with the deoptimization support. |
412 info_->AddInlinedFunction(info.shared_info()); | 419 info_->AddInlinedFunction(shared_info); |
413 | 420 |
414 // ---------------------------------------------------------------- | 421 // ---------------------------------------------------------------- |
415 // After this point, we've made a decision to inline this function. | 422 // After this point, we've made a decision to inline this function. |
416 // We shall not bailout from inlining if we got here. | 423 // We shall not bailout from inlining if we got here. |
417 | 424 |
418 TRACE("Inlining %s into %s\n", | 425 TRACE("Inlining %s into %s\n", |
419 function->shared()->DebugName()->ToCString().get(), | 426 shared_info->DebugName()->ToCString().get(), |
420 info_->shared_info()->DebugName()->ToCString().get()); | 427 info_->shared_info()->DebugName()->ToCString().get()); |
421 | 428 |
422 // TODO(mstarzinger): We could use the temporary zone for the graph because | 429 // TODO(mstarzinger): We could use the temporary zone for the graph because |
423 // nodes are copied. This however leads to Zone-Types being allocated in the | 430 // nodes are copied. This however leads to Zone-Types being allocated in the |
424 // wrong zone and makes the engine explode at high speeds. Explosion bad! | 431 // wrong zone and makes the engine explode at high speeds. Explosion bad! |
425 Graph graph(jsgraph_->zone()); | 432 Graph graph(jsgraph_->zone()); |
426 JSGraph jsgraph(info.isolate(), &graph, jsgraph_->common(), | 433 JSGraph jsgraph(info.isolate(), &graph, jsgraph_->common(), |
427 jsgraph_->javascript(), jsgraph_->simplified(), | 434 jsgraph_->javascript(), jsgraph_->simplified(), |
428 jsgraph_->machine()); | 435 jsgraph_->machine()); |
429 AstGraphBuilder graph_builder(local_zone_, &info, &jsgraph); | 436 AstGraphBuilder graph_builder(local_zone_, &info, &jsgraph); |
430 graph_builder.CreateGraph(false); | 437 graph_builder.CreateGraph(false); |
431 | 438 |
432 CopyVisitor visitor(&graph, jsgraph_->graph(), &zone); | 439 CopyVisitor visitor(&graph, jsgraph_->graph(), &zone); |
433 visitor.CopyGraph(); | 440 visitor.CopyGraph(); |
434 | 441 |
435 Node* start = visitor.GetCopy(graph.start()); | 442 Node* start = visitor.GetCopy(graph.start()); |
436 Node* end = visitor.GetCopy(graph.end()); | 443 Node* end = visitor.GetCopy(graph.end()); |
437 Node* frame_state = call.frame_state_after(); | 444 Node* frame_state = call.frame_state_after(); |
438 Node* new_target = jsgraph_->UndefinedConstant(); | 445 Node* new_target = jsgraph_->UndefinedConstant(); |
439 | 446 |
440 // Insert nodes around the call that model the behavior required for a | 447 // Insert nodes around the call that model the behavior required for a |
441 // constructor dispatch (allocate implicit receiver and check return value). | 448 // constructor dispatch (allocate implicit receiver and check return value). |
442 // This models the behavior usually accomplished by our {JSConstructStub}. | 449 // This models the behavior usually accomplished by our {JSConstructStub}. |
443 // Note that the context has to be the callers context (input to call node). | 450 // Note that the context has to be the callers context (input to call node). |
444 Node* receiver = jsgraph_->UndefinedConstant(); // Implicit receiver. | 451 Node* receiver = jsgraph_->UndefinedConstant(); // Implicit receiver. |
445 if (node->opcode() == IrOpcode::kJSCallConstruct && | 452 if (node->opcode() == IrOpcode::kJSCallConstruct && |
446 NeedsImplicitReceiver(function, info_->isolate())) { | 453 NeedsImplicitReceiver(shared_info)) { |
447 Node* effect = NodeProperties::GetEffectInput(node); | 454 Node* effect = NodeProperties::GetEffectInput(node); |
448 Node* context = NodeProperties::GetContextInput(node); | 455 Node* context = NodeProperties::GetContextInput(node); |
449 Node* create = jsgraph_->graph()->NewNode( | 456 Node* create = jsgraph_->graph()->NewNode( |
450 jsgraph_->javascript()->Create(), call.target(), call.new_target(), | 457 jsgraph_->javascript()->Create(), call.target(), call.new_target(), |
451 context, call.frame_state_before(), effect); | 458 context, call.frame_state_before(), effect); |
452 NodeProperties::ReplaceEffectInput(node, create); | 459 NodeProperties::ReplaceEffectInput(node, create); |
453 // Insert a check of the return value to determine whether the return value | 460 // Insert a check of the return value to determine whether the return value |
454 // or the implicit receiver should be selected as a result of the call. | 461 // or the implicit receiver should be selected as a result of the call. |
455 Node* check = jsgraph_->graph()->NewNode( | 462 Node* check = jsgraph_->graph()->NewNode( |
456 jsgraph_->javascript()->CallRuntime(Runtime::kInlineIsJSReceiver, 1), | 463 jsgraph_->javascript()->CallRuntime(Runtime::kInlineIsJSReceiver, 1), |
(...skipping 28 matching lines...) Expand all Loading... |
485 // type feedback in the compiler. | 492 // type feedback in the compiler. |
486 Node* context = jsgraph_->Constant(handle(function->context())); | 493 Node* context = jsgraph_->Constant(handle(function->context())); |
487 | 494 |
488 // Insert a JSConvertReceiver node for sloppy callees. Note that the context | 495 // Insert a JSConvertReceiver node for sloppy callees. Note that the context |
489 // passed into this node has to be the callees context (loaded above). Note | 496 // passed into this node has to be the callees context (loaded above). Note |
490 // that the frame state passed to the JSConvertReceiver must be the frame | 497 // that the frame state passed to the JSConvertReceiver must be the frame |
491 // state _before_ the call; it is not necessary to fiddle with the receiver | 498 // state _before_ the call; it is not necessary to fiddle with the receiver |
492 // in that frame state tho, as the conversion of the receiver can be repeated | 499 // in that frame state tho, as the conversion of the receiver can be repeated |
493 // any number of times, it's not observable. | 500 // any number of times, it's not observable. |
494 if (node->opcode() == IrOpcode::kJSCallFunction && | 501 if (node->opcode() == IrOpcode::kJSCallFunction && |
495 is_sloppy(info.language_mode()) && !function->shared()->native()) { | 502 is_sloppy(info.language_mode()) && !shared_info->native()) { |
496 const CallFunctionParameters& p = CallFunctionParametersOf(node->op()); | 503 const CallFunctionParameters& p = CallFunctionParametersOf(node->op()); |
497 Node* effect = NodeProperties::GetEffectInput(node); | 504 Node* effect = NodeProperties::GetEffectInput(node); |
498 Node* convert = jsgraph_->graph()->NewNode( | 505 Node* convert = jsgraph_->graph()->NewNode( |
499 jsgraph_->javascript()->ConvertReceiver(p.convert_mode()), | 506 jsgraph_->javascript()->ConvertReceiver(p.convert_mode()), |
500 call.receiver(), context, call.frame_state_before(), effect, start); | 507 call.receiver(), context, call.frame_state_before(), effect, start); |
501 NodeProperties::ReplaceValueInput(node, convert, 1); | 508 NodeProperties::ReplaceValueInput(node, convert, 1); |
502 NodeProperties::ReplaceEffectInput(node, convert); | 509 NodeProperties::ReplaceEffectInput(node, convert); |
503 } | 510 } |
504 | 511 |
505 // Insert argument adaptor frame if required. The callees formal parameter | 512 // Insert argument adaptor frame if required. The callees formal parameter |
506 // count (i.e. value outputs of start node minus target, receiver, new target, | 513 // count (i.e. value outputs of start node minus target, receiver, new target, |
507 // arguments count and context) have to match the number of arguments passed | 514 // arguments count and context) have to match the number of arguments passed |
508 // to the call. | 515 // to the call. |
509 DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5); | 516 DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5); |
510 if (call.formal_arguments() != parameter_count) { | 517 if (call.formal_arguments() != parameter_count) { |
511 frame_state = CreateArtificialFrameState( | 518 frame_state = CreateArtificialFrameState( |
512 node, frame_state, call.formal_arguments(), | 519 node, frame_state, call.formal_arguments(), |
513 FrameStateType::kArgumentsAdaptor, info.shared_info()); | 520 FrameStateType::kArgumentsAdaptor, shared_info); |
514 } | 521 } |
515 | 522 |
516 return InlineCall(node, new_target, context, frame_state, start, end); | 523 return InlineCall(node, new_target, context, frame_state, start, end); |
517 } | 524 } |
518 | 525 |
519 } // namespace compiler | 526 } // namespace compiler |
520 } // namespace internal | 527 } // namespace internal |
521 } // namespace v8 | 528 } // namespace v8 |
OLD | NEW |