OLD | NEW |
---|---|
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 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/interpreter/interpreter-assembler.h" | 5 #include "src/interpreter/interpreter-assembler.h" |
6 | 6 |
7 #include <limits> | 7 #include <limits> |
8 #include <ostream> | 8 #include <ostream> |
9 | 9 |
10 #include "src/code-factory.h" | 10 #include "src/code-factory.h" |
(...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
431 void InterpreterAssembler::CallEpilogue() { | 431 void InterpreterAssembler::CallEpilogue() { |
432 if (FLAG_debug_code && !disable_stack_check_across_call_) { | 432 if (FLAG_debug_code && !disable_stack_check_across_call_) { |
433 Node* stack_pointer_after_call = LoadStackPointer(); | 433 Node* stack_pointer_after_call = LoadStackPointer(); |
434 Node* stack_pointer_before_call = stack_pointer_before_call_; | 434 Node* stack_pointer_before_call = stack_pointer_before_call_; |
435 stack_pointer_before_call_ = nullptr; | 435 stack_pointer_before_call_ = nullptr; |
436 AbortIfWordNotEqual(stack_pointer_before_call, stack_pointer_after_call, | 436 AbortIfWordNotEqual(stack_pointer_before_call, stack_pointer_after_call, |
437 kUnexpectedStackPointer); | 437 kUnexpectedStackPointer); |
438 } | 438 } |
439 } | 439 } |
440 | 440 |
441 Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, | |
rmcilroy
2016/07/08 09:57:59
As discussed offline, if slot '0' is not a valid s
mythria
2016/07/11 15:25:24
Done.
| |
442 Node* first_arg, Node* arg_count, | |
443 Node* slot_id_smi, | |
rmcilroy
2016/07/08 09:57:59
You could pass slot_id as a non-smi, to avoid unta
mythria
2016/07/11 15:25:24
Done.
| |
444 Node* type_feedback_vector, | |
445 TailCallMode tail_call_mode) { | |
446 // Static checks to assert it is safe to examine the type feedback element. | |
447 // We don't know that we have a weak cell. We might have a private symbol | |
448 // or an AllocationSite, but the memory is safe to examine. | |
449 // AllocationSite::kTransitionInfoOffset - contains a Smi or pointer to | |
450 // FixedArray. | |
451 // WeakCell::kValueOffset - contains a JSFunction or Smi(0) | |
452 // Symbol::kHashFieldSlot - if the low bit is 1, then the hash is not | |
453 // computed, meaning that it can't appear to be a pointer. If the low bit is | |
454 // 0, then hash is computed, but the 0 bit prevents the field from appearing | |
455 // to be a pointer. | |
456 STATIC_ASSERT(WeakCell::kSize >= kPointerSize); | |
457 STATIC_ASSERT(AllocationSite::kTransitionInfoOffset == | |
458 WeakCell::kValueOffset && | |
459 WeakCell::kValueOffset == Symbol::kHashFieldSlot); | |
460 | |
461 Variable return_value(this, MachineRepresentation::kTagged); | |
462 Label handle_monomorphic(this), extra_checks(this), end(this), call(this); | |
463 | |
464 // The checks. First, does rdi match the recorded monomorphic target? | |
465 Node* slot_id = SmiUntag(slot_id_smi); | |
466 Node* feedback_element = LoadFixedArrayElement(type_feedback_vector, slot_id); | |
467 Node* feedback_value = LoadWeakCellValue(feedback_element); | |
468 Node* is_monomorphic = WordEqual(function, feedback_value); | |
469 BranchIf(is_monomorphic, &handle_monomorphic, &extra_checks); | |
470 | |
471 Bind(&handle_monomorphic); | |
472 { | |
473 // The compare above could have been a SMI/SMI comparison. Guard against | |
474 // this convincing us that we have a monomorphic JSFunction. | |
475 Node* is_smi = WordIsSmi(function); | |
476 GotoIf(is_smi, &extra_checks); | |
477 | |
478 // Increment the call count. | |
479 Node* call_count_slot = IntPtrAdd(slot_id, IntPtrConstant(1)); | |
480 Node* call_count = | |
481 LoadFixedArrayElement(type_feedback_vector, call_count_slot); | |
482 Node* new_count = SmiAdd(call_count, SmiTag(Int32Constant(1))); | |
483 // Count is Smi, so we don't need a write barrier. | |
484 StoreFixedArrayElement(type_feedback_vector, call_count_slot, new_count, | |
485 SKIP_WRITE_BARRIER); | |
486 | |
487 // Call using call function builtin. | |
488 Callable callable = CodeFactory::InterpreterPushArgsAndCall( | |
489 isolate(), tail_call_mode, true); | |
490 Node* code_target = HeapConstant(callable.code()); | |
491 Node* ret_value = CallStub(callable.descriptor(), code_target, context, | |
492 arg_count, first_arg, function); | |
493 return_value.Bind(ret_value); | |
494 Goto(&end); | |
495 } | |
496 | |
497 Bind(&extra_checks); | |
498 { | |
499 Label check_initialized(this, Label::kDeferred), mark_megamorphic(this); | |
500 // Check if it is a megamorphic target | |
501 Node* is_megamorphic = WordEqual( | |
502 feedback_element, | |
503 HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate()))); | |
504 BranchIf(is_megamorphic, &call, &check_initialized); | |
505 | |
506 Bind(&check_initialized); | |
507 { | |
508 Label possibly_monomorphic(this); | |
509 // Check if it is uninitialized. | |
510 Node* is_uninitialized = WordEqual( | |
511 feedback_element, | |
512 HeapConstant(TypeFeedbackVector::UninitializedSentinel(isolate()))); | |
513 GotoUnless(is_uninitialized, &mark_megamorphic); | |
514 | |
515 Node* is_smi = WordIsSmi(function); | |
516 GotoIf(is_smi, &mark_megamorphic); | |
517 | |
518 // Check if function is an object of JSFunction type | |
519 Node* instance_type = LoadInstanceType(function); | |
520 Node* is_js_function = | |
521 WordEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE)); | |
522 GotoUnless(is_js_function, &mark_megamorphic); | |
523 | |
524 // Check that it is not the Array() function. | |
525 Node* context_slot = | |
526 LoadFixedArrayElement(LoadNativeContext(context), | |
527 Int32Constant(Context::ARRAY_FUNCTION_INDEX)); | |
528 Node* is_array_function = WordEqual(context_slot, function); | |
529 GotoIf(is_array_function, &mark_megamorphic); | |
rmcilroy
2016/07/08 09:57:59
Benedikt, just want to check that it is correct to
Benedikt Meurer
2016/07/09 18:19:22
Yes, it's not ideal, but it's correct (and the sim
| |
530 | |
531 // Check if the function belongs to the same native context | |
532 Node* native_context = LoadNativeContext( | |
533 LoadObjectField(function, JSFunction::kContextOffset)); | |
534 Node* is_same_native_context = | |
535 WordEqual(native_context, LoadNativeContext(context)); | |
536 GotoUnless(is_same_native_context, &mark_megamorphic); | |
537 | |
538 // Initialize it to a monomorphic target. | |
539 Node* call_count_slot = IntPtrAdd(slot_id, IntPtrConstant(1)); | |
540 // Count is Smi, so we don't need a write barrier. | |
541 StoreFixedArrayElement(type_feedback_vector, call_count_slot, | |
542 SmiTag(Int32Constant(1)), SKIP_WRITE_BARRIER); | |
543 | |
544 CreateWeakCellStub weak_cell_stub(isolate()); | |
545 CallStub(weak_cell_stub.GetCallInterfaceDescriptor(), | |
546 HeapConstant(weak_cell_stub.GetCode()), context, | |
547 type_feedback_vector, slot_id_smi, function); | |
548 | |
549 // Call using call function builtin. | |
550 Callable callable = CodeFactory::InterpreterPushArgsAndCall( | |
551 isolate(), tail_call_mode, true); | |
552 Node* code_target = HeapConstant(callable.code()); | |
553 Node* ret_value = CallStub(callable.descriptor(), code_target, context, | |
554 arg_count, first_arg, function); | |
555 return_value.Bind(ret_value); | |
556 Goto(&end); | |
557 } | |
558 | |
559 Bind(&mark_megamorphic); | |
560 { | |
561 // Mark it as a megamorphic. | |
562 // MegamorphicSentinel is created as a part of Heap::InitialObjects | |
563 // and will not move during a GC. So it is safe to skip write barrier. | |
564 StoreFixedArrayElement( | |
565 type_feedback_vector, slot_id, | |
566 HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())), | |
567 SKIP_WRITE_BARRIER); | |
mythria
2016/07/07 08:18:36
MegamorphicSentinel is created here: Heap::CreateI
Benedikt Meurer
2016/07/09 18:19:22
Yes, it's part of the immortal, immovable roots, a
mythria
2016/07/11 15:25:24
Thanks Benedikt. I added a DCHECK more to make the
| |
568 Goto(&call); | |
569 } | |
570 } | |
571 | |
572 Bind(&call); | |
573 { | |
574 // Call using call builtin. | |
575 Callable callable_call = CodeFactory::InterpreterPushArgsAndCall( | |
576 isolate(), tail_call_mode, false); | |
577 Node* code_target_call = HeapConstant(callable_call.code()); | |
578 Node* ret_value = CallStub(callable_call.descriptor(), code_target_call, | |
579 context, arg_count, first_arg, function); | |
580 return_value.Bind(ret_value); | |
581 Goto(&end); | |
582 } | |
583 | |
584 Bind(&end); | |
585 return return_value.value(); | |
586 } | |
587 | |
441 Node* InterpreterAssembler::CallJS(Node* function, Node* context, | 588 Node* InterpreterAssembler::CallJS(Node* function, Node* context, |
442 Node* first_arg, Node* arg_count, | 589 Node* first_arg, Node* arg_count, |
443 TailCallMode tail_call_mode) { | 590 TailCallMode tail_call_mode) { |
444 Callable callable = | 591 Callable callable = |
445 CodeFactory::InterpreterPushArgsAndCall(isolate(), tail_call_mode); | 592 CodeFactory::InterpreterPushArgsAndCall(isolate(), tail_call_mode); |
446 Node* code_target = HeapConstant(callable.code()); | 593 Node* code_target = HeapConstant(callable.code()); |
447 return CallStub(callable.descriptor(), code_target, context, arg_count, | 594 return CallStub(callable.descriptor(), code_target, context, arg_count, |
448 first_arg, function); | 595 first_arg, function); |
449 } | 596 } |
450 | 597 |
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
779 Goto(&loop); | 926 Goto(&loop); |
780 } | 927 } |
781 Bind(&done_loop); | 928 Bind(&done_loop); |
782 | 929 |
783 return array; | 930 return array; |
784 } | 931 } |
785 | 932 |
786 } // namespace interpreter | 933 } // namespace interpreter |
787 } // namespace internal | 934 } // namespace internal |
788 } // namespace v8 | 935 } // namespace v8 |
OLD | NEW |