#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/full-codegen/full-codegen.h"
-#include "src/interpreter/bytecodes.h"
#include "src/runtime/runtime.h"
namespace v8 {
__ bind(&ok);
// If ok, push undefined as the initial value for all register file entries.
- // Note: there should always be at least one stack slot for the return
- // register in the register file.
Label loop_header;
+ Label loop_check;
__ LoadRoot(r9, Heap::kUndefinedValueRootIndex);
+ __ b(&loop_check, al);
__ bind(&loop_header);
// TODO(rmcilroy): Consider doing more than one push per loop iteration.
__ push(r9);
// Continue loop if not done.
+ __ bind(&loop_check);
__ sub(r4, r4, Operand(kPointerSize), SetCC);
- __ b(&loop_header, ne);
+ __ b(&loop_header, ge);
}
// TODO(rmcilroy): List of things not currently dealt with here but done in
__ bind(&ok);
}
- // Load bytecode offset and dispatch table into registers.
+ // Load accumulator, register file, bytecode offset, dispatch table into
+ // registers.
+ __ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
+ __ sub(kInterpreterRegisterFileRegister, fp,
+ Operand(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
__ mov(kInterpreterBytecodeOffsetRegister,
Operand(BytecodeArray::kHeaderSize - kHeapObjectTag));
__ LoadRoot(kInterpreterDispatchTableRegister,
Operand(FixedArray::kHeaderSize - kHeapObjectTag));
// Dispatch to the first bytecode handler for the function.
- __ ldrb(r0, MemOperand(kInterpreterBytecodeArrayRegister,
+ __ ldrb(r1, MemOperand(kInterpreterBytecodeArrayRegister,
kInterpreterBytecodeOffsetRegister));
- __ ldr(ip, MemOperand(kInterpreterDispatchTableRegister, r0, LSL,
+ __ ldr(ip, MemOperand(kInterpreterDispatchTableRegister, r1, LSL,
kPointerSizeLog2));
// TODO(rmcilroy): Make dispatch table point to code entrys to avoid untagging
// and header removal.
__ add(ip, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
- __ Jump(ip);
+ __ Call(ip);
}
// - Support profiler (specifically decrementing profiling_counter
// appropriately and calling out to HandleInterrupts if necessary).
- // Load return value into r0.
- __ ldr(r0, MemOperand(fp, -kPointerSize -
- StandardFrameConstants::kFixedFrameSizeFromFp));
+ // The return value is in accumulator, which is already in r0.
+
// Leave the frame (also dropping the register file).
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
// Drop receiver + arguments.
const Register kReturnRegister1 = {kRegister_r1_Code};
const Register kJSFunctionRegister = {kRegister_r1_Code};
const Register kContextRegister = {kRegister_r7_Code};
+const Register kInterpreterAccumulatorRegister = {kRegister_r0_Code};
+const Register kInterpreterRegisterFileRegister = {kRegister_r4_Code};
const Register kInterpreterBytecodeOffsetRegister = {kRegister_r5_Code};
const Register kInterpreterBytecodeArrayRegister = {kRegister_r6_Code};
const Register kInterpreterDispatchTableRegister = {kRegister_r8_Code};
__ Bind(&ok);
}
- // Load bytecode offset and dispatch table into registers.
+ // Load accumulator, register file, bytecode offset, dispatch table into
+ // registers.
+ __ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
+ __ Sub(kInterpreterRegisterFileRegister, fp,
+ Operand(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
__ Mov(kInterpreterBytecodeOffsetRegister,
Operand(BytecodeArray::kHeaderSize - kHeapObjectTag));
__ LoadRoot(kInterpreterDispatchTableRegister,
Operand(FixedArray::kHeaderSize - kHeapObjectTag));
// Dispatch to the first bytecode handler for the function.
- __ Ldrb(x0, MemOperand(kInterpreterBytecodeArrayRegister,
+ __ Ldrb(x1, MemOperand(kInterpreterBytecodeArrayRegister,
kInterpreterBytecodeOffsetRegister));
- __ Mov(x0, Operand(x0, LSL, kPointerSizeLog2));
- __ Ldr(ip0, MemOperand(kInterpreterDispatchTableRegister, x0));
+ __ Mov(x1, Operand(x1, LSL, kPointerSizeLog2));
+ __ Ldr(ip0, MemOperand(kInterpreterDispatchTableRegister, x1));
// TODO(rmcilroy): Make dispatch table point to code entrys to avoid untagging
// and header removal.
__ Add(ip0, ip0, Operand(Code::kHeaderSize - kHeapObjectTag));
- __ Jump(ip0);
+ __ Call(ip0);
}
// - Support profiler (specifically decrementing profiling_counter
// appropriately and calling out to HandleInterrupts if necessary).
- // Load return value into x0.
- __ ldr(x0, MemOperand(fp, -kPointerSize -
- StandardFrameConstants::kFixedFrameSizeFromFp));
+ // The return value is in accumulator, which is already in x0.
+
// Leave the frame (also dropping the register file).
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
// Drop receiver + arguments.
#define kReturnRegister1 x1
#define kJSFunctionRegister x1
#define kContextRegister cp
+#define kInterpreterAccumulatorRegister x0
+#define kInterpreterRegisterFileRegister x18
#define kInterpreterBytecodeOffsetRegister x19
#define kInterpreterBytecodeArrayRegister x20
#define kInterpreterDispatchTableRegister x21
Linkage::GetInterpreterDispatchDescriptor(zone), kMachPtr,
InstructionSelector::SupportedMachineOperatorFlags())),
end_node_(nullptr),
+ accumulator_(
+ raw_assembler_->Parameter(Linkage::kInterpreterAccumulatorParameter)),
code_generated_(false) {}
}
-Node* InterpreterAssembler::BytecodeArrayPointer() {
- return raw_assembler_->Parameter(Linkage::kInterpreterBytecodeArrayParameter);
+Node* InterpreterAssembler::GetAccumulator() {
+ return accumulator_;
}
-Node* InterpreterAssembler::BytecodeOffset() {
- return raw_assembler_->Parameter(
- Linkage::kInterpreterBytecodeOffsetParameter);
+void InterpreterAssembler::SetAccumulator(Node* value) {
+ accumulator_ = value;
}
-Node* InterpreterAssembler::DispatchTablePointer() {
- return raw_assembler_->Parameter(Linkage::kInterpreterDispatchTableParameter);
+Node* InterpreterAssembler::RegisterFileRawPointer() {
+ return raw_assembler_->Parameter(Linkage::kInterpreterRegisterFileParameter);
}
-Node* InterpreterAssembler::FramePointer() {
- return raw_assembler_->LoadFramePointer();
+Node* InterpreterAssembler::BytecodeArrayTaggedPointer() {
+ return raw_assembler_->Parameter(Linkage::kInterpreterBytecodeArrayParameter);
}
-Node* InterpreterAssembler::RegisterFrameOffset(int index) {
- DCHECK_LE(index, kMaxRegisterIndex);
- return Int32Constant(kFirstRegisterOffsetFromFp -
- (index << kPointerSizeLog2));
+Node* InterpreterAssembler::BytecodeOffset() {
+ return raw_assembler_->Parameter(
+ Linkage::kInterpreterBytecodeOffsetParameter);
}
-Node* InterpreterAssembler::RegisterFrameOffset(Node* index) {
- return raw_assembler_->IntPtrSub(
- Int32Constant(kFirstRegisterOffsetFromFp),
- raw_assembler_->WordShl(index, Int32Constant(kPointerSizeLog2)));
+Node* InterpreterAssembler::DispatchTableRawPointer() {
+ return raw_assembler_->Parameter(Linkage::kInterpreterDispatchTableParameter);
}
-Node* InterpreterAssembler::BytecodeOperand(int delta) {
- DCHECK_LT(delta, interpreter::Bytecodes::NumberOfOperands(bytecode_));
- return raw_assembler_->Load(
- kMachUint8, BytecodeArrayPointer(),
- raw_assembler_->IntPtrAdd(BytecodeOffset(), Int32Constant(1 + delta)));
+Node* InterpreterAssembler::RegisterFrameOffset(Node* index) {
+ return raw_assembler_->WordShl(index, Int32Constant(kPointerSizeLog2));
}
-Node* InterpreterAssembler::LoadRegister(int index) {
- return raw_assembler_->Load(kMachPtr, FramePointer(),
- RegisterFrameOffset(index));
+Node* InterpreterAssembler::LoadRegister(Node* reg_index) {
+ return raw_assembler_->Load(kMachPtr, RegisterFileRawPointer(),
+ RegisterFrameOffset(reg_index));
}
-Node* InterpreterAssembler::LoadRegister(Node* index) {
- return raw_assembler_->Load(kMachPtr, FramePointer(),
- RegisterFrameOffset(index));
+Node* InterpreterAssembler::StoreRegister(Node* value, Node* reg_index) {
+ return raw_assembler_->Store(kMachPtr, RegisterFileRawPointer(),
+ RegisterFrameOffset(reg_index), value);
}
-Node* InterpreterAssembler::StoreRegister(Node* value, int index) {
- return raw_assembler_->Store(kMachPtr, FramePointer(),
- RegisterFrameOffset(index), value);
+Node* InterpreterAssembler::BytecodeOperand(int delta) {
+ DCHECK_LT(delta, interpreter::Bytecodes::NumberOfOperands(bytecode_));
+ return raw_assembler_->Load(
+ kMachUint8, BytecodeArrayTaggedPointer(),
+ raw_assembler_->IntPtrAdd(BytecodeOffset(), Int32Constant(1 + delta)));
}
-Node* InterpreterAssembler::StoreRegister(Node* value, Node* index) {
- return raw_assembler_->Store(kMachPtr, FramePointer(),
- RegisterFrameOffset(index), value);
+Node* InterpreterAssembler::BytecodeOperandSignExtended(int delta) {
+ DCHECK_LT(delta, interpreter::Bytecodes::NumberOfOperands(bytecode_));
+ Node* load = raw_assembler_->Load(
+ kMachInt8, BytecodeArrayTaggedPointer(),
+ raw_assembler_->IntPtrAdd(BytecodeOffset(), Int32Constant(1 + delta)));
+ // Ensure that we sign extend to full pointer size
+ if (kPointerSize == 8) {
+ load = raw_assembler_->ChangeInt32ToInt64(load);
+ }
+ return load;
}
HeapConstant(Unique<HeapObject>::CreateImmovable(
isolate()->builtins()->InterpreterExitTrampoline()));
// If the order of the parameters you need to change the call signature below.
- STATIC_ASSERT(0 == Linkage::kInterpreterBytecodeOffsetParameter);
- STATIC_ASSERT(1 == Linkage::kInterpreterBytecodeArrayParameter);
- STATIC_ASSERT(2 == Linkage::kInterpreterDispatchTableParameter);
- Node* tail_call = graph()->NewNode(
- common()->TailCall(call_descriptor()), exit_trampoline_code_object,
- BytecodeOffset(), BytecodeArrayPointer(), DispatchTablePointer(),
- graph()->start(), graph()->start());
- schedule()->AddTailCall(raw_assembler_->CurrentBlock(), tail_call);
+ STATIC_ASSERT(0 == Linkage::kInterpreterAccumulatorParameter);
+ STATIC_ASSERT(1 == Linkage::kInterpreterRegisterFileParameter);
+ STATIC_ASSERT(2 == Linkage::kInterpreterBytecodeOffsetParameter);
+ STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter);
+ STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
+ Node* tail_call = raw_assembler_->TailCallInterpreterDispatch(
+ call_descriptor(), exit_trampoline_code_object, GetAccumulator(),
+ RegisterFileRawPointer(), BytecodeOffset(), BytecodeArrayTaggedPointer(),
+ DispatchTableRawPointer());
// This should always be the end node.
SetEndInput(tail_call);
}
void InterpreterAssembler::Dispatch() {
Node* new_bytecode_offset = Advance(interpreter::Bytecodes::Size(bytecode_));
Node* target_bytecode = raw_assembler_->Load(
- kMachUint8, BytecodeArrayPointer(), new_bytecode_offset);
+ kMachUint8, BytecodeArrayTaggedPointer(), new_bytecode_offset);
// TODO(rmcilroy): Create a code target dispatch table to avoid conversion
// from code object on every dispatch.
Node* target_code_object = raw_assembler_->Load(
- kMachPtr, DispatchTablePointer(),
+ kMachPtr, DispatchTableRawPointer(),
raw_assembler_->Word32Shl(target_bytecode,
Int32Constant(kPointerSizeLog2)));
// If the order of the parameters you need to change the call signature below.
- STATIC_ASSERT(0 == Linkage::kInterpreterBytecodeOffsetParameter);
- STATIC_ASSERT(1 == Linkage::kInterpreterBytecodeArrayParameter);
- STATIC_ASSERT(2 == Linkage::kInterpreterDispatchTableParameter);
- Node* tail_call = graph()->NewNode(
- common()->TailCall(call_descriptor()), target_code_object,
- new_bytecode_offset, BytecodeArrayPointer(), DispatchTablePointer(),
- graph()->start(), graph()->start());
- schedule()->AddTailCall(raw_assembler_->CurrentBlock(), tail_call);
+ STATIC_ASSERT(0 == Linkage::kInterpreterAccumulatorParameter);
+ STATIC_ASSERT(1 == Linkage::kInterpreterRegisterFileParameter);
+ STATIC_ASSERT(2 == Linkage::kInterpreterBytecodeOffsetParameter);
+ STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter);
+ STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
+ Node* tail_call = raw_assembler_->TailCallInterpreterDispatch(
+ call_descriptor(), target_code_object, GetAccumulator(),
+ RegisterFileRawPointer(), new_bytecode_offset,
+ BytecodeArrayTaggedPointer(), DispatchTableRawPointer());
// This should always be the end node.
SetEndInput(tail_call);
}
void InterpreterAssembler::End() {
DCHECK(end_node_);
// TODO(rmcilroy): Support more than 1 end input.
- Node* end = graph()->NewNode(common()->End(1), end_node_);
+ Node* end = graph()->NewNode(raw_assembler_->common()->End(1), end_node_);
graph()->SetEnd(end);
}
}
-MachineOperatorBuilder* InterpreterAssembler::machine() {
- return raw_assembler_->machine();
-}
-
-
-CommonOperatorBuilder* InterpreterAssembler::common() {
- return raw_assembler_->common();
-}
-
-
Node* InterpreterAssembler::Int32Constant(int value) {
return raw_assembler_->Int32Constant(value);
}
return raw_assembler_->HeapConstant(object);
}
-
} // namespace interpreter
} // namespace internal
} // namespace v8
namespace compiler {
class CallDescriptor;
-class CommonOperatorBuilder;
class Graph;
-class MachineOperatorBuilder;
class Node;
class Operator;
class RawMachineAssembler;
Handle<Code> GenerateCode();
+ // Accumulator.
+ Node* GetAccumulator();
+ void SetAccumulator(Node* value);
+
+ // Loads from and stores to the interpreter register file.
+ Node* LoadRegister(Node* reg_index);
+ Node* StoreRegister(Node* value, Node* reg_index);
+
// Constants.
Node* Int32Constant(int value);
Node* NumberConstant(double value);
Node* HeapConstant(Unique<HeapObject> object);
- // Returns the bytecode operand |index| for the current bytecode.
- Node* BytecodeOperand(int index);
-
- // Loads from and stores to the interpreter register file.
- Node* LoadRegister(int index);
- Node* LoadRegister(Node* index);
- Node* StoreRegister(Node* value, int index);
- Node* StoreRegister(Node* value, Node* index);
-
// Returns from the function.
void Return();
// Dispatch to the bytecode.
void Dispatch();
- protected:
- static const int kFirstRegisterOffsetFromFp =
- -kPointerSize - StandardFrameConstants::kFixedFrameSizeFromFp;
-
- // TODO(rmcilroy): Increase this when required.
- static const int kMaxRegisterIndex = 255;
+ Node* BytecodeOperand(int index);
+ Node* BytecodeOperandSignExtended(int index);
+ protected:
// Close the graph.
void End();
Graph* graph();
private:
+ // Returns a raw pointer to start of the register file on the stack.
+ Node* RegisterFileRawPointer();
// Returns a tagged pointer to the current function's BytecodeArray object.
- Node* BytecodeArrayPointer();
+ Node* BytecodeArrayTaggedPointer();
// Returns the offset from the BytecodeArrayPointer of the current bytecode.
Node* BytecodeOffset();
// Returns a pointer to first entry in the interpreter dispatch table.
- Node* DispatchTablePointer();
- // Returns the frame pointer for the current function.
- Node* FramePointer();
+ Node* DispatchTableRawPointer();
- // Returns the offset of register |index|.
- Node* RegisterFrameOffset(int index);
+ // Returns the offset of register |index| relative to RegisterFilePointer().
Node* RegisterFrameOffset(Node* index);
// Returns BytecodeOffset() advanced by delta bytecodes. Note: this does not
// Private helpers which delegate to RawMachineAssembler.
Isolate* isolate();
Schedule* schedule();
- MachineOperatorBuilder* machine();
- CommonOperatorBuilder* common();
interpreter::Bytecode bytecode_;
base::SmartPointer<RawMachineAssembler> raw_assembler_;
Node* end_node_;
+ Node* accumulator_;
bool code_generated_;
DISALLOW_COPY_AND_ASSIGN(InterpreterAssembler);
CallDescriptor* Linkage::GetInterpreterDispatchDescriptor(Zone* zone) {
- MachineSignature::Builder types(zone, 0, 3);
- LocationSignature::Builder locations(zone, 0, 3);
+ MachineSignature::Builder types(zone, 0, 5);
+ LocationSignature::Builder locations(zone, 0, 5);
// Add registers for fixed parameters passed via interpreter dispatch.
- STATIC_ASSERT(0 == Linkage::kInterpreterBytecodeOffsetParameter);
+ STATIC_ASSERT(0 == Linkage::kInterpreterAccumulatorParameter);
+ types.AddParam(kMachAnyTagged);
+ locations.AddParam(regloc(kInterpreterAccumulatorRegister));
+
+ STATIC_ASSERT(1 == Linkage::kInterpreterRegisterFileParameter);
+ types.AddParam(kMachPtr);
+ locations.AddParam(regloc(kInterpreterRegisterFileRegister));
+
+ STATIC_ASSERT(2 == Linkage::kInterpreterBytecodeOffsetParameter);
types.AddParam(kMachIntPtr);
locations.AddParam(regloc(kInterpreterBytecodeOffsetRegister));
- STATIC_ASSERT(1 == Linkage::kInterpreterBytecodeArrayParameter);
+ STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter);
types.AddParam(kMachAnyTagged);
locations.AddParam(regloc(kInterpreterBytecodeArrayRegister));
- STATIC_ASSERT(2 == Linkage::kInterpreterDispatchTableParameter);
+ STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
types.AddParam(kMachPtr);
locations.AddParam(regloc(kInterpreterDispatchTableRegister));
// Special parameter indices used to pass fixed register data through
// interpreter dispatches.
- static const int kInterpreterBytecodeOffsetParameter = 0;
- static const int kInterpreterBytecodeArrayParameter = 1;
- static const int kInterpreterDispatchTableParameter = 2;
+ static const int kInterpreterAccumulatorParameter = 0;
+ static const int kInterpreterRegisterFileParameter = 1;
+ static const int kInterpreterBytecodeOffsetParameter = 2;
+ static const int kInterpreterBytecodeArrayParameter = 3;
+ static const int kInterpreterDispatchTableParameter = 4;
private:
CallDescriptor* const incoming_;
}
+Node* RawMachineAssembler::TailCallInterpreterDispatch(
+ const CallDescriptor* call_descriptor, Node* target, Node* arg1, Node* arg2,
+ Node* arg3, Node* arg4, Node* arg5) {
+ Node* tail_call =
+ graph()->NewNode(common()->TailCall(call_descriptor), target, arg1, arg2,
+ arg3, arg4, arg5, graph()->start(), graph()->start());
+ schedule()->AddTailCall(CurrentBlock(), tail_call);
+ return tail_call;
+}
+
+
void RawMachineAssembler::Bind(Label* label) {
DCHECK(current_block_ == nullptr);
DCHECK(!label->bound_);
MachineType arg7_type, Node* function, Node* arg0,
Node* arg1, Node* arg2, Node* arg3, Node* arg4,
Node* arg5, Node* arg6, Node* arg7);
+ Node* TailCallInterpreterDispatch(const CallDescriptor* call_descriptor,
+ Node* target, Node* arg1, Node* arg2,
+ Node* arg3, Node* arg4, Node* arg5);
// ===========================================================================
// The following utility methods deal with control flow, hence might switch
// Get the bytecode array from the function object and load the pointer to the
// first entry into edi (InterpreterBytecodeRegister).
- __ mov(edi, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
- __ mov(edi, FieldOperand(edi, SharedFunctionInfo::kFunctionDataOffset));
+ __ mov(eax, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(kInterpreterBytecodeArrayRegister,
+ FieldOperand(eax, SharedFunctionInfo::kFunctionDataOffset));
if (FLAG_debug_code) {
// Check function data field is actually a BytecodeArray object.
- __ AssertNotSmi(edi);
- __ CmpObjectType(edi, BYTECODE_ARRAY_TYPE, eax);
+ __ AssertNotSmi(kInterpreterBytecodeArrayRegister);
+ __ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE,
+ eax);
__ Assert(equal, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
}
// Allocate the local and temporary register file on the stack.
{
// Load frame size from the BytecodeArray object.
- __ mov(ebx, FieldOperand(edi, BytecodeArray::kFrameSizeOffset));
+ __ mov(ebx, FieldOperand(kInterpreterBytecodeArrayRegister,
+ BytecodeArray::kFrameSizeOffset));
// Do a stack check to ensure we don't go over the limit.
Label ok;
ExternalReference stack_limit =
ExternalReference::address_of_real_stack_limit(masm->isolate());
__ cmp(ecx, Operand::StaticVariable(stack_limit));
- __ j(above_equal, &ok, Label::kNear);
+ __ j(above_equal, &ok);
__ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION);
__ bind(&ok);
// If ok, push undefined as the initial value for all register file entries.
- // Note: there should always be at least one stack slot for the return
- // register in the register file.
Label loop_header;
+ Label loop_check;
__ mov(eax, Immediate(masm->isolate()->factory()->undefined_value()));
+ __ jmp(&loop_check);
__ bind(&loop_header);
// TODO(rmcilroy): Consider doing more than one push per loop iteration.
__ push(eax);
// Continue loop if not done.
+ __ bind(&loop_check);
__ sub(ebx, Immediate(kPointerSize));
- __ j(not_equal, &loop_header, Label::kNear);
+ __ j(greater_equal, &loop_header);
}
// TODO(rmcilroy): List of things not currently dealt with here but done in
ExternalReference stack_limit =
ExternalReference::address_of_stack_limit(masm->isolate());
__ cmp(esp, Operand::StaticVariable(stack_limit));
- __ j(above_equal, &ok, Label::kNear);
+ __ j(above_equal, &ok);
__ CallRuntime(Runtime::kStackGuard, 0);
__ bind(&ok);
}
- // Load bytecode offset and dispatch table into registers.
- __ mov(ecx, Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag));
+ // Load accumulator, register file, bytecode offset, dispatch table into
+ // registers.
+ __ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
+ __ mov(kInterpreterRegisterFileRegister, ebp);
+ __ sub(
+ kInterpreterRegisterFileRegister,
+ Immediate(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
+ __ mov(kInterpreterBytecodeOffsetRegister,
+ Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag));
// Since the dispatch table root might be set after builtins are generated,
// load directly from the roots table.
- __ LoadRoot(ebx, Heap::kInterpreterTableRootIndex);
- __ add(ebx, Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ LoadRoot(kInterpreterDispatchTableRegister,
+ Heap::kInterpreterTableRootIndex);
+ __ add(kInterpreterDispatchTableRegister,
+ Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
+
+ // TODO(rmcilroy) Push our context as a stack located parameter of the
+ // bytecode handler.
// Dispatch to the first bytecode handler for the function.
- __ movzx_b(eax, Operand(edi, ecx, times_1, 0));
- __ mov(eax, Operand(ebx, eax, times_pointer_size, 0));
+ __ movzx_b(esi, Operand(kInterpreterBytecodeArrayRegister,
+ kInterpreterBytecodeOffsetRegister, times_1, 0));
+ __ mov(esi, Operand(kInterpreterDispatchTableRegister, esi,
+ times_pointer_size, 0));
// TODO(rmcilroy): Make dispatch table point to code entrys to avoid untagging
// and header removal.
- __ add(eax, Immediate(Code::kHeaderSize - kHeapObjectTag));
- __ jmp(eax);
+ __ add(esi, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ __ call(esi);
}
// - Support profiler (specifically decrementing profiling_counter
// appropriately and calling out to HandleInterrupts if necessary).
- // Load return value into r0.
- __ mov(eax, Operand(ebp, -kPointerSize -
- StandardFrameConstants::kFixedFrameSizeFromFp));
+ // The return value is in accumulator, which is already in rax.
+
// Leave the frame (also dropping the register file).
__ leave();
// Return droping receiver + arguments.
const Register kReturnRegister1 = {kRegister_edx_Code};
const Register kJSFunctionRegister = {kRegister_edi_Code};
const Register kContextRegister = {kRegister_esi_Code};
+const Register kInterpreterAccumulatorRegister = {kRegister_eax_Code};
+const Register kInterpreterRegisterFileRegister = {kRegister_edx_Code};
const Register kInterpreterBytecodeOffsetRegister = {kRegister_ecx_Code};
const Register kInterpreterBytecodeArrayRegister = {kRegister_edi_Code};
const Register kInterpreterDispatchTableRegister = {kRegister_ebx_Code};
__ bind(&ok);
// If ok, push undefined as the initial value for all register file entries.
- // Note: there should always be at least one stack slot for the return
- // register in the register file.
Label loop_header;
+ Label loop_check;
__ LoadRoot(t1, Heap::kUndefinedValueRootIndex);
+ __ Branch(&loop_check);
__ bind(&loop_header);
// TODO(rmcilroy): Consider doing more than one push per loop iteration.
__ push(t1);
// Continue loop if not done.
+ __ bind(&loop_check);
__ Subu(t0, t0, Operand(kPointerSize));
__ Branch(&loop_header, ge, t0, Operand(zero_reg));
}
}
// Load bytecode offset and dispatch table into registers.
+ __ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
+ __ Subu(
+ kInterpreterRegisterFileRegister, fp,
+ Operand(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
__ li(kInterpreterBytecodeOffsetRegister,
Operand(BytecodeArray::kHeaderSize - kHeapObjectTag));
__ LoadRoot(kInterpreterDispatchTableRegister,
// TODO(rmcilroy): Make dispatch table point to code entrys to avoid untagging
// and header removal.
__ Addu(at, at, Operand(Code::kHeaderSize - kHeapObjectTag));
- __ Jump(at);
+ __ Call(at);
}
// - Support profiler (specifically decrementing profiling_counter
// appropriately and calling out to HandleInterrupts if necessary).
- // Load return value into v0.
- __ lw(v0, MemOperand(fp, -kPointerSize -
- StandardFrameConstants::kFixedFrameSizeFromFp));
+ // The return value is in accumulator, which is already in v0.
+
// Leave the frame (also dropping the register file).
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
// Drop receiver + arguments.
const Register kReturnRegister1 = {kRegister_v1_Code};
const Register kJSFunctionRegister = {kRegister_a1_Code};
const Register kContextRegister = {Register::kCpRegister};
+const Register kInterpreterAccumulatorRegister = {kRegister_v0_Code};
+const Register kInterpreterRegisterFileRegister = {kRegister_t3_Code};
const Register kInterpreterBytecodeOffsetRegister = {kRegister_t4_Code};
const Register kInterpreterBytecodeArrayRegister = {kRegister_t5_Code};
const Register kInterpreterDispatchTableRegister = {kRegister_t6_Code};
// Core register.
struct Register {
static const int kNumRegisters = v8::internal::kNumRegisters;
- static const int kMaxNumAllocatableRegisters = 14; // v0 through t6 and cp.
+ static const int kMaxNumAllocatableRegisters = 14; // v0 through t2 and cp.
static const int kSizeInBytes = 8;
static const int kCpRegister = 23; // cp (s7) is the 23rd register.
__ bind(&ok);
// If ok, push undefined as the initial value for all register file entries.
- // Note: there should always be at least one stack slot for the return
- // register in the register file.
Label loop_header;
+ Label loop_check;
__ LoadRoot(a5, Heap::kUndefinedValueRootIndex);
+ __ Branch(&loop_check);
__ bind(&loop_header);
// TODO(rmcilroy): Consider doing more than one push per loop iteration.
__ push(a5);
// Continue loop if not done.
+ __ bind(&loop_check);
__ Dsubu(a4, a4, Operand(kPointerSize));
__ Branch(&loop_header, ge, a4, Operand(zero_reg));
}
}
// Load bytecode offset and dispatch table into registers.
+ __ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
+ __ Dsubu(
+ kInterpreterRegisterFileRegister, fp,
+ Operand(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
__ li(kInterpreterBytecodeOffsetRegister,
Operand(BytecodeArray::kHeaderSize - kHeapObjectTag));
__ LoadRoot(kInterpreterDispatchTableRegister,
// TODO(rmcilroy): Make dispatch table point to code entrys to avoid untagging
// and header removal.
__ Daddu(at, at, Operand(Code::kHeaderSize - kHeapObjectTag));
- __ Jump(at);
+ __ Call(at);
}
// - Support profiler (specifically decrementing profiling_counter
// appropriately and calling out to HandleInterrupts if necessary).
- // Load return value into v0.
- __ ld(v0, MemOperand(fp, -kPointerSize -
- StandardFrameConstants::kFixedFrameSizeFromFp));
+ // The return value is in accumulator, which is already in v0.
+
// Leave the frame (also dropping the register file).
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
// Drop receiver + arguments.
const Register kReturnRegister1 = {kRegister_v1_Code};
const Register kJSFunctionRegister = {kRegister_a1_Code};
const Register kContextRegister = {kRegister_s7_Code};
+const Register kInterpreterAccumulatorRegister = {kRegister_v0_Code};
+const Register kInterpreterRegisterFileRegister = {kRegister_a7_Code};
const Register kInterpreterBytecodeOffsetRegister = {kRegister_t0_Code};
const Register kInterpreterBytecodeArrayRegister = {kRegister_t1_Code};
const Register kInterpreterDispatchTableRegister = {kRegister_t2_Code};
// Get the bytecode array from the function object and load the pointer to the
// first entry into edi (InterpreterBytecodeRegister).
- __ movp(r14, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
- __ movp(r14, FieldOperand(r14, SharedFunctionInfo::kFunctionDataOffset));
+ __ movp(rax, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ movp(kInterpreterBytecodeArrayRegister,
+ FieldOperand(rax, SharedFunctionInfo::kFunctionDataOffset));
if (FLAG_debug_code) {
// Check function data field is actually a BytecodeArray object.
- __ AssertNotSmi(r14);
- __ CmpObjectType(r14, BYTECODE_ARRAY_TYPE, rax);
+ __ AssertNotSmi(kInterpreterBytecodeArrayRegister);
+ __ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE,
+ rax);
__ Assert(equal, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
}
// Allocate the local and temporary register file on the stack.
{
// Load frame size from the BytecodeArray object.
- __ movl(rcx, FieldOperand(r14, BytecodeArray::kFrameSizeOffset));
+ __ movl(rcx, FieldOperand(kInterpreterBytecodeArrayRegister,
+ BytecodeArray::kFrameSizeOffset));
// Do a stack check to ensure we don't go over the limit.
Label ok;
__ bind(&ok);
// If ok, push undefined as the initial value for all register file entries.
- // Note: there should always be at least one stack slot for the return
- // register in the register file.
Label loop_header;
+ Label loop_check;
__ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
+ __ j(always, &loop_check);
__ bind(&loop_header);
// TODO(rmcilroy): Consider doing more than one push per loop iteration.
__ Push(rdx);
// Continue loop if not done.
+ __ bind(&loop_check);
__ subp(rcx, Immediate(kPointerSize));
- __ j(not_equal, &loop_header, Label::kNear);
+ __ j(greater_equal, &loop_header, Label::kNear);
}
// TODO(rmcilroy): List of things not currently dealt with here but done in
__ bind(&ok);
}
- // Load bytecode offset and dispatch table into registers.
- __ movp(r12, Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag));
- __ LoadRoot(r15, Heap::kInterpreterTableRootIndex);
- __ addp(r15, Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
+ // Load accumulator, register file, bytecode offset, dispatch table into
+ // registers.
+ __ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
+ __ movp(kInterpreterRegisterFileRegister, rbp);
+ __ subp(
+ kInterpreterRegisterFileRegister,
+ Immediate(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
+ __ movp(kInterpreterBytecodeOffsetRegister,
+ Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag));
+ __ LoadRoot(kInterpreterDispatchTableRegister,
+ Heap::kInterpreterTableRootIndex);
+ __ addp(kInterpreterDispatchTableRegister,
+ Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
// Dispatch to the first bytecode handler for the function.
- __ movzxbp(rax, Operand(r14, r12, times_1, 0));
- __ movp(rax, Operand(r15, rax, times_pointer_size, 0));
+ __ movzxbp(rbx, Operand(kInterpreterBytecodeArrayRegister,
+ kInterpreterBytecodeOffsetRegister, times_1, 0));
+ __ movp(rbx, Operand(kInterpreterDispatchTableRegister, rbx,
+ times_pointer_size, 0));
// TODO(rmcilroy): Make dispatch table point to code entrys to avoid untagging
// and header removal.
- __ addp(rax, Immediate(Code::kHeaderSize - kHeapObjectTag));
- __ jmp(rax);
+ __ addp(rbx, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ __ call(rbx);
}
// - Support profiler (specifically decrementing profiling_counter
// appropriately and calling out to HandleInterrupts if necessary).
- // Load return value into r0.
- __ movp(rax, Operand(rbp, -kPointerSize -
- StandardFrameConstants::kFixedFrameSizeFromFp));
+ // The return value is in accumulator, which is already in rax.
+
// Leave the frame (also dropping the register file).
__ leave();
// Return droping receiver + arguments.
const Register kReturnRegister1 = {kRegister_rdx_Code};
const Register kJSFunctionRegister = {kRegister_rdi_Code};
const Register kContextRegister = {kRegister_rsi_Code};
+const Register kInterpreterAccumulatorRegister = {kRegister_rax_Code};
+const Register kInterpreterRegisterFileRegister = {kRegister_r11_Code};
const Register kInterpreterBytecodeOffsetRegister = {kRegister_r12_Code};
const Register kInterpreterBytecodeArrayRegister = {kRegister_r14_Code};
const Register kInterpreterDispatchTableRegister = {kRegister_r15_Code};
handles.main_isolate()->factory()->undefined_value();
BytecodeArrayBuilder builder(handles.main_isolate());
- // TODO(rmcilroy) set to 0 once BytecodeArray update to allow zero size
- // register file.
- builder.set_locals_count(1);
+ builder.set_locals_count(0);
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
#include "test/unittests/compiler/compiler-test-utils.h"
#include "test/unittests/compiler/node-test-utils.h"
+using ::testing::_;
+
namespace v8 {
namespace internal {
namespace compiler {
EXPECT_THAT(
tail_call_node,
IsTailCall(m.call_descriptor(), code_target_matcher,
+ IsParameter(Linkage::kInterpreterAccumulatorParameter),
+ IsParameter(Linkage::kInterpreterRegisterFileParameter),
next_bytecode_offset_matcher,
IsParameter(Linkage::kInterpreterBytecodeArrayParameter),
IsParameter(Linkage::kInterpreterDispatchTableParameter),
EXPECT_THAT(
tail_call_node,
IsTailCall(m.call_descriptor(), IsHeapConstant(exit_trampoline),
+ IsParameter(Linkage::kInterpreterAccumulatorParameter),
+ IsParameter(Linkage::kInterpreterRegisterFileParameter),
IsParameter(Linkage::kInterpreterBytecodeOffsetParameter),
IsParameter(Linkage::kInterpreterBytecodeArrayParameter),
IsParameter(Linkage::kInterpreterDispatchTableParameter),
}
-TARGET_TEST_F(InterpreterAssemblerTest, LoadRegisterFixed) {
+TARGET_TEST_F(InterpreterAssemblerTest, BytecodeOperandSignExtended) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
- for (int i = 0; i < m.kMaxRegisterIndex; i++) {
- Node* load_reg_node = m.LoadRegister(i);
- EXPECT_THAT(load_reg_node,
- m.IsLoad(kMachPtr, IsLoadFramePointer(),
- IsInt32Constant(m.kFirstRegisterOffsetFromFp -
- (i << kPointerSizeLog2))));
+ int number_of_operands = interpreter::Bytecodes::NumberOfOperands(bytecode);
+ for (int i = 0; i < number_of_operands; i++) {
+ Node* load_arg_node = m.BytecodeOperandSignExtended(i);
+ Matcher<Node*> load_matcher = m.IsLoad(
+ kMachInt8, IsParameter(Linkage::kInterpreterBytecodeArrayParameter),
+ IsIntPtrAdd(IsParameter(Linkage::kInterpreterBytecodeOffsetParameter),
+ IsInt32Constant(1 + i)));
+ if (kPointerSize == 8) {
+ load_matcher = IsChangeInt32ToInt64(load_matcher);
+ }
+ EXPECT_THAT(load_arg_node, load_matcher);
}
}
}
-TARGET_TEST_F(InterpreterAssemblerTest, LoadRegister) {
+TARGET_TEST_F(InterpreterAssemblerTest, GetSetAccumulator) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
- Node* reg_index_node = m.Int32Constant(44);
- Node* load_reg_node = m.LoadRegister(reg_index_node);
- EXPECT_THAT(
- load_reg_node,
- m.IsLoad(kMachPtr, IsLoadFramePointer(),
- IsIntPtrSub(IsInt32Constant(m.kFirstRegisterOffsetFromFp),
- IsWordShl(reg_index_node,
- IsInt32Constant(kPointerSizeLog2)))));
+ // Should be incoming accumulator if not set.
+ EXPECT_THAT(m.GetAccumulator(),
+ IsParameter(Linkage::kInterpreterAccumulatorParameter));
+
+ // Should be set by SedtAccumulator.
+ Node* accumulator_value_1 = m.Int32Constant(0xdeadbeef);
+ m.SetAccumulator(accumulator_value_1);
+ EXPECT_THAT(m.GetAccumulator(), accumulator_value_1);
+ Node* accumulator_value_2 = m.Int32Constant(42);
+ m.SetAccumulator(accumulator_value_2);
+ EXPECT_THAT(m.GetAccumulator(), accumulator_value_2);
+
+ // Should be passed to next bytecode handler on dispatch.
+ m.Dispatch();
+ Graph* graph = m.GetCompletedGraph();
+
+ Node* end = graph->end();
+ EXPECT_EQ(1, end->InputCount());
+ Node* tail_call_node = end->InputAt(0);
+
+ EXPECT_THAT(tail_call_node,
+ IsTailCall(m.call_descriptor(), _, accumulator_value_2, _, _, _,
+ _, graph->start(), graph->start()));
}
}
-TARGET_TEST_F(InterpreterAssemblerTest, StoreRegisterFixed) {
+TARGET_TEST_F(InterpreterAssemblerTest, LoadRegister) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
- Node* store_value = m.Int32Constant(0xdeadbeef);
- for (int i = 0; i < m.kMaxRegisterIndex; i++) {
- Node* store_reg_node = m.StoreRegister(store_value, i);
- EXPECT_THAT(store_reg_node,
- m.IsStore(StoreRepresentation(kMachPtr, kNoWriteBarrier),
- IsLoadFramePointer(),
- IsInt32Constant(m.kFirstRegisterOffsetFromFp -
- (i << kPointerSizeLog2)),
- store_value));
- }
+ Node* reg_index_node = m.Int32Constant(44);
+ Node* load_reg_node = m.LoadRegister(reg_index_node);
+ EXPECT_THAT(
+ load_reg_node,
+ m.IsLoad(kMachPtr,
+ IsParameter(Linkage::kInterpreterRegisterFileParameter),
+ IsWordShl(reg_index_node, IsInt32Constant(kPointerSizeLog2))));
}
}
EXPECT_THAT(
store_reg_node,
m.IsStore(StoreRepresentation(kMachPtr, kNoWriteBarrier),
- IsLoadFramePointer(),
- IsIntPtrSub(IsInt32Constant(m.kFirstRegisterOffsetFromFp),
- IsWordShl(reg_index_node,
- IsInt32Constant(kPointerSizeLog2))),
+ IsParameter(Linkage::kInterpreterRegisterFileParameter),
+ IsWordShl(reg_index_node, IsInt32Constant(kPointerSizeLog2)),
store_value));
}
}
using InterpreterAssembler::call_descriptor;
using InterpreterAssembler::graph;
- using InterpreterAssembler::kMaxRegisterIndex;
- using InterpreterAssembler::kFirstRegisterOffsetFromFp;
private:
DISALLOW_COPY_AND_ASSIGN(InterpreterAssemblerForTest);
}
+Matcher<Node*> IsTailCall(
+ const Matcher<CallDescriptor const*>& descriptor_matcher,
+ const Matcher<Node*>& value0_matcher, const Matcher<Node*>& value1_matcher,
+ const Matcher<Node*>& value2_matcher, const Matcher<Node*>& value3_matcher,
+ const Matcher<Node*>& value4_matcher, const Matcher<Node*>& effect_matcher,
+ const Matcher<Node*>& control_matcher) {
+ std::vector<Matcher<Node*>> value_matchers;
+ value_matchers.push_back(value0_matcher);
+ value_matchers.push_back(value1_matcher);
+ value_matchers.push_back(value2_matcher);
+ value_matchers.push_back(value3_matcher);
+ value_matchers.push_back(value4_matcher);
+ return MakeMatcher(new IsTailCallMatcher(descriptor_matcher, value_matchers,
+ effect_matcher, control_matcher));
+}
+
+
+Matcher<Node*> IsTailCall(
+ const Matcher<CallDescriptor const*>& descriptor_matcher,
+ const Matcher<Node*>& value0_matcher, const Matcher<Node*>& value1_matcher,
+ const Matcher<Node*>& value2_matcher, const Matcher<Node*>& value3_matcher,
+ const Matcher<Node*>& value4_matcher, const Matcher<Node*>& value5_matcher,
+ const Matcher<Node*>& effect_matcher,
+ const Matcher<Node*>& control_matcher) {
+ std::vector<Matcher<Node*>> value_matchers;
+ value_matchers.push_back(value0_matcher);
+ value_matchers.push_back(value1_matcher);
+ value_matchers.push_back(value2_matcher);
+ value_matchers.push_back(value3_matcher);
+ value_matchers.push_back(value4_matcher);
+ value_matchers.push_back(value5_matcher);
+ return MakeMatcher(new IsTailCallMatcher(descriptor_matcher, value_matchers,
+ effect_matcher, control_matcher));
+}
+
+
Matcher<Node*> IsReferenceEqual(const Matcher<Type*>& type_matcher,
const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher) {
const Matcher<Node*>& value2_matcher, const Matcher<Node*>& value3_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
+Matcher<Node*> IsTailCall(
+ const Matcher<CallDescriptor const*>& descriptor_matcher,
+ const Matcher<Node*>& value0_matcher, const Matcher<Node*>& value1_matcher,
+ const Matcher<Node*>& value2_matcher, const Matcher<Node*>& value3_matcher,
+ const Matcher<Node*>& value4_matcher, const Matcher<Node*>& effect_matcher,
+ const Matcher<Node*>& control_matcher);
+Matcher<Node*> IsTailCall(
+ const Matcher<CallDescriptor const*>& descriptor_matcher,
+ const Matcher<Node*>& value0_matcher, const Matcher<Node*>& value1_matcher,
+ const Matcher<Node*>& value2_matcher, const Matcher<Node*>& value3_matcher,
+ const Matcher<Node*>& value4_matcher, const Matcher<Node*>& value5_matcher,
+ const Matcher<Node*>& effect_matcher,
+ const Matcher<Node*>& control_matcher);
+
Matcher<Node*> IsBooleanNot(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsReferenceEqual(const Matcher<Type*>& type_matcher,