commit 3242115cd4ed390910a7ed8009d6312ab61b237a
parent e277fc4fc6061cdbf973371a4c01e648e9fc5711
Author: Iain Ireland <iireland@mozilla.com>
Date: Mon, 6 Oct 2025 20:44:10 +0000
Bug 1989107: Don't build rectifier frames during bailouts r=jandem
Differential Revision: https://phabricator.services.mozilla.com/D267104
Diffstat:
1 file changed, 92 insertions(+), 170 deletions(-)
diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp
@@ -195,10 +195,12 @@ class MOZ_STACK_CLASS BaselineStackBuilder {
[[nodiscard]] bool prepareForNextFrame(HandleValueVector savedCallerArgs);
[[nodiscard]] bool finishOuterFrame();
+
+ template <typename GetSlot>
+ [[nodiscard]] bool buildStubFrameArgs(uint32_t actualArgs, bool constructing,
+ GetSlot getSlot);
[[nodiscard]] bool buildStubFrame(uint32_t frameSize,
HandleValueVector savedCallerArgs);
- [[nodiscard]] bool buildRectifierFrame(uint32_t actualArgc,
- size_t endOfBaselineStubArgs);
#ifdef DEBUG
[[nodiscard]] bool validateFrame();
@@ -972,6 +974,64 @@ bool BaselineStackBuilder::finishOuterFrame() {
return writePtr(retAddr, "ReturnAddr");
}
+template <typename GetSlot>
+bool BaselineStackBuilder::buildStubFrameArgs(uint32_t actualArgc,
+ bool constructing,
+ GetSlot getSlot) {
+ const uint32_t CalleeOffset = 0;
+ const uint32_t ThisOffset = 1;
+ const uint32_t ArgsOffset = 2; // callee + this
+
+ Value callee = getSlot(CalleeOffset);
+ JSFunction* calleeFun = &callee.toObject().as<JSFunction>();
+
+ bool hasUnderflow = actualArgc < calleeFun->nargs();
+ uint32_t argsPushed = hasUnderflow ? calleeFun->nargs() : actualArgc;
+ uint32_t afterFrameSize =
+ (1 + argsPushed + constructing) * sizeof(Value) + JitFrameLayout::Size();
+ if (!maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding")) {
+ return false;
+ }
+
+ if (constructing) {
+ Value newTarget = getSlot(ArgsOffset + actualArgc);
+ if (!writeValue(newTarget, "NewTarget")) {
+ return false;
+ }
+ }
+
+ if (hasUnderflow) {
+ uint32_t numUndef = argsPushed - actualArgc;
+ for (uint32_t i = 0; i < numUndef; i++) {
+ if (!writeValue(UndefinedValue(), "UndefArgVal")) {
+ return false;
+ }
+ }
+ }
+
+ for (int32_t arg = actualArgc - 1; arg >= 0; arg--) {
+ Value v = getSlot(ArgsOffset + arg); // callee + this
+ if (!writeValue(v, "ArgVal")) {
+ return false;
+ }
+ }
+
+ Value v = getSlot(ThisOffset); // callee + this
+ if (!writeValue(v, "ThisVal")) {
+ return false;
+ }
+
+ // Push callee token (must be a JS Function)
+ JitSpew(JitSpew_BaselineBailouts, " Callee = %016" PRIx64,
+ callee.asRawBits());
+
+ if (!writePtr(CalleeToToken(calleeFun, constructing), "CalleeToken")) {
+ return false;
+ }
+
+ return true;
+}
+
bool BaselineStackBuilder::buildStubFrame(uint32_t frameSize,
HandleValueVector savedCallerArgs) {
// Build baseline stub frame:
@@ -1018,48 +1078,41 @@ bool BaselineStackBuilder::buildStubFrame(uint32_t frameSize,
// of the arguments is reversed relative to the baseline frame's stack
// values.
MOZ_ASSERT(IsIonInlinableOp(op_));
- bool pushedNewTarget = IsConstructPC(pc_);
+ bool constructing = IsConstructPC(pc_);
unsigned actualArgc;
Value callee;
if (needToSaveCallerArgs()) {
- // For accessors, the arguments are not on the stack anymore,
- // but they are copied in a vector and are written here.
+ MOZ_ASSERT(!constructing);
callee = savedCallerArgs[0];
actualArgc = IsSetPropOp(op_) ? 1 : 0;
- // Align the stack based on the number of arguments.
- size_t afterFrameSize =
- (actualArgc + 1) * sizeof(Value) + JitFrameLayout::Size();
- if (!maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding")) {
+ // For accessors, the arguments are not on the stack anymore,
+ // but they are copied in the savedCallerArgs vector.
+ if (!buildStubFrameArgs(actualArgc, constructing, [&](uint32_t idx) {
+ return savedCallerArgs[idx];
+ })) {
return false;
}
-
- // Push arguments.
- MOZ_ASSERT(actualArgc + 2 <= exprStackSlots());
- MOZ_ASSERT(savedCallerArgs.length() == actualArgc + 2);
- for (unsigned i = 0; i < actualArgc + 1; i++) {
- size_t arg = savedCallerArgs.length() - (i + 1);
- if (!writeValue(savedCallerArgs[arg], "ArgVal")) {
- return false;
- }
- }
} else if (resumeMode() == ResumeMode::InlinedFunCall && GET_ARGC(pc_) == 0) {
// When calling FunCall with 0 arguments, we push |undefined|
// for this. See BaselineCacheIRCompiler::pushFunCallArguments.
- MOZ_ASSERT(!pushedNewTarget);
+ MOZ_ASSERT(!constructing);
actualArgc = 0;
- // Align the stack based on pushing |this| and 0 arguments.
- size_t afterFrameSize = sizeof(Value) + JitFrameLayout::Size();
- if (!maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding")) {
- return false;
- }
- // Push an undefined value for |this|.
- if (!writeValue(UndefinedValue(), "ThisValue")) {
- return false;
- }
+
size_t calleeSlot = blFrame()->numValueSlots(frameSize) - 1;
callee = *blFrame()->valueSlot(calleeSlot);
-
+ if (!buildStubFrameArgs(actualArgc, constructing, [&](uint32_t idx) {
+ switch (idx) {
+ case 0:
+ return callee;
+ case 1:
+ return UndefinedValue(); // this
+ default:
+ MOZ_CRASH("unreachable");
+ }
+ })) {
+ return false;
+ }
} else {
MOZ_ASSERT(resumeMode() == ResumeMode::InlinedStandardCall ||
resumeMode() == ResumeMode::InlinedFunCall);
@@ -1070,43 +1123,17 @@ bool BaselineStackBuilder::buildStubFrame(uint32_t frameSize,
actualArgc--;
}
- // In addition to the formal arguments, we must also push |this|.
- // When calling a constructor, we must also push |newTarget|.
- uint32_t numArguments = actualArgc + 1 + pushedNewTarget;
-
- // Align the stack based on the number of arguments.
- size_t afterFrameSize =
- numArguments * sizeof(Value) + JitFrameLayout::Size();
- if (!maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding")) {
- return false;
- }
-
- // Copy the arguments and |this| from the BaselineFrame, in reverse order.
size_t valueSlot = blFrame()->numValueSlots(frameSize) - 1;
- size_t calleeSlot = valueSlot - numArguments;
-
- for (size_t i = valueSlot; i > calleeSlot; i--) {
- Value v = *blFrame()->valueSlot(i);
- if (!writeValue(v, "ArgVal")) {
- return false;
- }
+ size_t calleeSlot = valueSlot - actualArgc - 1 - constructing;
+ if (!buildStubFrameArgs(actualArgc, constructing, [&](uint32_t idx) {
+ return *blFrame()->valueSlot(calleeSlot + idx);
+ })) {
+ return false;
}
-
callee = *blFrame()->valueSlot(calleeSlot);
}
- // In case these arguments need to be copied on the stack again for a
- // rectifier frame, save the framePushed values here for later use.
- size_t endOfBaselineStubArgs = framePushed();
-
- // Push callee token (must be a JS Function)
- JitSpew(JitSpew_BaselineBailouts, " Callee = %016" PRIx64,
- callee.asRawBits());
-
JSFunction* calleeFun = &callee.toObject().as<JSFunction>();
- if (!writePtr(CalleeToToken(calleeFun, pushedNewTarget), "CalleeToken")) {
- return false;
- }
const ICEntry& icScriptEntry = icScript_->icEntryFromPCOffset(pcOff);
ICFallbackStub* icScriptFallback =
icScript_->fallbackStubForICEntry(&icScriptEntry);
@@ -1129,109 +1156,6 @@ bool BaselineStackBuilder::buildStubFrame(uint32_t frameSize,
// The stack must be aligned after the callee pushes the frame pointer.
MOZ_ASSERT((framePushed() + sizeof(void*)) % JitStackAlignment == 0);
- // Build a rectifier frame if necessary
- if (actualArgc < calleeFun->nargs() &&
- !buildRectifierFrame(actualArgc, endOfBaselineStubArgs)) {
- return false;
- }
-
- return true;
-}
-
-bool BaselineStackBuilder::buildRectifierFrame(uint32_t actualArgc,
- size_t endOfBaselineStubArgs) {
- // Push a reconstructed rectifier frame.
- // +===============+
- // | Padding? |
- // +---------------+
- // | UndefinedU |
- // +---------------+
- // | ... |
- // +---------------+
- // | Undefined0 |
- // +---------------+
- // | ArgA |
- // +---------------+
- // | ... |
- // +---------------+
- // | Arg0 |
- // +---------------+
- // | ThisV |
- // +---------------+
- // | CalleeToken |
- // +---------------+
- // | Descr(Rect) |
- // +---------------+
- // | ReturnAddr |
- // +===============+
-
- JitSpew(JitSpew_BaselineBailouts, " [RECTIFIER FRAME]");
- bool pushedNewTarget = IsConstructPC(pc_);
-
- if (!writePtr(prevFramePtr(), "PrevFramePtr")) {
- return false;
- }
- prevFramePtr_ = virtualPointerAtStackOffset(0);
-
- // Align the stack based on the number of arguments.
- size_t afterFrameSize =
- (nextCallee()->nargs() + 1 + pushedNewTarget) * sizeof(Value) +
- RectifierFrameLayout::Size();
- if (!maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding")) {
- return false;
- }
-
- // Copy new.target, if necessary.
- if (pushedNewTarget) {
- size_t newTargetOffset = (framePushed() - endOfBaselineStubArgs) +
- (actualArgc + 1) * sizeof(Value);
- Value newTargetValue = *valuePointerAtStackOffset(newTargetOffset);
- if (!writeValue(newTargetValue, "CopiedNewTarget")) {
- return false;
- }
- }
-
- // Push undefined for missing arguments.
- for (unsigned i = 0; i < (nextCallee()->nargs() - actualArgc); i++) {
- if (!writeValue(UndefinedValue(), "FillerVal")) {
- return false;
- }
- }
-
- // Copy arguments + thisv from BaselineStub frame.
- if (!subtract((actualArgc + 1) * sizeof(Value), "CopiedArgs")) {
- return false;
- }
- BufferPointer<uint8_t> stubArgsEnd =
- pointerAtStackOffset<uint8_t>(framePushed() - endOfBaselineStubArgs);
- JitSpew(JitSpew_BaselineBailouts, " MemCpy from %p", stubArgsEnd.get());
- memcpy(pointerAtStackOffset<uint8_t>(0).get(), stubArgsEnd.get(),
- (actualArgc + 1) * sizeof(Value));
-
- // Push calleeToken again.
- if (!writePtr(CalleeToToken(nextCallee(), pushedNewTarget), "CalleeToken")) {
- return false;
- }
-
- // Push rectifier frame descriptor
- size_t rectifierFrameDescr =
- MakeFrameDescriptorForJitCall(FrameType::Rectifier, actualArgc);
- if (!writeWord(rectifierFrameDescr, "Descriptor")) {
- return false;
- }
-
- // Push return address into the ArgumentsRectifier code, immediately after the
- // ioncode call.
- void* rectReturnAddr =
- cx_->runtime()->jitRuntime()->getArgumentsRectifierReturnAddr().value;
- MOZ_ASSERT(rectReturnAddr);
- if (!writePtr(rectReturnAddr, "ReturnAddr")) {
- return false;
- }
-
- // The stack must be aligned after the callee pushes the frame pointer.
- MOZ_ASSERT((framePushed() + sizeof(void*)) % JitStackAlignment == 0);
-
return true;
}
@@ -1484,9 +1408,9 @@ bool BaselineStackBuilder::buildOneFrame() {
// . .
// . . <-- If there are additional frames inlined into this
// | Descr(BLJS) | one, we finish this frame. We generate a stub
- // +---------------+ frame (and maybe also a rectifier frame) between
- // | ReturnAddr | this frame and the inlined frame.
- // +===============+ See: prepareForNextFrame()
+ // +---------------+ frame between this frame and the inlined frame.
+ // | ReturnAddr | See: prepareForNextFrame()
+ // +===============+
if (!initFrame()) {
return false;
@@ -1544,7 +1468,7 @@ bool BaselineStackBuilder::buildOneFrame() {
// Otherwise, this is an outer frame for an inlined call or
// accessor. We will be building an inner frame. Before that,
- // we must create a stub frame, and potentially a rectifier frame.
+ // we must create a stub frame.
return prepareForNextFrame(savedCallerArgs);
}
@@ -1577,7 +1501,6 @@ bool jit::BailoutIonToBaseline(JSContext* cx, JitActivation* activation,
// IonJS - Ion calling into Ion.
// BaselineStub - Baseline calling into Ion.
// Entry / WasmToJSJit - Interpreter or other (wasm) calling into Ion.
- // Rectifier - Arguments rectifier calling into Ion.
// BaselineJS - Resume'd Baseline, then likely OSR'd into Ion.
MOZ_ASSERT(iter.isBailoutJS());
#if defined(DEBUG) || defined(JS_JITSPEW)
@@ -1585,7 +1508,6 @@ bool jit::BailoutIonToBaseline(JSContext* cx, JitActivation* activation,
MOZ_ASSERT(JSJitFrameIter::isEntry(prevFrameType) ||
prevFrameType == FrameType::IonJS ||
prevFrameType == FrameType::BaselineStub ||
- prevFrameType == FrameType::Rectifier ||
prevFrameType == FrameType::TrampolineNative ||
prevFrameType == FrameType::IonICCall ||
prevFrameType == FrameType::BaselineJS ||