commit 9b710255f243aeb399642d6af4c713f8f1b61339
parent 36aa80e5a65150d11dfb757f8597e192c1d5e096
Author: Ryan Hunt <rhunt@eqrion.net>
Date: Tue, 18 Nov 2025 16:57:29 +0000
Bug 1990931 - wasm: Move stack overflow check trap OOL. r=yury
Differential Revision: https://phabricator.services.mozilla.com/D271457
Diffstat:
7 files changed, 47 insertions(+), 45 deletions(-)
diff --git a/js/src/jit-test/tests/wasm/simd/const-arm64-vixl-codegen.js b/js/src/jit-test/tests/wasm/simd/const-arm64-vixl-codegen.js
@@ -9,7 +9,7 @@ var prefix = `${lead}sub sp, sp, #0x.. \\(..\\)
${lead}str x23, \\[sp, #..\\]`;
var suffix =
-`${lead}b #\\+0x14 \\(addr 0x.*\\)
+`${lead}b #\\+0x18 \\(addr 0x.*\\)
${lead}brk #0xf000`;
for ( let [bits, expected, values] of [
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
@@ -17146,7 +17146,13 @@ bool CodeGenerator::generateWasm(wasm::CallIndirectId callIndirectId,
// need to check for interrupts.
MOZ_ASSERT(omitOverRecursedInterruptCheck());
} else {
- masm.wasmReserveStackChecked(frameSize(), entryTrapSiteDesc);
+ auto* ool = new (alloc())
+ LambdaOutOfLineCode([this, entryTrapSiteDesc](OutOfLineCode& ool) {
+ masm.wasmTrap(wasm::Trap::StackOverflow, entryTrapSiteDesc);
+ return true;
+ });
+ addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
+ masm.wasmReserveStackChecked(frameSize(), ool->entry());
if (!omitOverRecursedInterruptCheck()) {
wasm::StackMap* functionEntryStackMap = nullptr;
diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp
@@ -5835,40 +5835,28 @@ void MacroAssembler::wasmTrap(wasm::Trap trap,
append(trap, wasm::TrapMachineInsn::OfficialUD, fco.get(), trapSiteDesc);
}
-std::pair<CodeOffset, uint32_t> MacroAssembler::wasmReserveStackChecked(
- uint32_t amount, const wasm::TrapSiteDesc& trapSiteDesc) {
+uint32_t MacroAssembler::wasmReserveStackChecked(uint32_t amount, Label* fail) {
if (amount > MAX_UNCHECKED_LEAF_FRAME_SIZE) {
// The frame is large. Don't bump sp until after the stack limit check so
// that the trap handler isn't called with a wild sp.
- Label ok;
Register scratch = ABINonArgReg0;
moveStackPtrTo(scratch);
-
- Label trap;
- branchPtr(Assembler::Below, scratch, Imm32(amount), &trap);
+ branchPtr(Assembler::Below, scratch, Imm32(amount), fail);
subPtr(Imm32(amount), scratch);
- branchPtr(Assembler::Below,
+ branchPtr(Assembler::AboveOrEqual,
Address(InstanceReg, wasm::Instance::offsetOfStackLimit()),
- scratch, &ok);
-
- bind(&trap);
- wasmTrap(wasm::Trap::StackOverflow, trapSiteDesc);
- CodeOffset trapInsnOffset = CodeOffset(currentOffset());
-
- bind(&ok);
+ scratch, fail);
reserveStack(amount);
- return std::pair<CodeOffset, uint32_t>(trapInsnOffset, 0);
+ // The stack amount was reserved after branching to the fail label.
+ return 0;
}
reserveStack(amount);
- Label ok;
- branchStackPtrRhs(Assembler::Below,
+ branchStackPtrRhs(Assembler::AboveOrEqual,
Address(InstanceReg, wasm::Instance::offsetOfStackLimit()),
- &ok);
- wasmTrap(wasm::Trap::StackOverflow, trapSiteDesc);
- CodeOffset trapInsnOffset = CodeOffset(currentOffset());
- bind(&ok);
- return std::pair<CodeOffset, uint32_t>(trapInsnOffset, amount);
+ fail);
+ // The stack amount was reserved before branching to the fail label.
+ return amount;
}
static void MoveDataBlock(MacroAssembler& masm, Register base, int32_t from,
diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h
@@ -3707,11 +3707,10 @@ class MacroAssembler : public MacroAssemblerSpecific {
void loadWasmPinnedRegsFromInstance(
const wasm::MaybeTrapSiteDesc& trapSiteDesc);
- // Returns a pair: the offset of the undefined (trapping) instruction, and
- // the number of extra bytes of stack allocated prior to the trap
- // instruction proper.
- std::pair<CodeOffset, uint32_t> wasmReserveStackChecked(
- uint32_t amount, const wasm::TrapSiteDesc& trapSiteDesc);
+ // Branches to the fail label if the stack would overflow the current stack
+ // limit. Returns the number of extra bytes of stack allocated prior to
+ // branching to the fail label.
+ uint32_t wasmReserveStackChecked(uint32_t amount, Label* fail);
// Emit a bounds check against the wasm heap limit, jumping to 'ok' if 'cond'
// holds; this can be the label either of the access or of the trap. The
diff --git a/js/src/wasm/WasmBCFrame.h b/js/src/wasm/WasmBCFrame.h
@@ -539,14 +539,11 @@ class BaseStackFrame final : public BaseStackFrameAllocator {
// Note the platform scratch register may be used by branchPtr(), so
// generally tmp must be something else.
- void checkStack(Register tmp, TrapSiteDesc trapSiteDesc) {
+ void checkStack(Register tmp, Label* stackOverflowTrap) {
stackAddOffset_ = masm.sub32FromStackPtrWithPatch(tmp);
- Label ok;
- masm.branchPtr(Assembler::Below,
+ masm.branchPtr(Assembler::AboveOrEqual,
Address(InstanceReg, wasm::Instance::offsetOfStackLimit()),
- tmp, &ok);
- masm.wasmTrap(Trap::StackOverflow, trapSiteDesc);
- masm.bind(&ok);
+ tmp, stackOverflowTrap);
}
void patchCheckStack() {
diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp
@@ -245,7 +245,6 @@ class OutOfLineResumableTrap : public OutOfLineCode {
masm->wasmTrap(trap_, desc_);
if (stackMap_ && !stackMaps_->add(masm->currentOffset(), stackMap_)) {
masm->setOOM();
- return;
}
masm->jump(rejoin());
}
@@ -574,9 +573,6 @@ bool BaseCompiler::beginFunction() {
// Generate a stack-overflow check and its associated stackmap.
- fr.checkStack(ABINonArgReg0,
- TrapSiteDesc(BytecodeOffset(func_.lineOrBytecode)));
-
ExitStubMapVector extras;
StackMap* functionEntryStackMap;
if (!stackMapGenerator_.generateStackmapEntriesForTrapExit(args, &extras) ||
@@ -586,15 +582,25 @@ bool BaseCompiler::beginFunction() {
return false;
}
- OutOfLineCode* ool = addOutOfLineCode(new (alloc_) OutOfLineResumableTrap(
- Trap::CheckInterrupt, trapSiteDesc(), functionEntryStackMap, stackMaps_));
- if (!ool) {
+ OutOfLineCode* oolStackOverflowTrap =
+ addOutOfLineCode(new (alloc_) OutOfLineAbortingTrap(
+ Trap::StackOverflow,
+ TrapSiteDesc(BytecodeOffset(func_.lineOrBytecode))));
+ if (!oolStackOverflowTrap) {
+ return false;
+ }
+ fr.checkStack(ABINonArgReg0, oolStackOverflowTrap->entry());
+
+ OutOfLineCode* oolInterruptTrap = addOutOfLineCode(
+ new (alloc_) OutOfLineResumableTrap(Trap::CheckInterrupt, trapSiteDesc(),
+ functionEntryStackMap, stackMaps_));
+ if (!oolInterruptTrap) {
return false;
}
masm.branch32(Assembler::NotEqual,
Address(InstanceReg, wasm::Instance::offsetOfInterrupt()),
- Imm32(0), ool->entry());
- masm.bind(ool->rejoin());
+ Imm32(0), oolInterruptTrap->entry());
+ masm.bind(oolInterruptTrap->rejoin());
size_t reservedBytes = fr.fixedAllocSize() - masm.framePushed();
MOZ_ASSERT(0 == (reservedBytes % sizeof(void*)));
diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp
@@ -1894,7 +1894,8 @@ static bool GenerateImportFunction(jit::MacroAssembler& masm,
sizeof(Frame), // pushed by prologue
StackArgBytesForWasmABI(funcType) + sizeOfInstanceSlot);
- masm.wasmReserveStackChecked(framePushed, TrapSiteDesc());
+ Label stackOverflowTrap;
+ masm.wasmReserveStackChecked(framePushed, &stackOverflowTrap);
MOZ_ASSERT(masm.framePushed() == framePushed);
@@ -1941,6 +1942,11 @@ static bool GenerateImportFunction(jit::MacroAssembler& masm,
masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1);
GenerateFunctionEpilogue(masm, framePushed, offsets);
+
+ // Emit the stack overflow trap as OOL code.
+ masm.bind(&stackOverflowTrap);
+ masm.wasmTrap(wasm::Trap::StackOverflow, wasm::TrapSiteDesc());
+
return FinishOffsets(masm, offsets);
}