Bug 1112160 - Baseline Stubs: Align Jit frames before calling any jitted code. r=jandem

This commit is contained in:
Nicolas B. Pierron 2015-02-12 14:53:06 +01:00
parent 82a03cf1ed
commit d0f0f47d58
6 changed files with 196 additions and 29 deletions

View File

@ -53,7 +53,7 @@ for (var l = 0; l < 4; l++) {
ionFrameSize_args[l][a] = gen_ionFrameSize(30 + l, a, "ionFrameSize_callee_verify");;
}
// Check ion frames during function calls are always correctly aligned.
// Check ion frames during function apply calls with the argument vector.
function ionFrame_funApply_0() {
assertJitStackInvariants.apply(this, arguments);
}
@ -61,6 +61,53 @@ function ionFrame_funApply_1() {
ionFrame_funApply_0.apply(this, arguments);
}
// Check ion frames during function apply calls with an array of arguments.
function ionFrame_funApply_2() {
var arr = Array.apply(Array, arguments);
assertJitStackInvariants.apply(this, arr);
}
function ionFrame_funApply_3() {
var arr = Array.apply(Array, arguments);
ionFrame_funApply_2.apply(this, arr);
}
// Check ion frames during function .call calls.
function ionFrame_funCall_0() {
assertJitStackInvariants.call(this);
}
function ionFrame_funCall_1(a) {
assertJitStackInvariants.call(this, a);
}
function ionFrame_funCall_2(a, b) {
assertJitStackInvariants.call(this, a, b);
}
function ionFrame_funCall_3(a, b, c) {
assertJitStackInvariants.call(this, a, b, c);
}
function ionFrame_funCall_x0() {
ionFrame_funCall_0.call(this);
}
function ionFrame_funCall_x1(a) {
ionFrame_funCall_1.call(this, a);
}
function ionFrame_funCall_x2(a, b) {
ionFrame_funCall_2.call(this, a, b);
}
function ionFrame_funCall_x3(a, b, c) {
ionFrame_funCall_3.call(this, a, b, c);
}
// Check ion frames during spread calls.
function ionFrame_spreadCall_0() {
var arr = Array.apply(Array, arguments);
assertJitStackInvariants(...arr);
}
function ionFrame_spreadCall_1() {
var arr = Array.apply(Array, arguments);
ionFrame_spreadCall_0(...arr);
}
for (i = 0; i < 40; i++) {
entryFrame_1();
@ -89,4 +136,31 @@ for (i = 0; i < 40; i++) {
ionFrame_funApply_1(1);
ionFrame_funApply_1(1, 2);
ionFrame_funApply_1(1, 2, 3);
ionFrame_funApply_2();
ionFrame_funApply_2(1);
ionFrame_funApply_2(1, 2);
ionFrame_funApply_2(1, 2, 3);
ionFrame_funApply_3();
ionFrame_funApply_3(1);
ionFrame_funApply_3(1, 2);
ionFrame_funApply_3(1, 2, 3);
ionFrame_funCall_0();
ionFrame_funCall_1(1);
ionFrame_funCall_2(1, 2);
ionFrame_funCall_3(1, 2, 3);
ionFrame_funCall_x0();
ionFrame_funCall_x1(1);
ionFrame_funCall_x2(1, 2);
ionFrame_funCall_x3(1, 2, 3);
ionFrame_spreadCall_0();
ionFrame_spreadCall_0(1);
ionFrame_spreadCall_0(1, 2);
ionFrame_spreadCall_0(1, 2, 3);
ionFrame_spreadCall_1();
ionFrame_spreadCall_1(1);
ionFrame_spreadCall_1(1, 2);
ionFrame_spreadCall_1(1, 2, 3);
}

View File

@ -9622,7 +9622,8 @@ DoSpreadCallFallback(JSContext *cx, BaselineFrame *frame, ICCall_Fallback *stub_
}
void
ICCallStubCompiler::pushCallArguments(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg)
ICCallStubCompiler::pushCallArguments(MacroAssembler &masm, GeneralRegisterSet regs,
Register argcReg, bool isJitCall)
{
MOZ_ASSERT(!regs.has(argcReg));
@ -9639,6 +9640,11 @@ ICCallStubCompiler::pushCallArguments(MacroAssembler &masm, GeneralRegisterSet r
// return address, old frame pointer and stub reg.
masm.addPtr(Imm32(STUB_FRAME_SIZE), argPtr);
// Align the stack such that the JitFrameLayout is aligned on the
// JitStackAlignment.
if (isJitCall)
masm.alignJitStackBasedOnNArgs(argcReg);
// Push all values, starting at the last one.
Label loop, done;
masm.bind(&loop);
@ -9670,7 +9676,7 @@ ICCallStubCompiler::guardSpreadCall(MacroAssembler &masm, Register argcReg, Labe
void
ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler &masm, GeneralRegisterSet regs,
Register argcReg)
Register argcReg, bool isJitCall)
{
// Push arguments
Register startReg = regs.takeAny();
@ -9682,6 +9688,11 @@ ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler &masm, GeneralRegiste
masm.lshiftPtr(Imm32(3), endReg);
masm.addPtr(startReg, endReg);
// Align the stack such that the JitFrameLayout is aligned on the
// JitStackAlignment.
if (isJitCall)
masm.alignJitStackBasedOnNArgs(argcReg);
// Copying pre-decrements endReg by 8 until startReg is reached
Label copyDone;
Label copyStart;
@ -9824,6 +9835,7 @@ ICCallStubCompiler::pushCallerArguments(MacroAssembler &masm, GeneralRegisterSet
masm.loadPtr(Address(BaselineFrameReg, 0), startReg);
masm.loadPtr(Address(startReg, BaselineFrame::offsetOfNumActualArgs()), endReg);
masm.addPtr(Imm32(BaselineFrame::offsetOfArg(0)), startReg);
masm.alignJitStackBasedOnNArgs(endReg);
masm.lshiftPtr(Imm32(ValueShift), endReg);
masm.addPtr(startReg, endReg);
@ -9850,6 +9862,7 @@ ICCallStubCompiler::pushArrayArguments(MacroAssembler &masm, Address arrayVal,
masm.extractObject(arrayVal, startReg);
masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
masm.load32(Address(startReg, ObjectElements::offsetOfInitializedLength()), endReg);
masm.alignJitStackBasedOnNArgs(endReg);
masm.lshiftPtr(Imm32(ValueShift), endReg);
masm.addPtr(startReg, endReg);
@ -9914,7 +9927,7 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
regs.take(R0.scratchReg()); // argc.
pushCallArguments(masm, regs, R0.scratchReg());
pushCallArguments(masm, regs, R0.scratchReg(), /* isJitCall = */ false);
masm.push(BaselineStackReg);
masm.push(R0.scratchReg());
@ -10144,9 +10157,9 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler &masm)
// right-to-left so duplicate them on the stack in reverse order.
// |this| and callee are pushed last.
if (isSpread_)
pushSpreadCallArguments(masm, regs, argcReg);
pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ true);
else
pushCallArguments(masm, regs, argcReg);
pushCallArguments(masm, regs, argcReg, /* isJitCall = */ true);
// The callee is on top of the stack. Pop and unbox it.
ValueOperand val = regs.takeAnyValue();
@ -10187,27 +10200,40 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler &masm)
Label skipThisReplace;
masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
Register scratchReg = JSReturnOperand.scratchReg();
// Current stack: [ ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
// Current stack: [ Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
// However, we can't use this ThisVal, because it hasn't been traced. We need to use
// The ThisVal higher up the stack:
// Current stack: [ ThisVal, ARGVALS..., ...STUB FRAME...,
// ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
masm.loadPtr(Address(BaselineStackReg, 2*sizeof(size_t)), scratchReg);
// Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
// scratchReg now contains actualArgCount. Double it to account for skipping past two
// pushed copies of argument values for non-spread, increment it to account for skipping
// actual argument values and array object for spread. Additionally, we need to add:
// STUB_FRAME_SIZE + sizeof(ThisVal) + sizeof(size_t) + sizeof(void *) + sizoef(size_t)
// for: stub frame, this value, actual argc, callee, and descriptor
if (isSpread_)
masm.add32(Imm32(1), scratchReg);
else
masm.lshiftPtr(Imm32(1), scratchReg);
BaseValueIndex reloadThisSlot(BaselineStackReg, scratchReg,
STUB_FRAME_SIZE + sizeof(Value) + 3 * sizeof(size_t));
masm.loadValue(reloadThisSlot, JSReturnOperand);
// Restore the BaselineFrameReg based on the frame descriptor.
//
// BaselineFrameReg = BaselineStackReg
// + sizeof(Descriptor) + sizeof(Callee) + sizeof(ActualArgc)
// + stubFrameSize(Descriptor)
// - sizeof(BaselineStubReg) - sizeof(BaselineFrameReg)
Address descriptorAddr(BaselineStackReg, 0);
masm.loadPtr(descriptorAddr, BaselineFrameReg);
masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), BaselineFrameReg);
masm.addPtr(Imm32((3 - 2) * sizeof(size_t)), BaselineFrameReg);
masm.addPtr(BaselineStackReg, BaselineFrameReg);
// Load the number of arguments present before the stub frame.
Register argcReg = JSReturnOperand.scratchReg();
if (isSpread_) {
// Account for the Array object.
masm.move32(Imm32(1), argcReg);
} else {
Address argcAddr(BaselineStackReg, 2 * sizeof(size_t));
masm.loadPtr(argcAddr, argcReg);
}
// Current stack: [ ThisVal, ARGVALS..., ...STUB FRAME..., <-- BaselineFrameReg
// Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
//
// &ThisVal = BaselineFrameReg + argc * sizeof(Value) + STUB_FRAME_SIZE
BaseValueIndex thisSlotAddr(BaselineFrameReg, argcReg, STUB_FRAME_SIZE);
masm.loadValue(thisSlotAddr, JSReturnOperand);
#ifdef DEBUG
masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
masm.assumeUnreachable("Return of constructing call should be an object.");
@ -10407,9 +10433,9 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler &masm)
// right-to-left so duplicate them on the stack in reverse order.
// |this| and callee are pushed last.
if (isSpread_)
pushSpreadCallArguments(masm, regs, argcReg);
pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ false);
else
pushCallArguments(masm, regs, argcReg);
pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false);
if (isConstructing_) {
// Stack looks like: [ ..., Arg0Val, ThisVal, CalleeVal ]
@ -10504,7 +10530,7 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler &masm)
enterStubFrame(masm, regs.getAny());
regs.add(scratch);
pushCallArguments(masm, regs, argcReg);
pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false);
regs.take(scratch);
if (isConstructing_) {
@ -10811,7 +10837,7 @@ ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler &masm)
// Values are on the stack left-to-right. Calling convention wants them
// right-to-left so duplicate them on the stack in reverse order.
pushCallArguments(masm, regs, argcReg);
pushCallArguments(masm, regs, argcReg, /* isJitCall = */ true);
// Discard callee (function.call).
masm.addPtr(Imm32(sizeof(Value)), StackPointer);

View File

@ -5908,8 +5908,10 @@ class ICCallStubCompiler : public ICStubCompiler
FunApply_Array
};
void pushCallArguments(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg);
void pushSpreadCallArguments(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg);
void pushCallArguments(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg,
bool isJitCall);
void pushSpreadCallArguments(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg,
bool isJitCall);
void guardSpreadCall(MacroAssembler &masm, Register argcReg, Label *failure);
Register guardFunApply(MacroAssembler &masm, GeneralRegisterSet regs, Register argcReg,
bool checkNative, FunApplyThing applyThing, Label *failure);

View File

@ -3089,6 +3089,7 @@ AssertJitStackInvariants(JSContext *cx)
JitFrameIterator frames(activations);
size_t prevFrameSize = 0;
size_t frameSize = 0;
bool isScriptedCallee = false;
for (; !frames.done(); ++frames) {
size_t calleeFp = reinterpret_cast<size_t>(frames.fp());
size_t callerFp = reinterpret_cast<size_t>(frames.prevFp());
@ -3134,6 +3135,17 @@ AssertJitStackInvariants(JSContext *cx)
"The ion frame should keep the alignment");
}
}
// The stack is dynamically aligned by baseline stubs before calling
// any jitted code.
if (frames.prevType() == JitFrame_BaselineStub && isScriptedCallee) {
MOZ_RELEASE_ASSERT(calleeFp % JitStackAlignment == 0,
"The baseline stub restores the stack alignment");
}
isScriptedCallee = false
|| frames.isScripted()
|| frames.type() == JitFrame_Rectifier;
}
MOZ_RELEASE_ASSERT(frames.type() == JitFrame_Entry,

View File

@ -2296,3 +2296,51 @@ MacroAssembler::profilerPreCallImpl(Register reg, Register reg2)
appendProfilerCallSite(label);
}
void
MacroAssembler::alignJitStackBasedOnNArgs(Register nargs)
{
const uint32_t alignment = JitStackAlignment / sizeof(Value);
if (alignment == 1)
return;
// A JitFrameLayout is composed of the following:
// [padding?] [argN] .. [arg1] [this] [[argc] [callee] [descr] [raddr]]
//
// We want to ensure that the |raddr| address is aligned.
// Which implies that we want to ensure that |this| is aligned.
static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0,
"No need to consider the JitFrameLayout for aligning the stack");
// Which implies that |argN| is aligned if |nargs| is even, and offset by
// |sizeof(Value)| if |nargs| is odd.
MOZ_ASSERT(alignment == 2);
// Thus the |padding| is offset by |sizeof(Value)| if |nargs| is even, and
// aligned if |nargs| is odd.
// if (nargs % 2 == 0) {
// if (sp % JitStackAlignment == 0)
// sp -= sizeof(Value);
// MOZ_ASSERT(sp % JitStackAlignment == JitStackAlignment - sizeof(Value));
// } else {
// sp = sp & ~(JitStackAlignment - 1);
// }
Label odd, end;
Label *maybeAssert = &end;
#ifdef DEBUG
Label assert;
maybeAssert = &assert;
#endif
branchTestPtr(Assembler::NonZero, nargs, Imm32(1), &odd);
branchTestPtr(Assembler::NonZero, StackPointer, Imm32(JitStackAlignment - 1), maybeAssert);
subPtr(Imm32(sizeof(Value)), StackPointer);
#ifdef DEBUG
bind(&assert);
#endif
assertStackAlignment(JitStackAlignment, sizeof(Value));
jump(&end);
bind(&odd);
andPtr(Imm32(~(JitStackAlignment - 1)), StackPointer);
bind(&end);
}

View File

@ -1252,6 +1252,11 @@ class MacroAssembler : public MacroAssemblerSpecific
PopRegsInMask(liveRegs);
}
// Align the stack pointer based on the number of arguments which are pushed
// on the stack, such that the JitFrameLayout would be correctly aligned on
// the JitStackAlignment.
void alignJitStackBasedOnNArgs(Register nargs);
void assertStackAlignment(uint32_t alignment, int32_t offset = 0) {
#ifdef DEBUG
Label ok, bad;