summaryrefslogtreecommitdiff
path: root/deps/v8/src/interpreter/interpreter-assembler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/interpreter/interpreter-assembler.cc')
-rw-r--r--deps/v8/src/interpreter/interpreter-assembler.cc365
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);