зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1778466 part 7 - Don't use a tagged frame pointer for direct JIT => Wasm calls. r=rhunt
The tag bit is now only used for the activation's `exitFP` field. Similar to part 2, this makes it easier for stack unwinders to unwind through Wasm frames. Differential Revision: https://phabricator.services.mozilla.com/D151252
This commit is contained in:
Родитель
e431a956e1
Коммит
2ff99f68dc
|
@ -103,7 +103,7 @@ static constexpr uint32_t FAKE_EXITFP_FOR_BAILOUT_ADDR = 0xba2;
|
|||
static uint8_t* const FAKE_EXITFP_FOR_BAILOUT =
|
||||
reinterpret_cast<uint8_t*>(FAKE_EXITFP_FOR_BAILOUT_ADDR);
|
||||
|
||||
static_assert(!(FAKE_EXITFP_FOR_BAILOUT_ADDR & wasm::ExitOrJitEntryFPTag),
|
||||
static_assert(!(FAKE_EXITFP_FOR_BAILOUT_ADDR & wasm::ExitFPTag),
|
||||
"FAKE_EXITFP_FOR_BAILOUT could be mistaken as a low-bit tagged "
|
||||
"wasm exit fp");
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ class JitActivation : public Activation {
|
|||
bool hasExitFP() const { return !!packedExitFP_; }
|
||||
uint8_t* jsOrWasmExitFP() const {
|
||||
if (hasWasmExitFP()) {
|
||||
return wasm::Frame::toJitEntryCaller(packedExitFP_);
|
||||
return wasm::Frame::untagExitFP(packedExitFP_);
|
||||
}
|
||||
return packedExitFP_;
|
||||
}
|
||||
|
@ -207,21 +207,19 @@ class JitActivation : public Activation {
|
|||
void setLastProfilingCallSite(void* ptr) { lastProfilingCallSite_ = ptr; }
|
||||
|
||||
// WebAssembly specific attributes.
|
||||
bool hasWasmExitFP() const {
|
||||
return wasm::Frame::isExitOrJitEntryFP(packedExitFP_);
|
||||
}
|
||||
bool hasWasmExitFP() const { return wasm::Frame::isExitFP(packedExitFP_); }
|
||||
wasm::Frame* wasmExitFP() const {
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
return reinterpret_cast<wasm::Frame*>(
|
||||
wasm::Frame::toJitEntryCaller(packedExitFP_));
|
||||
wasm::Frame::untagExitFP(packedExitFP_));
|
||||
}
|
||||
wasm::Instance* wasmExitInstance() const {
|
||||
return wasm::GetNearestEffectiveInstance(wasmExitFP());
|
||||
}
|
||||
void setWasmExitFP(const wasm::Frame* fp) {
|
||||
if (fp) {
|
||||
MOZ_ASSERT(!wasm::Frame::isExitOrJitEntryFP(fp));
|
||||
packedExitFP_ = wasm::Frame::addExitOrJitEntryFPTag(fp);
|
||||
MOZ_ASSERT(!wasm::Frame::isExitFP(fp));
|
||||
packedExitFP_ = wasm::Frame::addExitFPTag(fp);
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
} else {
|
||||
packedExitFP_ = nullptr;
|
||||
|
|
|
@ -272,18 +272,8 @@ namespace wasm {
|
|||
|
||||
class Instance;
|
||||
|
||||
// Bit set as the lowest bit of a frame pointer, used in two different mutually
|
||||
// exclusive situations:
|
||||
// - either it's a low bit tag in a FramePointer value read from the
|
||||
// Frame::callerFP of an inner wasm frame. This indicates the previous call
|
||||
// frame has been set up by a JIT caller that directly called into a wasm
|
||||
// function's body. This is only stored in Frame::callerFP for a wasm frame
|
||||
// called from JIT code, and thus it can not appear in a JitActivation's
|
||||
// exitFP.
|
||||
// - or it's the low big tag set when exiting wasm code in JitActivation's
|
||||
// exitFP.
|
||||
|
||||
constexpr uintptr_t ExitOrJitEntryFPTag = 0x1;
|
||||
// Bit tag set when exiting wasm code in JitActivation's exitFP.
|
||||
constexpr uintptr_t ExitFPTag = 0x1;
|
||||
|
||||
// wasm::Frame represents the bytes pushed by the call instruction and the
|
||||
// fixed prologue generated by wasm::GenerateCallablePrologue.
|
||||
|
@ -326,36 +316,29 @@ class Frame {
|
|||
|
||||
uint8_t* rawCaller() const { return callerFP_; }
|
||||
|
||||
Frame* wasmCaller() const {
|
||||
MOZ_ASSERT(!callerIsExitOrJitEntryFP());
|
||||
return reinterpret_cast<Frame*>(callerFP_);
|
||||
}
|
||||
Frame* wasmCaller() const { return reinterpret_cast<Frame*>(callerFP_); }
|
||||
|
||||
bool callerIsExitOrJitEntryFP() const {
|
||||
return isExitOrJitEntryFP(callerFP_);
|
||||
}
|
||||
|
||||
uint8_t* jitEntryCaller() const { return toJitEntryCaller(callerFP_); }
|
||||
uint8_t* jitEntryCaller() const { return callerFP_; }
|
||||
|
||||
static const Frame* fromUntaggedWasmExitFP(const void* savedFP) {
|
||||
MOZ_ASSERT(!isExitOrJitEntryFP(savedFP));
|
||||
MOZ_ASSERT(!isExitFP(savedFP));
|
||||
return reinterpret_cast<const Frame*>(savedFP);
|
||||
}
|
||||
|
||||
static bool isExitOrJitEntryFP(const void* fp) {
|
||||
return reinterpret_cast<uintptr_t>(fp) & ExitOrJitEntryFPTag;
|
||||
static bool isExitFP(const void* fp) {
|
||||
return reinterpret_cast<uintptr_t>(fp) & ExitFPTag;
|
||||
}
|
||||
|
||||
static uint8_t* toJitEntryCaller(const void* fp) {
|
||||
MOZ_ASSERT(isExitOrJitEntryFP(fp));
|
||||
static uint8_t* untagExitFP(const void* fp) {
|
||||
MOZ_ASSERT(isExitFP(fp));
|
||||
return reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(fp) &
|
||||
~ExitOrJitEntryFPTag);
|
||||
~ExitFPTag);
|
||||
}
|
||||
|
||||
static uint8_t* addExitOrJitEntryFPTag(const Frame* fp) {
|
||||
MOZ_ASSERT(!isExitOrJitEntryFP(fp));
|
||||
static uint8_t* addExitFPTag(const Frame* fp) {
|
||||
MOZ_ASSERT(!isExitFP(fp));
|
||||
return reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(fp) |
|
||||
ExitOrJitEntryFPTag);
|
||||
ExitFPTag);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -134,9 +134,6 @@ static inline void AssertDirectJitCall(const void* fp) {
|
|||
// pointing in the middle of the exit frame, right before the exit
|
||||
// footer; ensure the exit frame type is the expected one.
|
||||
#ifdef DEBUG
|
||||
if (Frame::isExitOrJitEntryFP(fp)) {
|
||||
fp = Frame::toJitEntryCaller(fp);
|
||||
}
|
||||
auto* jitCaller = (ExitFrameLayout*)fp;
|
||||
MOZ_ASSERT(jitCaller->footer()->type() ==
|
||||
jit::ExitFrameType::DirectWasmJitCall);
|
||||
|
@ -148,21 +145,19 @@ void WasmFrameIter::popFrame() {
|
|||
code_ = LookupCode(returnAddress, &codeRange_);
|
||||
|
||||
if (!code_) {
|
||||
// We run into a frame pointer which has the low bit set,
|
||||
// indicating this is a direct call from the jit into the wasm
|
||||
// function's body. The call stack resembles this at this point:
|
||||
// This is a direct call from the jit into the wasm function's body. The
|
||||
// call stack resembles this at this point:
|
||||
//
|
||||
// |---------------------|
|
||||
// | JIT FRAME |
|
||||
// | JIT FAKE EXIT FRAME | <-- tagged fp_->callerFP_
|
||||
// | JIT FAKE EXIT FRAME | <-- fp_->callerFP_
|
||||
// | WASM FRAME | <-- fp_
|
||||
// |---------------------|
|
||||
//
|
||||
// fp_->callerFP_ points to the fake exit frame set up by the jit caller,
|
||||
// and the return-address-to-fp is in JIT code, thus doesn't belong to any
|
||||
// wasm instance's code (in particular, there's no associated CodeRange).
|
||||
// Mark the frame as such and untag FP.
|
||||
MOZ_ASSERT(fp_->callerIsExitOrJitEntryFP());
|
||||
// Mark the frame as such.
|
||||
AssertDirectJitCall(fp_->jitEntryCaller());
|
||||
|
||||
unwoundJitCallerFP_ = fp_->jitEntryCaller();
|
||||
|
@ -427,10 +422,10 @@ void wasm::SetExitFP(MacroAssembler& masm, ExitReason reason,
|
|||
Imm32(reason.encode()),
|
||||
Address(scratch, JitActivation::offsetOfEncodedWasmExitReason()));
|
||||
|
||||
masm.orPtr(Imm32(ExitOrJitEntryFPTag), FramePointer);
|
||||
masm.orPtr(Imm32(ExitFPTag), FramePointer);
|
||||
masm.storePtr(FramePointer,
|
||||
Address(scratch, JitActivation::offsetOfPackedExitFP()));
|
||||
masm.andPtr(Imm32(int32_t(~ExitOrJitEntryFPTag)), FramePointer);
|
||||
masm.andPtr(Imm32(int32_t(~ExitFPTag)), FramePointer);
|
||||
}
|
||||
|
||||
void wasm::ClearExitFP(MacroAssembler& masm, Register scratch) {
|
||||
|
@ -793,7 +788,7 @@ static void AssertNoWasmExitFPInJitExit(MacroAssembler& masm) {
|
|||
Label ok;
|
||||
masm.branchTestPtr(Assembler::Zero,
|
||||
Address(scratch, JitActivation::offsetOfPackedExitFP()),
|
||||
Imm32(ExitOrJitEntryFPTag), &ok);
|
||||
Imm32(ExitFPTag), &ok);
|
||||
masm.breakpoint();
|
||||
masm.bind(&ok);
|
||||
#endif
|
||||
|
@ -978,8 +973,8 @@ void ProfilingFrameIterator::initFromExitFP(const Frame* fp) {
|
|||
code_ = LookupCode(fp->returnAddress(), &codeRange_);
|
||||
|
||||
if (!code_) {
|
||||
// This is a direct call from the JIT, the caller FP is pointing to a
|
||||
// tagged JIT caller's frame.
|
||||
// This is a direct call from the JIT, the caller FP is pointing to the JIT
|
||||
// caller's frame.
|
||||
AssertDirectJitCall(fp->jitEntryCaller());
|
||||
|
||||
unwoundJitCallerFP_ = fp->jitEntryCaller();
|
||||
|
@ -1047,7 +1042,6 @@ const Instance* js::wasm::GetNearestEffectiveInstance(const Frame* fp) {
|
|||
|
||||
if (!code) {
|
||||
// It is a direct call from JIT.
|
||||
MOZ_ASSERT(fp->callerIsExitOrJitEntryFP());
|
||||
AssertDirectJitCall(fp->jitEntryCaller());
|
||||
return ExtractCalleeInstanceFromFrameWithInstances(fp);
|
||||
}
|
||||
|
@ -1081,12 +1075,11 @@ bool js::wasm::StartUnwinding(const RegisterState& registers,
|
|||
void** const sp = (void**)registers.sp;
|
||||
|
||||
// The frame pointer might be:
|
||||
// - in the process of tagging/untagging when calling into the JITs;
|
||||
// make sure it's untagged.
|
||||
// - tagged by an direct JIT call.
|
||||
// - in the process of tagging/untagging when calling into C++ code (this
|
||||
// happens in wasm::SetExitFP); make sure it's untagged.
|
||||
// - unreliable if it's not been set yet, in prologues.
|
||||
uint8_t* fp = Frame::isExitOrJitEntryFP(registers.fp)
|
||||
? Frame::toJitEntryCaller(registers.fp)
|
||||
uint8_t* fp = Frame::isExitFP(registers.fp)
|
||||
? Frame::untagExitFP(registers.fp)
|
||||
: reinterpret_cast<uint8_t*>(registers.fp);
|
||||
|
||||
// Get the CodeRange describing pc and the base address to which the
|
||||
|
@ -1214,10 +1207,7 @@ bool js::wasm::StartUnwinding(const RegisterState& registers,
|
|||
} else if (offsetFromEntry == PushedFP) {
|
||||
// The full Frame has been pushed; fp is still the caller's fp.
|
||||
const auto* frame = Frame::fromUntaggedWasmExitFP(sp);
|
||||
DebugOnly<const uint8_t*> caller = frame->callerIsExitOrJitEntryFP()
|
||||
? frame->jitEntryCaller()
|
||||
: frame->rawCaller();
|
||||
MOZ_ASSERT(caller == fp);
|
||||
MOZ_ASSERT(frame->rawCaller() == fp);
|
||||
fixedPC = frame->returnAddress();
|
||||
fixedFP = fp;
|
||||
AssertMatchesCallSite(fixedPC, fixedFP);
|
||||
|
@ -1317,7 +1307,7 @@ bool js::wasm::StartUnwinding(const RegisterState& registers,
|
|||
}
|
||||
// On the error return path, FP might be set to FailFP. Ignore these
|
||||
// transient frames.
|
||||
if (intptr_t(fp) == (FailFP & ~ExitOrJitEntryFPTag)) {
|
||||
if (intptr_t(fp) == (FailFP & ~ExitFPTag)) {
|
||||
return false;
|
||||
}
|
||||
// Set fixedFP to the address of the JitFrameLayout on the stack.
|
||||
|
@ -1415,13 +1405,11 @@ void ProfilingFrameIterator::operator++() {
|
|||
code_ = LookupCode(callerPC_, &codeRange_);
|
||||
|
||||
if (!code_) {
|
||||
// The parent frame is an inlined wasm call, the tagged FP points to
|
||||
// the fake exit frame.
|
||||
// The parent frame is an inlined wasm call, callerFP_ points to the fake
|
||||
// exit frame.
|
||||
MOZ_ASSERT(!codeRange_);
|
||||
AssertDirectJitCall(callerFP_);
|
||||
unwoundJitCallerFP_ = Frame::isExitOrJitEntryFP(callerFP_)
|
||||
? Frame::toJitEntryCaller(callerFP_)
|
||||
: callerFP_;
|
||||
unwoundJitCallerFP_ = callerFP_;
|
||||
MOZ_ASSERT(done());
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -817,10 +817,10 @@ static bool GenerateInterpEntry(MacroAssembler& masm, const FuncExport& fe,
|
|||
SetupABIArguments(masm, fe, argv, scratch);
|
||||
|
||||
// Setup wasm register state. Ensure the frame pointer passed by the C++
|
||||
// caller doesn't have the ExitOrJitEntryFPTag bit set to not confuse frame
|
||||
// iterators. This bit shouldn't be set if C++ code is using frame pointers,
|
||||
// so this has no effect on native stack unwinders.
|
||||
masm.andPtr(Imm32(int32_t(~ExitOrJitEntryFPTag)), FramePointer);
|
||||
// caller doesn't have the ExitFPTag bit set to not confuse frame iterators.
|
||||
// This bit shouldn't be set if C++ code is using frame pointers, so this has
|
||||
// no effect on native stack unwinders.
|
||||
masm.andPtr(Imm32(int32_t(~ExitFPTag)), FramePointer);
|
||||
|
||||
masm.loadWasmPinnedRegsFromInstance();
|
||||
|
||||
|
@ -1452,12 +1452,10 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
|
|||
// frame pointer. We also use this to restore the frame pointer after the
|
||||
// call.
|
||||
*callOffset = masm.buildFakeExitFrame(scratch);
|
||||
// FP := ExitFrameLayout*
|
||||
masm.moveStackPtrTo(FramePointer);
|
||||
masm.loadJSContext(scratch);
|
||||
masm.enterFakeExitFrame(scratch, scratch, ExitFrameType::DirectWasmJitCall);
|
||||
// FP := ExitFrameLayout* | ExitOrJitEntryFPTag
|
||||
masm.moveStackPtrTo(FramePointer);
|
||||
masm.addPtr(Imm32(ExitFooterFrame::Size()), FramePointer);
|
||||
masm.orPtr(Imm32(ExitOrJitEntryFPTag), FramePointer);
|
||||
|
||||
// Move stack arguments to their final locations.
|
||||
unsigned bytesNeeded = StackArgBytesForWasmABI(fe.funcType());
|
||||
|
|
Загрузка…
Ссылка в новой задаче