diff options
Diffstat (limited to 'deps/v8/src/interpreter/interpreter-assembler.cc')
-rw-r--r-- | deps/v8/src/interpreter/interpreter-assembler.cc | 365 |
1 files changed, 332 insertions, 33 deletions
diff --git a/deps/v8/src/interpreter/interpreter-assembler.cc b/deps/v8/src/interpreter/interpreter-assembler.cc index 227fd395ce..5767ffa8a5 100644 --- a/deps/v8/src/interpreter/interpreter-assembler.cc +++ b/deps/v8/src/interpreter/interpreter-assembler.cc @@ -14,7 +14,7 @@ #include "src/interpreter/interpreter.h" #include "src/machine-type.h" #include "src/macro-assembler.h" -#include "src/zone.h" +#include "src/zone/zone.h" namespace v8 { namespace internal { @@ -84,6 +84,71 @@ void InterpreterAssembler::SetContext(Node* value) { StoreRegister(value, Register::current_context()); } +Node* InterpreterAssembler::GetContextAtDepth(Node* context, Node* depth) { + Variable cur_context(this, MachineRepresentation::kTaggedPointer); + cur_context.Bind(context); + + Variable cur_depth(this, MachineRepresentation::kWord32); + cur_depth.Bind(depth); + + Label context_found(this); + + Variable* context_search_loop_variables[2] = {&cur_depth, &cur_context}; + Label context_search(this, 2, context_search_loop_variables); + + // Fast path if the depth is 0. + BranchIfWord32Equal(depth, Int32Constant(0), &context_found, &context_search); + + // Loop until the depth is 0. + Bind(&context_search); + { + cur_depth.Bind(Int32Sub(cur_depth.value(), Int32Constant(1))); + cur_context.Bind( + LoadContextSlot(cur_context.value(), Context::PREVIOUS_INDEX)); + + BranchIfWord32Equal(cur_depth.value(), Int32Constant(0), &context_found, + &context_search); + } + + Bind(&context_found); + return cur_context.value(); +} + +void InterpreterAssembler::GotoIfHasContextExtensionUpToDepth(Node* context, + Node* depth, + Label* target) { + Variable cur_context(this, MachineRepresentation::kTaggedPointer); + cur_context.Bind(context); + + Variable cur_depth(this, MachineRepresentation::kWord32); + cur_depth.Bind(depth); + + Variable* context_search_loop_variables[2] = {&cur_depth, &cur_context}; + Label context_search(this, 2, context_search_loop_variables); + + // Loop until the depth is 0. + Goto(&context_search); + Bind(&context_search); + { + // TODO(leszeks): We only need to do this check if the context had a sloppy + // eval, we could pass in a context chain bitmask to figure out which + // contexts actually need to be checked. + + Node* extension_slot = + LoadContextSlot(cur_context.value(), Context::EXTENSION_INDEX); + + // Jump to the target if the extension slot is not a hole. + GotoIf(WordNotEqual(extension_slot, TheHoleConstant()), target); + + cur_depth.Bind(Int32Sub(cur_depth.value(), Int32Constant(1))); + cur_context.Bind( + LoadContextSlot(cur_context.value(), Context::PREVIOUS_INDEX)); + + GotoIf(Word32NotEqual(cur_depth.value(), Int32Constant(0)), + &context_search); + } +} + Node* InterpreterAssembler::BytecodeOffset() { return bytecode_offset_.value(); } @@ -341,6 +406,14 @@ Node* InterpreterAssembler::BytecodeOperandFlag(int operand_index) { return BytecodeUnsignedOperand(operand_index, operand_size); } +Node* InterpreterAssembler::BytecodeOperandUImm(int operand_index) { + DCHECK_EQ(OperandType::kUImm, + Bytecodes::GetOperandType(bytecode_, operand_index)); + OperandSize operand_size = + Bytecodes::GetOperandSize(bytecode_, operand_index, operand_scale()); + return BytecodeUnsignedOperand(operand_index, operand_size); +} + Node* InterpreterAssembler::BytecodeOperandImm(int operand_index) { DCHECK_EQ(OperandType::kImm, Bytecodes::GetOperandType(bytecode_, operand_index)); @@ -460,6 +533,18 @@ void InterpreterAssembler::CallEpilogue() { } } +Node* InterpreterAssembler::IncrementCallCount(Node* type_feedback_vector, + Node* slot_id) { + Comment("increment call count"); + Node* call_count_slot = IntPtrAdd(slot_id, IntPtrConstant(1)); + Node* call_count = + LoadFixedArrayElement(type_feedback_vector, call_count_slot); + Node* new_count = SmiAdd(call_count, SmiTag(Int32Constant(1))); + // Count is Smi, so we don't need a write barrier. + return StoreFixedArrayElement(type_feedback_vector, call_count_slot, + new_count, SKIP_WRITE_BARRIER); +} + Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, Node* first_arg, Node* arg_count, Node* slot_id, @@ -481,15 +566,16 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, WeakCell::kValueOffset == Symbol::kHashFieldSlot); Variable return_value(this, MachineRepresentation::kTagged); - Label handle_monomorphic(this), extra_checks(this), end(this), call(this); + Label handle_monomorphic(this), extra_checks(this), end(this), call(this), + call_function(this), call_without_feedback(this); // Slot id of 0 is used to indicate no typefeedback is available. Call using // call builtin. STATIC_ASSERT(TypeFeedbackVector::kReservedIndexCount > 0); Node* is_feedback_unavailable = Word32Equal(slot_id, Int32Constant(0)); - GotoIf(is_feedback_unavailable, &call); + GotoIf(is_feedback_unavailable, &call_without_feedback); - // The checks. First, does rdi match the recorded monomorphic target? + // The checks. First, does function match the recorded monomorphic target? Node* feedback_element = LoadFixedArrayElement(type_feedback_vector, slot_id); Node* feedback_value = LoadWeakCellValue(feedback_element); Node* is_monomorphic = WordEqual(function, feedback_value); @@ -503,13 +589,7 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, GotoIf(is_smi, &extra_checks); // Increment the call count. - Node* call_count_slot = IntPtrAdd(slot_id, IntPtrConstant(1)); - Node* call_count = - LoadFixedArrayElement(type_feedback_vector, call_count_slot); - Node* new_count = SmiAdd(call_count, SmiTag(Int32Constant(1))); - // Count is Smi, so we don't need a write barrier. - StoreFixedArrayElement(type_feedback_vector, call_count_slot, new_count, - SKIP_WRITE_BARRIER); + IncrementCallCount(type_feedback_vector, slot_id); // Call using call function builtin. Callable callable = CodeFactory::InterpreterPushArgsAndCall( @@ -523,12 +603,42 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, Bind(&extra_checks); { - Label check_initialized(this, Label::kDeferred), mark_megamorphic(this); + Label check_initialized(this, Label::kDeferred), mark_megamorphic(this), + check_allocation_site(this), + create_allocation_site(this, Label::kDeferred); // Check if it is a megamorphic target Node* is_megamorphic = WordEqual( feedback_element, HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate()))); - BranchIf(is_megamorphic, &call, &check_initialized); + BranchIf(is_megamorphic, &call, &check_allocation_site); + + Bind(&check_allocation_site); + { + Node* is_allocation_site = + WordEqual(LoadMap(feedback_element), + LoadRoot(Heap::kAllocationSiteMapRootIndex)); + GotoUnless(is_allocation_site, &check_initialized); + + // If it is not the Array() function, mark megamorphic. + Node* context_slot = + LoadFixedArrayElement(LoadNativeContext(context), + Int32Constant(Context::ARRAY_FUNCTION_INDEX)); + Node* is_array_function = WordEqual(context_slot, function); + GotoUnless(is_array_function, &mark_megamorphic); + + // It is a monomorphic Array function. Increment the call count. + IncrementCallCount(type_feedback_vector, slot_id); + + // Call ArrayConstructorStub. + Callable callable_call = + CodeFactory::InterpreterPushArgsAndConstructArray(isolate()); + Node* code_target_call = HeapConstant(callable_call.code()); + Node* ret_value = + CallStub(callable_call.descriptor(), code_target_call, context, + arg_count, function, feedback_element, first_arg); + return_value.Bind(ret_value); + Goto(&end); + } Bind(&check_initialized); { @@ -548,12 +658,12 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, WordEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE)); GotoUnless(is_js_function, &mark_megamorphic); - // Check that it is not the Array() function. + // Check if it is the Array() function. Node* context_slot = LoadFixedArrayElement(LoadNativeContext(context), Int32Constant(Context::ARRAY_FUNCTION_INDEX)); Node* is_array_function = WordEqual(context_slot, function); - GotoIf(is_array_function, &mark_megamorphic); + GotoIf(is_array_function, &create_allocation_site); // Check if the function belongs to the same native context Node* native_context = LoadNativeContext( @@ -562,23 +672,22 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, WordEqual(native_context, LoadNativeContext(context)); GotoUnless(is_same_native_context, &mark_megamorphic); - // Initialize it to a monomorphic target. - Node* call_count_slot = IntPtrAdd(slot_id, IntPtrConstant(1)); - // Count is Smi, so we don't need a write barrier. - StoreFixedArrayElement(type_feedback_vector, call_count_slot, - SmiTag(Int32Constant(1)), SKIP_WRITE_BARRIER); - CreateWeakCellInFeedbackVector(type_feedback_vector, SmiTag(slot_id), function); // Call using call function builtin. - Callable callable = CodeFactory::InterpreterPushArgsAndCall( - isolate(), tail_call_mode, CallableType::kJSFunction); - Node* code_target = HeapConstant(callable.code()); - Node* ret_value = CallStub(callable.descriptor(), code_target, context, - arg_count, first_arg, function); - return_value.Bind(ret_value); - Goto(&end); + Goto(&call_function); + } + + Bind(&create_allocation_site); + { + CreateAllocationSiteInFeedbackVector(type_feedback_vector, + SmiTag(slot_id)); + + // Call using CallFunction builtin. CallICs have a PREMONOMORPHIC state. + // They start collecting feedback only when a call is executed the second + // time. So, do not pass any feedback here. + Goto(&call_function); } Bind(&mark_megamorphic); @@ -595,8 +704,37 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, } } + Bind(&call_function); + { + // Increment the call count. + IncrementCallCount(type_feedback_vector, slot_id); + + Callable callable_call = CodeFactory::InterpreterPushArgsAndCall( + isolate(), tail_call_mode, CallableType::kJSFunction); + Node* code_target_call = HeapConstant(callable_call.code()); + Node* ret_value = CallStub(callable_call.descriptor(), code_target_call, + context, arg_count, first_arg, function); + return_value.Bind(ret_value); + Goto(&end); + } + Bind(&call); { + // Increment the call count. + IncrementCallCount(type_feedback_vector, slot_id); + + // Call using call builtin. + Callable callable_call = CodeFactory::InterpreterPushArgsAndCall( + isolate(), tail_call_mode, CallableType::kAny); + Node* code_target_call = HeapConstant(callable_call.code()); + Node* ret_value = CallStub(callable_call.descriptor(), code_target_call, + context, arg_count, first_arg, function); + return_value.Bind(ret_value); + Goto(&end); + } + + Bind(&call_without_feedback); + { // Call using call builtin. Callable callable_call = CodeFactory::InterpreterPushArgsAndCall( isolate(), tail_call_mode, CallableType::kAny); @@ -623,11 +761,169 @@ Node* InterpreterAssembler::CallJS(Node* function, Node* context, Node* InterpreterAssembler::CallConstruct(Node* constructor, Node* context, Node* new_target, Node* first_arg, - Node* arg_count) { - Callable callable = CodeFactory::InterpreterPushArgsAndConstruct(isolate()); - Node* code_target = HeapConstant(callable.code()); - return CallStub(callable.descriptor(), code_target, context, arg_count, - new_target, constructor, first_arg); + Node* arg_count, Node* slot_id, + Node* type_feedback_vector) { + Label call_construct(this), js_function(this), end(this); + Variable return_value(this, MachineRepresentation::kTagged); + Variable allocation_feedback(this, MachineRepresentation::kTagged); + allocation_feedback.Bind(UndefinedConstant()); + + // Slot id of 0 is used to indicate no type feedback is available. + STATIC_ASSERT(TypeFeedbackVector::kReservedIndexCount > 0); + Node* is_feedback_unavailable = Word32Equal(slot_id, Int32Constant(0)); + GotoIf(is_feedback_unavailable, &call_construct); + + // Check that the constructor is not a smi. + Node* is_smi = WordIsSmi(constructor); + GotoIf(is_smi, &call_construct); + + // Check that constructor is a JSFunction. + Node* instance_type = LoadInstanceType(constructor); + Node* is_js_function = + WordEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE)); + BranchIf(is_js_function, &js_function, &call_construct); + + Bind(&js_function); + { + // Cache the called function in a feedback vector slot. Cache states + // are uninitialized, monomorphic (indicated by a JSFunction), and + // megamorphic. + // TODO(mythria/v8:5210): Check if it is better to mark extra_checks as a + // deferred block so that call_construct_function will be scheduled. + Label extra_checks(this), call_construct_function(this); + + Node* feedback_element = + LoadFixedArrayElement(type_feedback_vector, slot_id); + Node* feedback_value = LoadWeakCellValue(feedback_element); + Node* is_monomorphic = WordEqual(constructor, feedback_value); + BranchIf(is_monomorphic, &call_construct_function, &extra_checks); + + Bind(&extra_checks); + { + Label mark_megamorphic(this), initialize(this), + check_allocation_site(this), check_initialized(this), + set_alloc_feedback_and_call(this); + { + // Check if it is a megamorphic target + Comment("check if megamorphic"); + Node* is_megamorphic = WordEqual( + feedback_element, + HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate()))); + GotoIf(is_megamorphic, &call_construct_function); + + Comment("check if weak cell"); + Node* is_weak_cell = WordEqual(LoadMap(feedback_element), + LoadRoot(Heap::kWeakCellMapRootIndex)); + GotoUnless(is_weak_cell, &check_allocation_site); + // If the weak cell is cleared, we have a new chance to become + // monomorphic. + Comment("check if weak cell is cleared"); + Node* is_smi = WordIsSmi(feedback_value); + BranchIf(is_smi, &initialize, &mark_megamorphic); + } + + Bind(&check_allocation_site); + { + Comment("check if it is an allocation site"); + Node* is_allocation_site = + WordEqual(LoadObjectField(feedback_element, 0), + LoadRoot(Heap::kAllocationSiteMapRootIndex)); + GotoUnless(is_allocation_site, &check_initialized); + + // Make sure the function is the Array() function + Node* context_slot = + LoadFixedArrayElement(LoadNativeContext(context), + Int32Constant(Context::ARRAY_FUNCTION_INDEX)); + Node* is_array_function = WordEqual(context_slot, constructor); + BranchIf(is_array_function, &set_alloc_feedback_and_call, + &mark_megamorphic); + } + + Bind(&set_alloc_feedback_and_call); + { + allocation_feedback.Bind(feedback_element); + Goto(&call_construct_function); + } + + Bind(&check_initialized); + { + // Check if it is uninitialized. + Comment("check if uninitialized"); + Node* is_uninitialized = WordEqual( + feedback_element, LoadRoot(Heap::kuninitialized_symbolRootIndex)); + BranchIf(is_uninitialized, &initialize, &mark_megamorphic); + } + + Bind(&initialize); + { + Label create_weak_cell(this), create_allocation_site(this); + Comment("initialize the feedback element"); + // Check that it is the Array() function. + Node* context_slot = + LoadFixedArrayElement(LoadNativeContext(context), + Int32Constant(Context::ARRAY_FUNCTION_INDEX)); + Node* is_array_function = WordEqual(context_slot, constructor); + BranchIf(is_array_function, &create_allocation_site, &create_weak_cell); + + Bind(&create_allocation_site); + { + Node* site = CreateAllocationSiteInFeedbackVector( + type_feedback_vector, SmiTag(slot_id)); + allocation_feedback.Bind(site); + Goto(&call_construct_function); + } + + Bind(&create_weak_cell); + { + CreateWeakCellInFeedbackVector(type_feedback_vector, SmiTag(slot_id), + constructor); + Goto(&call_construct_function); + } + } + + Bind(&mark_megamorphic); + { + // MegamorphicSentinel is an immortal immovable object so + // write-barrier is not needed. + Comment("transition to megamorphic"); + DCHECK( + Heap::RootIsImmortalImmovable(Heap::kmegamorphic_symbolRootIndex)); + StoreFixedArrayElement( + type_feedback_vector, slot_id, + HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())), + SKIP_WRITE_BARRIER); + Goto(&call_construct_function); + } + } + + Bind(&call_construct_function); + { + Comment("call using callConstructFunction"); + IncrementCallCount(type_feedback_vector, slot_id); + Callable callable_function = CodeFactory::InterpreterPushArgsAndConstruct( + isolate(), CallableType::kJSFunction); + return_value.Bind(CallStub(callable_function.descriptor(), + HeapConstant(callable_function.code()), + context, arg_count, new_target, constructor, + allocation_feedback.value(), first_arg)); + Goto(&end); + } + } + + Bind(&call_construct); + { + Comment("call using callConstruct builtin"); + Callable callable = CodeFactory::InterpreterPushArgsAndConstruct( + isolate(), CallableType::kAny); + Node* code_target = HeapConstant(callable.code()); + return_value.Bind(CallStub(callable.descriptor(), code_target, context, + arg_count, new_target, constructor, + UndefinedConstant(), first_arg)); + Goto(&end); + } + + Bind(&end); + return return_value.value(); } Node* InterpreterAssembler::CallRuntimeN(Node* function_id, Node* context, @@ -651,6 +947,9 @@ Node* InterpreterAssembler::CallRuntimeN(Node* function_id, Node* context, } void InterpreterAssembler::UpdateInterruptBudget(Node* weight) { + // TODO(rmcilroy): It might be worthwhile to only update the budget for + // backwards branches. Those are distinguishable by the {JumpLoop} bytecode. + Label ok(this), interrupt_check(this, Label::kDeferred), end(this); Node* budget_offset = IntPtrConstant(BytecodeArray::kInterruptBudgetOffset - kHeapObjectTag); |