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.cc653
1 files changed, 521 insertions, 132 deletions
diff --git a/deps/v8/src/interpreter/interpreter-assembler.cc b/deps/v8/src/interpreter/interpreter-assembler.cc
index 2663e4a876..227fd395ce 100644
--- a/deps/v8/src/interpreter/interpreter-assembler.cc
+++ b/deps/v8/src/interpreter/interpreter-assembler.cc
@@ -4,6 +4,7 @@
#include "src/interpreter/interpreter-assembler.h"
+#include <limits>
#include <ostream>
#include "src/code-factory.h"
@@ -24,23 +25,22 @@ using compiler::Node;
InterpreterAssembler::InterpreterAssembler(Isolate* isolate, Zone* zone,
Bytecode bytecode,
OperandScale operand_scale)
- : compiler::CodeStubAssembler(isolate, zone,
- InterpreterDispatchDescriptor(isolate),
- Code::ComputeFlags(Code::BYTECODE_HANDLER),
- Bytecodes::ToString(bytecode), 0),
+ : CodeStubAssembler(isolate, zone, InterpreterDispatchDescriptor(isolate),
+ Code::ComputeFlags(Code::BYTECODE_HANDLER),
+ Bytecodes::ToString(bytecode),
+ Bytecodes::ReturnCount(bytecode)),
bytecode_(bytecode),
operand_scale_(operand_scale),
+ bytecode_offset_(this, MachineType::PointerRepresentation()),
+ interpreted_frame_pointer_(this, MachineType::PointerRepresentation()),
accumulator_(this, MachineRepresentation::kTagged),
accumulator_use_(AccumulatorUse::kNone),
- context_(this, MachineRepresentation::kTagged),
- bytecode_array_(this, MachineRepresentation::kTagged),
+ made_call_(false),
disable_stack_check_across_call_(false),
stack_pointer_before_call_(nullptr) {
- accumulator_.Bind(
- Parameter(InterpreterDispatchDescriptor::kAccumulatorParameter));
- context_.Bind(Parameter(InterpreterDispatchDescriptor::kContextParameter));
- bytecode_array_.Bind(
- Parameter(InterpreterDispatchDescriptor::kBytecodeArrayParameter));
+ accumulator_.Bind(Parameter(InterpreterDispatchDescriptor::kAccumulator));
+ bytecode_offset_.Bind(
+ Parameter(InterpreterDispatchDescriptor::kBytecodeOffset));
if (FLAG_trace_ignition) {
TraceBytecode(Runtime::kInterpreterTraceBytecodeEntry);
}
@@ -53,6 +53,13 @@ InterpreterAssembler::~InterpreterAssembler() {
DCHECK_EQ(accumulator_use_, Bytecodes::GetAccumulatorUse(bytecode_));
}
+Node* InterpreterAssembler::GetInterpretedFramePointer() {
+ if (!interpreted_frame_pointer_.IsBound()) {
+ interpreted_frame_pointer_.Bind(LoadParentFramePointer());
+ }
+ return interpreted_frame_pointer_.value();
+}
+
Node* InterpreterAssembler::GetAccumulatorUnchecked() {
return accumulator_.value();
}
@@ -69,64 +76,60 @@ void InterpreterAssembler::SetAccumulator(Node* value) {
accumulator_.Bind(value);
}
-Node* InterpreterAssembler::GetContext() { return context_.value(); }
+Node* InterpreterAssembler::GetContext() {
+ return LoadRegister(Register::current_context());
+}
void InterpreterAssembler::SetContext(Node* value) {
StoreRegister(value, Register::current_context());
- context_.Bind(value);
}
Node* InterpreterAssembler::BytecodeOffset() {
- return Parameter(InterpreterDispatchDescriptor::kBytecodeOffsetParameter);
-}
-
-Node* InterpreterAssembler::RegisterFileRawPointer() {
- return Parameter(InterpreterDispatchDescriptor::kRegisterFileParameter);
+ return bytecode_offset_.value();
}
Node* InterpreterAssembler::BytecodeArrayTaggedPointer() {
- return bytecode_array_.value();
+ if (made_call_) {
+ // If we have made a call, restore bytecode array from stack frame in case
+ // the debugger has swapped us to the patched debugger bytecode array.
+ return LoadRegister(Register::bytecode_array());
+ } else {
+ return Parameter(InterpreterDispatchDescriptor::kBytecodeArray);
+ }
}
Node* InterpreterAssembler::DispatchTableRawPointer() {
- return Parameter(InterpreterDispatchDescriptor::kDispatchTableParameter);
+ return Parameter(InterpreterDispatchDescriptor::kDispatchTable);
}
Node* InterpreterAssembler::RegisterLocation(Node* reg_index) {
- return IntPtrAdd(RegisterFileRawPointer(), RegisterFrameOffset(reg_index));
+ return IntPtrAdd(GetInterpretedFramePointer(),
+ RegisterFrameOffset(reg_index));
}
-Node* InterpreterAssembler::LoadRegister(int offset) {
- return Load(MachineType::AnyTagged(), RegisterFileRawPointer(),
- IntPtrConstant(offset));
+Node* InterpreterAssembler::RegisterFrameOffset(Node* index) {
+ return WordShl(index, kPointerSizeLog2);
}
Node* InterpreterAssembler::LoadRegister(Register reg) {
- return LoadRegister(IntPtrConstant(-reg.index()));
-}
-
-Node* InterpreterAssembler::RegisterFrameOffset(Node* index) {
- return WordShl(index, kPointerSizeLog2);
+ return Load(MachineType::AnyTagged(), GetInterpretedFramePointer(),
+ IntPtrConstant(reg.ToOperand() << kPointerSizeLog2));
}
Node* InterpreterAssembler::LoadRegister(Node* reg_index) {
- return Load(MachineType::AnyTagged(), RegisterFileRawPointer(),
+ return Load(MachineType::AnyTagged(), GetInterpretedFramePointer(),
RegisterFrameOffset(reg_index));
}
-Node* InterpreterAssembler::StoreRegister(Node* value, int offset) {
- return StoreNoWriteBarrier(MachineRepresentation::kTagged,
- RegisterFileRawPointer(), IntPtrConstant(offset),
- value);
-}
-
Node* InterpreterAssembler::StoreRegister(Node* value, Register reg) {
- return StoreRegister(value, IntPtrConstant(-reg.index()));
+ return StoreNoWriteBarrier(
+ MachineRepresentation::kTagged, GetInterpretedFramePointer(),
+ IntPtrConstant(reg.ToOperand() << kPointerSizeLog2), value);
}
Node* InterpreterAssembler::StoreRegister(Node* value, Node* reg_index) {
return StoreNoWriteBarrier(MachineRepresentation::kTagged,
- RegisterFileRawPointer(),
+ GetInterpretedFramePointer(),
RegisterFrameOffset(reg_index), value);
}
@@ -371,6 +374,15 @@ Node* InterpreterAssembler::BytecodeOperandRuntimeId(int operand_index) {
return BytecodeUnsignedOperand(operand_index, operand_size);
}
+Node* InterpreterAssembler::BytecodeOperandIntrinsicId(int operand_index) {
+ DCHECK(OperandType::kIntrinsicId ==
+ Bytecodes::GetOperandType(bytecode_, operand_index));
+ OperandSize operand_size =
+ Bytecodes::GetOperandSize(bytecode_, operand_index, operand_scale());
+ DCHECK_EQ(operand_size, OperandSize::kByte);
+ return BytecodeUnsignedOperand(operand_index, operand_size);
+}
+
Node* InterpreterAssembler::LoadConstantPoolEntry(Node* index) {
Node* constant_pool = LoadObjectField(BytecodeArrayTaggedPointer(),
BytecodeArray::kConstantPoolOffset);
@@ -380,9 +392,24 @@ Node* InterpreterAssembler::LoadConstantPoolEntry(Node* index) {
return Load(MachineType::AnyTagged(), constant_pool, entry_offset);
}
-Node* InterpreterAssembler::LoadObjectField(Node* object, int offset) {
- return Load(MachineType::AnyTagged(), object,
- IntPtrConstant(offset - kHeapObjectTag));
+Node* InterpreterAssembler::LoadAndUntagConstantPoolEntry(Node* index) {
+ Node* constant_pool = LoadObjectField(BytecodeArrayTaggedPointer(),
+ BytecodeArray::kConstantPoolOffset);
+ int offset = FixedArray::kHeaderSize - kHeapObjectTag;
+#if V8_TARGET_LITTLE_ENDIAN
+ if (Is64()) {
+ offset += kPointerSize / 2;
+ }
+#endif
+ Node* entry_offset =
+ IntPtrAdd(IntPtrConstant(offset), WordShl(index, kPointerSizeLog2));
+ if (Is64()) {
+ return ChangeInt32ToInt64(
+ Load(MachineType::Int32(), constant_pool, entry_offset));
+ } else {
+ return SmiUntag(
+ Load(MachineType::AnyTagged(), constant_pool, entry_offset));
+ }
}
Node* InterpreterAssembler::LoadContextSlot(Node* context, int slot_index) {
@@ -406,24 +433,21 @@ Node* InterpreterAssembler::StoreContextSlot(Node* context, Node* slot_index,
}
Node* InterpreterAssembler::LoadTypeFeedbackVector() {
- Node* function = Load(
- MachineType::AnyTagged(), RegisterFileRawPointer(),
- IntPtrConstant(InterpreterFrameConstants::kFunctionFromRegisterPointer));
- Node* shared_info =
- LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset);
+ Node* function = LoadRegister(Register::function_closure());
+ Node* literals = LoadObjectField(function, JSFunction::kLiteralsOffset);
Node* vector =
- LoadObjectField(shared_info, SharedFunctionInfo::kFeedbackVectorOffset);
+ LoadObjectField(literals, LiteralsArray::kFeedbackVectorOffset);
return vector;
}
void InterpreterAssembler::CallPrologue() {
- StoreRegister(SmiTag(BytecodeOffset()),
- InterpreterFrameConstants::kBytecodeOffsetFromRegisterPointer);
+ StoreRegister(SmiTag(BytecodeOffset()), Register::bytecode_offset());
if (FLAG_debug_code && !disable_stack_check_across_call_) {
DCHECK(stack_pointer_before_call_ == nullptr);
stack_pointer_before_call_ = LoadStackPointer();
}
+ made_call_ = true;
}
void InterpreterAssembler::CallEpilogue() {
@@ -434,18 +458,164 @@ void InterpreterAssembler::CallEpilogue() {
AbortIfWordNotEqual(stack_pointer_before_call, stack_pointer_after_call,
kUnexpectedStackPointer);
}
+}
+
+Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context,
+ Node* first_arg, Node* arg_count,
+ Node* slot_id,
+ Node* type_feedback_vector,
+ TailCallMode tail_call_mode) {
+ // Static checks to assert it is safe to examine the type feedback element.
+ // We don't know that we have a weak cell. We might have a private symbol
+ // or an AllocationSite, but the memory is safe to examine.
+ // AllocationSite::kTransitionInfoOffset - contains a Smi or pointer to
+ // FixedArray.
+ // WeakCell::kValueOffset - contains a JSFunction or Smi(0)
+ // Symbol::kHashFieldSlot - if the low bit is 1, then the hash is not
+ // computed, meaning that it can't appear to be a pointer. If the low bit is
+ // 0, then hash is computed, but the 0 bit prevents the field from appearing
+ // to be a pointer.
+ STATIC_ASSERT(WeakCell::kSize >= kPointerSize);
+ STATIC_ASSERT(AllocationSite::kTransitionInfoOffset ==
+ WeakCell::kValueOffset &&
+ WeakCell::kValueOffset == Symbol::kHashFieldSlot);
+
+ Variable return_value(this, MachineRepresentation::kTagged);
+ Label handle_monomorphic(this), extra_checks(this), end(this), call(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);
+
+ // The checks. First, does rdi 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);
+ BranchIf(is_monomorphic, &handle_monomorphic, &extra_checks);
+
+ Bind(&handle_monomorphic);
+ {
+ // The compare above could have been a SMI/SMI comparison. Guard against
+ // this convincing us that we have a monomorphic JSFunction.
+ Node* is_smi = WordIsSmi(function);
+ 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);
+
+ // 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);
+ }
+
+ Bind(&extra_checks);
+ {
+ Label check_initialized(this, Label::kDeferred), mark_megamorphic(this);
+ // Check if it is a megamorphic target
+ Node* is_megamorphic = WordEqual(
+ feedback_element,
+ HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())));
+ BranchIf(is_megamorphic, &call, &check_initialized);
+
+ Bind(&check_initialized);
+ {
+ Label possibly_monomorphic(this);
+ // Check if it is uninitialized.
+ Node* is_uninitialized = WordEqual(
+ feedback_element,
+ HeapConstant(TypeFeedbackVector::UninitializedSentinel(isolate())));
+ GotoUnless(is_uninitialized, &mark_megamorphic);
+
+ Node* is_smi = WordIsSmi(function);
+ GotoIf(is_smi, &mark_megamorphic);
+
+ // Check if function is an object of JSFunction type
+ Node* instance_type = LoadInstanceType(function);
+ Node* is_js_function =
+ WordEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE));
+ GotoUnless(is_js_function, &mark_megamorphic);
+
+ // Check that it is not 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);
+
+ // Check if the function belongs to the same native context
+ Node* native_context = LoadNativeContext(
+ LoadObjectField(function, JSFunction::kContextOffset));
+ Node* is_same_native_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);
+ }
+
+ Bind(&mark_megamorphic);
+ {
+ // Mark it as a megamorphic.
+ // MegamorphicSentinel is created as a part of Heap::InitialObjects
+ // and will not move during a GC. So it is safe to skip write barrier.
+ DCHECK(Heap::RootIsImmortalImmovable(Heap::kmegamorphic_symbolRootIndex));
+ StoreFixedArrayElement(
+ type_feedback_vector, slot_id,
+ HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())),
+ SKIP_WRITE_BARRIER);
+ Goto(&call);
+ }
+ }
+
+ Bind(&call);
+ {
+ // 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);
+ }
- // Restore bytecode array from stack frame in case the debugger has swapped us
- // to the patched debugger bytecode array.
- bytecode_array_.Bind(LoadRegister(
- InterpreterFrameConstants::kBytecodeArrayFromRegisterPointer));
+ Bind(&end);
+ return return_value.value();
}
Node* InterpreterAssembler::CallJS(Node* function, Node* context,
Node* first_arg, Node* arg_count,
TailCallMode tail_call_mode) {
- Callable callable =
- CodeFactory::InterpreterPushArgsAndCall(isolate(), tail_call_mode);
+ Callable callable = CodeFactory::InterpreterPushArgsAndCall(
+ isolate(), tail_call_mode, CallableType::kAny);
Node* code_target = HeapConstant(callable.code());
return CallStub(callable.descriptor(), code_target, context, arg_count,
first_arg, function);
@@ -481,53 +651,64 @@ Node* InterpreterAssembler::CallRuntimeN(Node* function_id, Node* context,
}
void InterpreterAssembler::UpdateInterruptBudget(Node* weight) {
- CodeStubAssembler::Label ok(this);
- CodeStubAssembler::Label interrupt_check(this);
- CodeStubAssembler::Label end(this);
+ Label ok(this), interrupt_check(this, Label::kDeferred), end(this);
Node* budget_offset =
IntPtrConstant(BytecodeArray::kInterruptBudgetOffset - kHeapObjectTag);
// Update budget by |weight| and check if it reaches zero.
+ Variable new_budget(this, MachineRepresentation::kWord32);
Node* old_budget =
Load(MachineType::Int32(), BytecodeArrayTaggedPointer(), budget_offset);
- Node* new_budget = Int32Add(old_budget, weight);
- Node* condition = Int32GreaterThanOrEqual(new_budget, Int32Constant(0));
+ new_budget.Bind(Int32Add(old_budget, weight));
+ Node* condition =
+ Int32GreaterThanOrEqual(new_budget.value(), Int32Constant(0));
Branch(condition, &ok, &interrupt_check);
// Perform interrupt and reset budget.
Bind(&interrupt_check);
- CallRuntime(Runtime::kInterrupt, GetContext());
- StoreNoWriteBarrier(MachineRepresentation::kWord32,
- BytecodeArrayTaggedPointer(), budget_offset,
- Int32Constant(Interpreter::InterruptBudget()));
- Goto(&end);
+ {
+ CallRuntime(Runtime::kInterrupt, GetContext());
+ new_budget.Bind(Int32Constant(Interpreter::InterruptBudget()));
+ Goto(&ok);
+ }
// Update budget.
Bind(&ok);
StoreNoWriteBarrier(MachineRepresentation::kWord32,
- BytecodeArrayTaggedPointer(), budget_offset, new_budget);
- Goto(&end);
- Bind(&end);
+ BytecodeArrayTaggedPointer(), budget_offset,
+ new_budget.value());
+}
+
+Node* InterpreterAssembler::Advance() {
+ return Advance(Bytecodes::Size(bytecode_, operand_scale_));
}
Node* InterpreterAssembler::Advance(int delta) {
- return IntPtrAdd(BytecodeOffset(), IntPtrConstant(delta));
+ return Advance(IntPtrConstant(delta));
}
Node* InterpreterAssembler::Advance(Node* delta) {
- return IntPtrAdd(BytecodeOffset(), delta);
+ if (FLAG_trace_ignition) {
+ TraceBytecode(Runtime::kInterpreterTraceBytecodeExit);
+ }
+ Node* next_offset = IntPtrAdd(BytecodeOffset(), delta);
+ bytecode_offset_.Bind(next_offset);
+ return next_offset;
}
-void InterpreterAssembler::Jump(Node* delta) {
+Node* InterpreterAssembler::Jump(Node* delta) {
+ DCHECK(!Bytecodes::IsStarLookahead(bytecode_, operand_scale_));
+
UpdateInterruptBudget(delta);
- DispatchTo(Advance(delta));
+ Node* new_bytecode_offset = Advance(delta);
+ Node* target_bytecode = LoadBytecode(new_bytecode_offset);
+ return DispatchToBytecode(target_bytecode, new_bytecode_offset);
}
void InterpreterAssembler::JumpConditional(Node* condition, Node* delta) {
- CodeStubAssembler::Label match(this);
- CodeStubAssembler::Label no_match(this);
+ Label match(this), no_match(this);
- Branch(condition, &match, &no_match);
+ BranchIf(condition, &match, &no_match);
Bind(&match);
Jump(delta);
Bind(&no_match);
@@ -543,37 +724,90 @@ void InterpreterAssembler::JumpIfWordNotEqual(Node* lhs, Node* rhs,
JumpConditional(WordNotEqual(lhs, rhs), delta);
}
-void InterpreterAssembler::Dispatch() {
- DispatchTo(Advance(Bytecodes::Size(bytecode_, operand_scale_)));
+Node* InterpreterAssembler::LoadBytecode(compiler::Node* bytecode_offset) {
+ Node* bytecode =
+ Load(MachineType::Uint8(), BytecodeArrayTaggedPointer(), bytecode_offset);
+ if (kPointerSize == 8) {
+ bytecode = ChangeUint32ToUint64(bytecode);
+ }
+ return bytecode;
}
-void InterpreterAssembler::DispatchTo(Node* new_bytecode_offset) {
- Node* target_bytecode = Load(
- MachineType::Uint8(), BytecodeArrayTaggedPointer(), new_bytecode_offset);
- if (kPointerSize == 8) {
- target_bytecode = ChangeUint32ToUint64(target_bytecode);
+Node* InterpreterAssembler::StarDispatchLookahead(Node* target_bytecode) {
+ Label do_inline_star(this), done(this);
+
+ Variable var_bytecode(this, MachineRepresentation::kWord8);
+ var_bytecode.Bind(target_bytecode);
+
+ Node* star_bytecode = IntPtrConstant(static_cast<int>(Bytecode::kStar));
+ Node* is_star = WordEqual(target_bytecode, star_bytecode);
+ BranchIf(is_star, &do_inline_star, &done);
+
+ Bind(&do_inline_star);
+ {
+ InlineStar();
+ var_bytecode.Bind(LoadBytecode(BytecodeOffset()));
+ Goto(&done);
}
+ Bind(&done);
+ return var_bytecode.value();
+}
+
+void InterpreterAssembler::InlineStar() {
+ Bytecode previous_bytecode = bytecode_;
+ AccumulatorUse previous_acc_use = accumulator_use_;
- // TODO(rmcilroy): Create a code target dispatch table to avoid conversion
- // from code object on every dispatch.
- Node* target_code_object =
+ bytecode_ = Bytecode::kStar;
+ accumulator_use_ = AccumulatorUse::kNone;
+
+ if (FLAG_trace_ignition) {
+ TraceBytecode(Runtime::kInterpreterTraceBytecodeEntry);
+ }
+ StoreRegister(GetAccumulator(), BytecodeOperandReg(0));
+
+ DCHECK_EQ(accumulator_use_, Bytecodes::GetAccumulatorUse(bytecode_));
+
+ Advance();
+ bytecode_ = previous_bytecode;
+ accumulator_use_ = previous_acc_use;
+}
+
+Node* InterpreterAssembler::Dispatch() {
+ Node* target_offset = Advance();
+ Node* target_bytecode = LoadBytecode(target_offset);
+
+ if (Bytecodes::IsStarLookahead(bytecode_, operand_scale_)) {
+ target_bytecode = StarDispatchLookahead(target_bytecode);
+ }
+ return DispatchToBytecode(target_bytecode, BytecodeOffset());
+}
+
+Node* InterpreterAssembler::DispatchToBytecode(Node* target_bytecode,
+ Node* new_bytecode_offset) {
+ if (FLAG_trace_ignition_dispatches) {
+ TraceBytecodeDispatch(target_bytecode);
+ }
+
+ Node* target_code_entry =
Load(MachineType::Pointer(), DispatchTableRawPointer(),
WordShl(target_bytecode, IntPtrConstant(kPointerSizeLog2)));
- DispatchToBytecodeHandler(target_code_object, new_bytecode_offset);
+ return DispatchToBytecodeHandlerEntry(target_code_entry, new_bytecode_offset);
}
-void InterpreterAssembler::DispatchToBytecodeHandler(Node* handler,
- Node* bytecode_offset) {
- if (FLAG_trace_ignition) {
- TraceBytecode(Runtime::kInterpreterTraceBytecodeExit);
- }
+Node* InterpreterAssembler::DispatchToBytecodeHandler(Node* handler,
+ Node* bytecode_offset) {
+ Node* handler_entry =
+ IntPtrAdd(handler, IntPtrConstant(Code::kHeaderSize - kHeapObjectTag));
+ return DispatchToBytecodeHandlerEntry(handler_entry, bytecode_offset);
+}
+Node* InterpreterAssembler::DispatchToBytecodeHandlerEntry(
+ Node* handler_entry, Node* bytecode_offset) {
InterpreterDispatchDescriptor descriptor(isolate());
- Node* args[] = {GetAccumulatorUnchecked(), RegisterFileRawPointer(),
- bytecode_offset, BytecodeArrayTaggedPointer(),
- DispatchTableRawPointer(), GetContext()};
- TailCall(descriptor, handler, args, 0);
+ Node* args[] = {GetAccumulatorUnchecked(), bytecode_offset,
+ BytecodeArrayTaggedPointer(), DispatchTableRawPointer()};
+ return TailCallBytecodeDispatch(descriptor, handler_entry, args);
}
void InterpreterAssembler::DispatchWide(OperandScale operand_scale) {
@@ -585,11 +819,12 @@ void InterpreterAssembler::DispatchWide(OperandScale operand_scale) {
// Indices 256-511 correspond to bytecodes with operand_scale == 1
// Indices 512-767 correspond to bytecodes with operand_scale == 2
Node* next_bytecode_offset = Advance(1);
- Node* next_bytecode = Load(MachineType::Uint8(), BytecodeArrayTaggedPointer(),
- next_bytecode_offset);
- if (kPointerSize == 8) {
- next_bytecode = ChangeUint32ToUint64(next_bytecode);
+ Node* next_bytecode = LoadBytecode(next_bytecode_offset);
+
+ if (FLAG_trace_ignition_dispatches) {
+ TraceBytecodeDispatch(next_bytecode);
}
+
Node* base_index;
switch (operand_scale) {
case OperandScale::kDouble:
@@ -603,14 +838,75 @@ void InterpreterAssembler::DispatchWide(OperandScale operand_scale) {
base_index = nullptr;
}
Node* target_index = IntPtrAdd(base_index, next_bytecode);
- Node* target_code_object =
+ Node* target_code_entry =
Load(MachineType::Pointer(), DispatchTableRawPointer(),
WordShl(target_index, kPointerSizeLog2));
- DispatchToBytecodeHandler(target_code_object, next_bytecode_offset);
+ DispatchToBytecodeHandlerEntry(target_code_entry, next_bytecode_offset);
+}
+
+Node* InterpreterAssembler::TruncateTaggedToWord32WithFeedback(
+ Node* context, Node* value, Variable* var_type_feedback) {
+ // We might need to loop once due to ToNumber conversion.
+ Variable var_value(this, MachineRepresentation::kTagged),
+ var_result(this, MachineRepresentation::kWord32);
+ Variable* loop_vars[] = {&var_value, var_type_feedback};
+ Label loop(this, 2, loop_vars), done_loop(this, &var_result);
+ var_value.Bind(value);
+ var_type_feedback->Bind(Int32Constant(BinaryOperationFeedback::kNone));
+ Goto(&loop);
+ Bind(&loop);
+ {
+ // Load the current {value}.
+ value = var_value.value();
+
+ // Check if the {value} is a Smi or a HeapObject.
+ Label if_valueissmi(this), if_valueisnotsmi(this);
+ Branch(WordIsSmi(value), &if_valueissmi, &if_valueisnotsmi);
+
+ Bind(&if_valueissmi);
+ {
+ // Convert the Smi {value}.
+ var_result.Bind(SmiToWord32(value));
+ var_type_feedback->Bind(
+ Word32Or(var_type_feedback->value(),
+ Int32Constant(BinaryOperationFeedback::kSignedSmall)));
+ Goto(&done_loop);
+ }
+
+ Bind(&if_valueisnotsmi);
+ {
+ // Check if {value} is a HeapNumber.
+ Label if_valueisheapnumber(this),
+ if_valueisnotheapnumber(this, Label::kDeferred);
+ Branch(WordEqual(LoadMap(value), HeapNumberMapConstant()),
+ &if_valueisheapnumber, &if_valueisnotheapnumber);
+
+ Bind(&if_valueisheapnumber);
+ {
+ // Truncate the floating point value.
+ var_result.Bind(TruncateHeapNumberValueToWord32(value));
+ var_type_feedback->Bind(
+ Word32Or(var_type_feedback->value(),
+ Int32Constant(BinaryOperationFeedback::kNumber)));
+ Goto(&done_loop);
+ }
+
+ Bind(&if_valueisnotheapnumber);
+ {
+ // Convert the {value} to a Number first.
+ Callable callable = CodeFactory::NonNumberToNumber(isolate());
+ var_value.Bind(CallStub(callable, context, value));
+ var_type_feedback->Bind(Int32Constant(BinaryOperationFeedback::kAny));
+ Goto(&loop);
+ }
+ }
+ }
+ Bind(&done_loop);
+ return var_result.value();
}
-void InterpreterAssembler::InterpreterReturn() {
+void InterpreterAssembler::UpdateInterruptBudgetOnReturn() {
// TODO(rmcilroy): Investigate whether it is worth supporting self
// optimization of primitive functions like FullCodegen.
@@ -620,29 +916,20 @@ void InterpreterAssembler::InterpreterReturn() {
Int32Sub(Int32Constant(kHeapObjectTag + BytecodeArray::kHeaderSize),
BytecodeOffset());
UpdateInterruptBudget(profiling_weight);
-
- Node* exit_trampoline_code_object =
- HeapConstant(isolate()->builtins()->InterpreterExitTrampoline());
- DispatchToBytecodeHandler(exit_trampoline_code_object);
}
-void InterpreterAssembler::StackCheck() {
- CodeStubAssembler::Label end(this);
- CodeStubAssembler::Label ok(this);
- CodeStubAssembler::Label stack_guard(this);
-
+Node* InterpreterAssembler::StackCheckTriggeredInterrupt() {
Node* sp = LoadStackPointer();
Node* stack_limit = Load(
MachineType::Pointer(),
ExternalConstant(ExternalReference::address_of_stack_limit(isolate())));
- Node* condition = UintPtrGreaterThanOrEqual(sp, stack_limit);
- Branch(condition, &ok, &stack_guard);
- Bind(&stack_guard);
- CallRuntime(Runtime::kStackGuard, GetContext());
- Goto(&end);
- Bind(&ok);
- Goto(&end);
- Bind(&end);
+ return UintPtrLessThan(sp, stack_limit);
+}
+
+Node* InterpreterAssembler::LoadOSRNestingLevel() {
+ Node* offset =
+ IntPtrConstant(BytecodeArray::kOSRNestingLevelOffset - kHeapObjectTag);
+ return Load(MachineType::Int8(), BytecodeArrayTaggedPointer(), offset);
}
void InterpreterAssembler::Abort(BailoutReason bailout_reason) {
@@ -654,18 +941,14 @@ void InterpreterAssembler::Abort(BailoutReason bailout_reason) {
void InterpreterAssembler::AbortIfWordNotEqual(Node* lhs, Node* rhs,
BailoutReason bailout_reason) {
- CodeStubAssembler::Label match(this);
- CodeStubAssembler::Label no_match(this);
- CodeStubAssembler::Label end(this);
+ Label ok(this), abort(this, Label::kDeferred);
+ BranchIfWordEqual(lhs, rhs, &ok, &abort);
- Node* condition = WordEqual(lhs, rhs);
- Branch(condition, &match, &no_match);
- Bind(&no_match);
+ Bind(&abort);
Abort(bailout_reason);
- Goto(&end);
- Bind(&match);
- Goto(&end);
- Bind(&end);
+ Goto(&ok);
+
+ Bind(&ok);
}
void InterpreterAssembler::TraceBytecode(Runtime::FunctionId function_id) {
@@ -673,20 +956,126 @@ void InterpreterAssembler::TraceBytecode(Runtime::FunctionId function_id) {
SmiTag(BytecodeOffset()), GetAccumulatorUnchecked());
}
+void InterpreterAssembler::TraceBytecodeDispatch(Node* target_bytecode) {
+ Node* counters_table = ExternalConstant(
+ ExternalReference::interpreter_dispatch_counters(isolate()));
+ Node* source_bytecode_table_index = IntPtrConstant(
+ static_cast<int>(bytecode_) * (static_cast<int>(Bytecode::kLast) + 1));
+
+ Node* counter_offset =
+ WordShl(IntPtrAdd(source_bytecode_table_index, target_bytecode),
+ IntPtrConstant(kPointerSizeLog2));
+ Node* old_counter =
+ Load(MachineType::IntPtr(), counters_table, counter_offset);
+
+ Label counter_ok(this), counter_saturated(this, Label::kDeferred);
+
+ Node* counter_reached_max = WordEqual(
+ old_counter, IntPtrConstant(std::numeric_limits<uintptr_t>::max()));
+ BranchIf(counter_reached_max, &counter_saturated, &counter_ok);
+
+ Bind(&counter_ok);
+ {
+ Node* new_counter = IntPtrAdd(old_counter, IntPtrConstant(1));
+ StoreNoWriteBarrier(MachineType::PointerRepresentation(), counters_table,
+ counter_offset, new_counter);
+ Goto(&counter_saturated);
+ }
+
+ Bind(&counter_saturated);
+}
+
// static
bool InterpreterAssembler::TargetSupportsUnalignedAccess() {
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
return false;
-#elif V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_PPC
- return CpuFeatures::IsSupported(UNALIGNED_ACCESSES);
#elif V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_X87 || \
- V8_TARGET_ARCH_S390
+ V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64 || \
+ V8_TARGET_ARCH_PPC
return true;
#else
#error "Unknown Architecture"
#endif
}
+Node* InterpreterAssembler::RegisterCount() {
+ Node* bytecode_array = LoadRegister(Register::bytecode_array());
+ Node* frame_size = LoadObjectField(
+ bytecode_array, BytecodeArray::kFrameSizeOffset, MachineType::Int32());
+ return Word32Sar(frame_size, Int32Constant(kPointerSizeLog2));
+}
+
+Node* InterpreterAssembler::ExportRegisterFile(Node* array) {
+ if (FLAG_debug_code) {
+ Node* array_size = LoadAndUntagFixedArrayBaseLength(array);
+ AbortIfWordNotEqual(
+ array_size, RegisterCount(), kInvalidRegisterFileInGenerator);
+ }
+
+ Variable var_index(this, MachineRepresentation::kWord32);
+ var_index.Bind(Int32Constant(0));
+
+ // Iterate over register file and write values into array.
+ // The mapping of register to array index must match that used in
+ // BytecodeGraphBuilder::VisitResumeGenerator.
+ Label loop(this, &var_index), done_loop(this);
+ Goto(&loop);
+ Bind(&loop);
+ {
+ Node* index = var_index.value();
+ Node* condition = Int32LessThan(index, RegisterCount());
+ GotoUnless(condition, &done_loop);
+
+ Node* reg_index =
+ Int32Sub(Int32Constant(Register(0).ToOperand()), index);
+ Node* value = LoadRegister(ChangeInt32ToIntPtr(reg_index));
+
+ StoreFixedArrayElement(array, index, value);
+
+ var_index.Bind(Int32Add(index, Int32Constant(1)));
+ Goto(&loop);
+ }
+ Bind(&done_loop);
+
+ return array;
+}
+
+Node* InterpreterAssembler::ImportRegisterFile(Node* array) {
+ if (FLAG_debug_code) {
+ Node* array_size = LoadAndUntagFixedArrayBaseLength(array);
+ AbortIfWordNotEqual(
+ array_size, RegisterCount(), kInvalidRegisterFileInGenerator);
+ }
+
+ Variable var_index(this, MachineRepresentation::kWord32);
+ var_index.Bind(Int32Constant(0));
+
+ // Iterate over array and write values into register file. Also erase the
+ // array contents to not keep them alive artificially.
+ Label loop(this, &var_index), done_loop(this);
+ Goto(&loop);
+ Bind(&loop);
+ {
+ Node* index = var_index.value();
+ Node* condition = Int32LessThan(index, RegisterCount());
+ GotoUnless(condition, &done_loop);
+
+ Node* value = LoadFixedArrayElement(array, index);
+
+ Node* reg_index =
+ Int32Sub(Int32Constant(Register(0).ToOperand()), index);
+ StoreRegister(value, ChangeInt32ToIntPtr(reg_index));
+
+ StoreFixedArrayElement(array, index, StaleRegisterConstant());
+
+ var_index.Bind(Int32Add(index, Int32Constant(1)));
+ Goto(&loop);
+ }
+ Bind(&done_loop);
+
+ return array;
+}
+
} // namespace interpreter
} // namespace internal
} // namespace v8