[interpreter] Switch passing of new.target to register.
This passes the new.target value in a register instead of through a side-channel via the construct stub. The interpreter entry trampoline stores this value in a bytecode register so that it can be accessed directly by the interpreter. The size of the interpreter stack frame hence grows by one slot. R=oth@chromium.org BUG=v8:4544 LOG=n Review URL: https://codereview.chromium.org/1469313002 Cr-Commit-Position: refs/heads/master@{#32264}
This commit is contained in:
parent
5335e7d55a
commit
3d004eeab2
@ -785,6 +785,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
|
||||
//
|
||||
// The live registers are:
|
||||
// o r1: the JS function object being called.
|
||||
// o r3: the new target
|
||||
// o cp: our context
|
||||
// o pp: the caller's constant pool pointer (if enabled)
|
||||
// o fp: the caller's frame pointer
|
||||
@ -802,6 +803,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
FrameScope frame_scope(masm, StackFrame::MANUAL);
|
||||
__ PushFixedFrame(r1);
|
||||
__ add(fp, sp, Operand(StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ push(r3);
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into kInterpreterBytecodeRegister.
|
||||
@ -870,7 +872,8 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
// registers.
|
||||
__ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
|
||||
__ sub(kInterpreterRegisterFileRegister, fp,
|
||||
Operand(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
Operand(2 * kPointerSize +
|
||||
StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ mov(kInterpreterBytecodeOffsetRegister,
|
||||
Operand(BytecodeArray::kHeaderSize - kHeapObjectTag));
|
||||
__ LoadRoot(kInterpreterDispatchTableRegister,
|
||||
|
@ -804,6 +804,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
|
||||
//
|
||||
// The live registers are:
|
||||
// - x1: the JS function object being called.
|
||||
// - x3: the new target
|
||||
// - cp: our context.
|
||||
// - fp: our caller's frame pointer.
|
||||
// - jssp: stack pointer.
|
||||
@ -820,6 +821,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
FrameScope frame_scope(masm, StackFrame::MANUAL);
|
||||
__ Push(lr, fp, cp, x1);
|
||||
__ Add(fp, jssp, StandardFrameConstants::kFixedFrameSizeFromFp);
|
||||
__ Push(x3);
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into kInterpreterBytecodeRegister.
|
||||
@ -885,7 +887,8 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
// registers.
|
||||
__ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
|
||||
__ Sub(kInterpreterRegisterFileRegister, fp,
|
||||
Operand(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
Operand(2 * kPointerSize +
|
||||
StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ Mov(kInterpreterBytecodeOffsetRegister,
|
||||
Operand(BytecodeArray::kHeaderSize - kHeapObjectTag));
|
||||
__ LoadRoot(kInterpreterDispatchTableRegister,
|
||||
|
@ -758,20 +758,6 @@ bool JavaScriptFrame::HasInlinedFrames() const {
|
||||
}
|
||||
|
||||
|
||||
Object* JavaScriptFrame::GetNewTarget() const {
|
||||
DCHECK(!HasInlinedFrames());
|
||||
Address fp = caller_fp();
|
||||
if (has_adapted_arguments()) {
|
||||
// Skip the arguments adaptor frame and look at the real caller.
|
||||
fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset);
|
||||
}
|
||||
DCHECK(IsConstructFrame(fp));
|
||||
STATIC_ASSERT(ConstructFrameConstants::kNewTargetOffset ==
|
||||
StandardFrameConstants::kExpressionsOffset - 3 * kPointerSize);
|
||||
return GetExpression(fp, 3);
|
||||
}
|
||||
|
||||
|
||||
int JavaScriptFrame::GetArgumentsLength() const {
|
||||
// If there is an arguments adaptor frame get the arguments length from it.
|
||||
if (has_adapted_arguments()) {
|
||||
|
11
src/frames.h
11
src/frames.h
@ -180,9 +180,10 @@ class InterpreterFrameConstants : public AllStatic {
|
||||
public:
|
||||
// Register file pointer relative.
|
||||
static const int kLastParamFromRegisterPointer =
|
||||
StandardFrameConstants::kFixedFrameSize + kPointerSize;
|
||||
static const int kFunctionFromRegisterPointer = kPointerSize;
|
||||
static const int kContextFromRegisterPointer = 2 * kPointerSize;
|
||||
StandardFrameConstants::kFixedFrameSize + 2 * kPointerSize;
|
||||
static const int kNewTargetFromRegisterPointer = kPointerSize;
|
||||
static const int kFunctionFromRegisterPointer = 2 * kPointerSize;
|
||||
static const int kContextFromRegisterPointer = 3 * kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
@ -583,10 +584,6 @@ class JavaScriptFrame: public StandardFrame {
|
||||
// about the inlined frames use {GetFunctions} and {Summarize}.
|
||||
bool HasInlinedFrames() const;
|
||||
|
||||
// Returns the new target function that was used in the constructor call to
|
||||
// this frame. Note that this is only valid on constructor frames.
|
||||
Object* GetNewTarget() const;
|
||||
|
||||
// Check if this frame has "adapted" arguments in the sense that the
|
||||
// actual passed arguments are available in an arguments adaptor
|
||||
// frame below it on the stack.
|
||||
|
@ -532,6 +532,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
|
||||
//
|
||||
// The live registers are:
|
||||
// o edi: the JS function object being called
|
||||
// o edx: the new target
|
||||
// o esi: our context
|
||||
// o ebp: the caller's frame pointer
|
||||
// o esp: stack pointer (pointing to return address)
|
||||
@ -549,6 +550,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ mov(ebp, esp);
|
||||
__ push(esi); // Callee's context.
|
||||
__ push(edi); // Callee's JS function.
|
||||
__ push(edx); // Callee's new target.
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into edi (InterpreterBytecodeRegister).
|
||||
@ -619,9 +621,9 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
// registers.
|
||||
__ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
|
||||
__ mov(kInterpreterRegisterFileRegister, ebp);
|
||||
__ sub(
|
||||
kInterpreterRegisterFileRegister,
|
||||
Immediate(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ sub(kInterpreterRegisterFileRegister,
|
||||
Immediate(2 * kPointerSize +
|
||||
StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ mov(kInterpreterBytecodeOffsetRegister,
|
||||
Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag));
|
||||
// Since the dispatch table root might be set after builtins are generated,
|
||||
|
@ -967,7 +967,8 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
|
||||
// Fall-through to kReg8 case.
|
||||
case OperandType::kReg8: {
|
||||
Register reg = Register::FromOperand(static_cast<uint8_t>(operand_value));
|
||||
if (reg.is_function_context() || reg.is_function_closure()) {
|
||||
if (reg.is_function_context() || reg.is_function_closure() ||
|
||||
reg.is_new_target()) {
|
||||
return true;
|
||||
} else if (reg.is_parameter()) {
|
||||
int parameter_index = reg.ToParameterIndex(parameter_count_);
|
||||
|
@ -2157,7 +2157,7 @@ void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) {
|
||||
if (variable == nullptr) return;
|
||||
|
||||
// Store the new target we were called with in the given variable.
|
||||
builder()->CallRuntime(Runtime::kGetNewTarget, Register(), 0);
|
||||
builder()->LoadAccumulatorWithRegister(Register::new_target());
|
||||
VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
|
||||
}
|
||||
|
||||
|
@ -228,6 +228,8 @@ std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start,
|
||||
os << "<context>";
|
||||
} else if (reg.is_function_closure()) {
|
||||
os << "<closure>";
|
||||
} else if (reg.is_new_target()) {
|
||||
os << "<new.target>";
|
||||
} else if (reg.is_parameter()) {
|
||||
int parameter_index = reg.ToParameterIndex(parameter_count);
|
||||
if (parameter_index == 0) {
|
||||
@ -273,6 +275,8 @@ static const int kFunctionClosureRegisterIndex =
|
||||
-InterpreterFrameConstants::kFunctionFromRegisterPointer / kPointerSize;
|
||||
static const int kFunctionContextRegisterIndex =
|
||||
-InterpreterFrameConstants::kContextFromRegisterPointer / kPointerSize;
|
||||
static const int kNewTargetRegisterIndex =
|
||||
-InterpreterFrameConstants::kNewTargetFromRegisterPointer / kPointerSize;
|
||||
|
||||
|
||||
// Registers occupy range 0-127 in 8-bit value leaving 128 unused values.
|
||||
@ -318,6 +322,14 @@ bool Register::is_function_context() const {
|
||||
}
|
||||
|
||||
|
||||
Register Register::new_target() { return Register(kNewTargetRegisterIndex); }
|
||||
|
||||
|
||||
bool Register::is_new_target() const {
|
||||
return index() == kNewTargetRegisterIndex;
|
||||
}
|
||||
|
||||
|
||||
int Register::MaxParameterIndex() { return kMaxParameterIndex; }
|
||||
|
||||
|
||||
|
@ -260,6 +260,10 @@ class Register {
|
||||
static Register function_context();
|
||||
bool is_function_context() const;
|
||||
|
||||
// Returns the register for the incoming new target value.
|
||||
static Register new_target();
|
||||
bool is_new_target() const;
|
||||
|
||||
static Register FromOperand(uint8_t operand);
|
||||
uint8_t ToOperand() const;
|
||||
|
||||
|
@ -786,6 +786,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
|
||||
//
|
||||
// The live registers are:
|
||||
// o a1: the JS function object being called.
|
||||
// o a3: the new target
|
||||
// o cp: our context
|
||||
// o fp: the caller's frame pointer
|
||||
// o sp: stack pointer
|
||||
@ -803,6 +804,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
|
||||
__ Push(ra, fp, cp, a1);
|
||||
__ Addu(fp, sp, Operand(StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ Push(a3);
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into kInterpreterBytecodeRegister.
|
||||
@ -868,9 +870,9 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
|
||||
// Load bytecode offset and dispatch table into registers.
|
||||
__ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
|
||||
__ Subu(
|
||||
kInterpreterRegisterFileRegister, fp,
|
||||
Operand(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ Subu(kInterpreterRegisterFileRegister, fp,
|
||||
Operand(2 * kPointerSize +
|
||||
StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ li(kInterpreterBytecodeOffsetRegister,
|
||||
Operand(BytecodeArray::kHeaderSize - kHeapObjectTag));
|
||||
__ LoadRoot(kInterpreterDispatchTableRegister,
|
||||
|
@ -779,6 +779,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
|
||||
//
|
||||
// The live registers are:
|
||||
// o a1: the JS function object being called.
|
||||
// o a3: the new target
|
||||
// o cp: our context
|
||||
// o fp: the caller's frame pointer
|
||||
// o sp: stack pointer
|
||||
@ -796,6 +797,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
|
||||
__ Push(ra, fp, cp, a1);
|
||||
__ Daddu(fp, sp, Operand(StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ Push(a3);
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into kInterpreterBytecodeRegister.
|
||||
@ -861,9 +863,9 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
|
||||
// Load bytecode offset and dispatch table into registers.
|
||||
__ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
|
||||
__ Dsubu(
|
||||
kInterpreterRegisterFileRegister, fp,
|
||||
Operand(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ Dsubu(kInterpreterRegisterFileRegister, fp,
|
||||
Operand(2 * kPointerSize +
|
||||
StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ li(kInterpreterBytecodeOffsetRegister,
|
||||
Operand(BytecodeArray::kHeaderSize - kHeapObjectTag));
|
||||
__ LoadRoot(kInterpreterDispatchTableRegister,
|
||||
|
@ -589,19 +589,6 @@ RUNTIME_FUNCTION(Runtime_Apply) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetNewTarget) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 0);
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
// TODO(4544): By now the runtime function is only used by the interpreter,
|
||||
// get rid of the entire runtime function once the interpreter is switched.
|
||||
DCHECK(!frame->is_optimized() && !frame->HasInlinedFrames());
|
||||
return frame->IsConstructor() ? frame->GetNewTarget()
|
||||
: isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
// ES6 section 9.2.1.2, OrdinaryCallBindThis for sloppy callee.
|
||||
RUNTIME_FUNCTION(Runtime_ConvertReceiver) {
|
||||
HandleScope scope(isolate);
|
||||
|
@ -261,7 +261,6 @@ namespace internal {
|
||||
F(Call, -1 /* >= 2 */, 1) \
|
||||
F(TailCall, -1 /* >= 2 */, 1) \
|
||||
F(Apply, 5, 1) \
|
||||
F(GetNewTarget, 0, 1) \
|
||||
F(ConvertReceiver, 1, 1) \
|
||||
F(IsConstructCall, 0, 1) \
|
||||
F(IsFunction, 1, 1)
|
||||
|
@ -595,6 +595,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
|
||||
//
|
||||
// The live registers are:
|
||||
// o rdi: the JS function object being called
|
||||
// o rdx: the new target
|
||||
// o rsi: our context
|
||||
// o rbp: the caller's frame pointer
|
||||
// o rsp: stack pointer (pointing to return address)
|
||||
@ -612,6 +613,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ movp(rbp, rsp);
|
||||
__ Push(rsi); // Callee's context.
|
||||
__ Push(rdi); // Callee's JS function.
|
||||
__ Push(rdx); // Callee's new target.
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into edi (InterpreterBytecodeRegister).
|
||||
@ -678,9 +680,9 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
// registers.
|
||||
__ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
|
||||
__ movp(kInterpreterRegisterFileRegister, rbp);
|
||||
__ subp(
|
||||
kInterpreterRegisterFileRegister,
|
||||
Immediate(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ subp(kInterpreterRegisterFileRegister,
|
||||
Immediate(2 * kPointerSize +
|
||||
StandardFrameConstants::kFixedFrameSizeFromFp));
|
||||
__ movp(kInterpreterBytecodeOffsetRegister,
|
||||
Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag));
|
||||
__ LoadRoot(kInterpreterDispatchTableRegister,
|
||||
|
@ -5190,16 +5190,28 @@ TEST(NewTarget) {
|
||||
InitializedHandleScope handle_scope;
|
||||
BytecodeGeneratorHelper helper;
|
||||
|
||||
int new_target = Register::new_target().index();
|
||||
|
||||
ExpectedSnippet<int> snippets[] = {
|
||||
{"return new.target;",
|
||||
1 * kPointerSize,
|
||||
1,
|
||||
8,
|
||||
5,
|
||||
{
|
||||
B(CallRuntime), U16(Runtime::kGetNewTarget), R(0), U8(0), //
|
||||
B(Star), R(0), //
|
||||
B(Return), //
|
||||
}}
|
||||
B(Ldar), R(new_target), //
|
||||
B(Star), R(0), //
|
||||
B(Return), //
|
||||
}},
|
||||
{"new.target;",
|
||||
1 * kPointerSize,
|
||||
1,
|
||||
6,
|
||||
{
|
||||
B(Ldar), R(new_target), //
|
||||
B(Star), R(0), //
|
||||
B(LdaUndefined), //
|
||||
B(Return), //
|
||||
}},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(snippets); i++) {
|
||||
|
Loading…
Reference in New Issue
Block a user