tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mjs/src/jit/BaselineBailouts.cpp | 262++++++++++++++++++++++++++++---------------------------------------------------
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 ||