mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1112160 - Baseline Stubs: Align Jit frames before calling any jitted code. r=jandem
This commit is contained in:
parent
82a03cf1ed
commit
d0f0f47d58
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user