[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:
mstarzinger 2015-11-25 03:36:38 -08:00 committed by Commit bot
parent 5335e7d55a
commit 3d004eeab2
15 changed files with 68 additions and 56 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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()) {

View File

@ -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.

View File

@ -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,

View File

@ -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_);

View File

@ -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());
}

View File

@ -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; }

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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)

View File

@ -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,

View File

@ -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++) {