tor-browser

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

commit fbd3f6de34bddd69d840526efef308ece5aae068
parent aec4bd2c8c26c608e74b45433ee672321edcd228
Author: Jan de Mooij <jdemooij@mozilla.com>
Date:   Tue, 18 Nov 2025 12:03:06 +0000

Bug 1992990 part 7 - Always store stub folding bailout data to distinguish GuardShapeList and GuardMultipleShapes. r=iain

Differential Revision: https://phabricator.services.mozilla.com/D272483

Diffstat:
Ajs/src/jit-test/tests/cacheir/stub-fold-add-shapes.js | 25+++++++++++++++++++++++++
Mjs/src/jit/BaselineBailouts.cpp | 18+++++++++---------
Mjs/src/jit/BaselineCacheIRCompiler.cpp | 32++++++++++++++++++++++----------
Mjs/src/jit/Ion.cpp | 8++++----
Mjs/src/jit/IonTypes.h | 24++++++++++++------------
Mjs/src/jit/JitZone.h | 39+++++++++++++++++++++------------------
Mjs/src/jit/WarpCacheIRTranspiler.cpp | 4+---
7 files changed, 94 insertions(+), 56 deletions(-)

diff --git a/js/src/jit-test/tests/cacheir/stub-fold-add-shapes.js b/js/src/jit-test/tests/cacheir/stub-fold-add-shapes.js @@ -0,0 +1,25 @@ +function foo(obj) { + return obj.x + obj.y + obj.x + obj.y; +} +function f() { + with ({}) {} + var arr = [{x: 1, y: 0}, {x: 0, y: 1, z: 2}]; + var res = 0; + for (var i = 0; i < 10_000; i++) { + var obj = arr[i % arr.length]; + res += foo(obj); + if (i === 4_000) { + arr.push({x: 1, y: 2, z: 3, a: 4}); + } else if (i === 5_000) { + arr.push({x: 1, y: 3, z: 3, b: 4}); + } else if (i === 6_000) { + arr.push({x: 3, y: 2, z: 3, c: 4}); + } else if (i === 7_000) { + arr.push({x: 4, y: 1, z: 3, d: 4}); + } else if (i === 8_000) { + arr.push({x: 5, y: 3, z: 3, e: 4}); + } + } + assertEq(res, 43174); +} +f(); diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp @@ -1928,17 +1928,17 @@ bool jit::FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfoArg) { saveFailedICHash = true; break; - case BailoutKind::MonomorphicInlinedStubFolding: + case BailoutKind::StubFoldingGuardMultipleShapes: action = BailoutAction::InvalidateIfFrequent; saveFailedICHash = true; - if (innerScript != outerScript) { - // In the case where this instruction comes from a monomorphic-inlined - // ICScript, we need to ensure that we note the connection between the - // inner script and the outer script, so that we can properly track if - // we add a new case to the folded stub and avoid invalidating the - // outer script. - cx->zone()->jitZone()->noteStubFoldingBailout(innerScript, outerScript); - } + // A GuardMultipleShapes LIR instruction bailed out. + // + // Call noteStubFoldingBailout so that AttachBaselineCacheIRStub can + // distinguish this from a bailout from GuardShapeList. This lets us avoid + // an invalidation if we add a new case to the folded stub. We also need + // to store the connection between the inner script and the outer script + // so that we can find the outer script after monomorphic inlining. + cx->zone()->jitZone()->noteStubFoldingBailout(innerScript, outerScript); break; case BailoutKind::SpeculativePhi: diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -2559,17 +2559,28 @@ ICAttachResult js::jit::AttachBaselineCacheIRStub( outerScript->lineno(), outerScript->column().oneOriginValue()); // Instead of adding a new stub, we have added a new case to an existing - // folded stub. We do not have to invalidate Warp, because the - // ShapeListObject that stores the cases is shared between baseline and - // Warp. Reset the entered count for the fallback stub so that we can still - // transpile, and reset the bailout counter if we have already been - // transpiled. + // folded stub. For invalidating Warp code, there are two cases to consider: + // + // (1) If we used MGuardShapeList, we need to invalidate Warp code because + // it bakes in the old shape list. + // + // (2) If we used MGuardMultipleShapes, we do not need to invalidate Warp, + // because the ShapeListObject that stores the cases is shared between + // Baseline and Warp. + // + // If we have stub folding bailout data stored in the JitZone for this + // script, this must be case (2). In this case we reset the bailout counter + // if we have already been transpiled. + // + // In both cases we reset the entered count for the fallback stub so that we + // can still transpile. stub->resetEnteredCount(); JSScript* owningScript = nullptr; + bool hadGuardMultipleShapesBailout = false; if (cx->zone()->jitZone()->hasStubFoldingBailoutData(outerScript)) { - owningScript = cx->zone()->jitZone()->stubFoldingBailoutParent(); - JitSpew(JitSpew_StubFolding, - "Found stub folding bailout parent: %s:%u:%u", + owningScript = cx->zone()->jitZone()->stubFoldingBailoutOuter(); + hadGuardMultipleShapesBailout = true; + JitSpew(JitSpew_StubFolding, "Found stub folding bailout outer: %s:%u:%u", owningScript->filename(), owningScript->lineno(), owningScript->column().oneOriginValue()); } else { @@ -2578,14 +2589,15 @@ ICAttachResult js::jit::AttachBaselineCacheIRStub( : outerScript; } cx->zone()->jitZone()->clearStubFoldingBailoutData(); - if (stub->usedByTranspiler()) { + if (stub->usedByTranspiler() && hadGuardMultipleShapesBailout) { if (owningScript->hasIonScript()) { owningScript->ionScript()->resetNumFixableBailouts(); } else if (owningScript->hasJitScript()) { owningScript->jitScript()->clearFailedICHash(); } } else { - // Update the last IC counter if this is not a bailout from Ion. + // Update the last IC counter if this is not a GuardMultipleShapes bailout + // from Ion. owningScript->updateLastICStubCounter(); } return ICAttachResult::Attached; diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp @@ -535,10 +535,10 @@ void JitZone::traceWeak(JSTracer* trc, Zone* zone) { baselineCacheIRStubCodes_.traceWeak(trc); inlinedCompilations_.traceWeak(trc); - TraceWeakEdge(trc, &lastStubFoldingBailoutChild_, - "JitZone::lastStubFoldingBailoutChild_"); - TraceWeakEdge(trc, &lastStubFoldingBailoutParent_, - "JitZone::lastStubFoldingBailoutParent_"); + TraceWeakEdge(trc, &lastStubFoldingBailoutInner_, + "JitZone::lastStubFoldingBailoutInner_"); + TraceWeakEdge(trc, &lastStubFoldingBailoutOuter_, + "JitZone::lastStubFoldingBailoutOuter_"); } void JitZone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h @@ -106,16 +106,16 @@ enum class BailoutKind : uint8_t { // and avoid a bailout loop. TranspiledCacheIR, - // An instruction generated by stub folding which has been transpiled into - // a monomorphic-inlined script. If this instruction bails out, we will - // return to baseline and see if we add a new case to the folded stub. If - // we do, this should not count as a bailout for the purpose of eventually - // invalidating this script. Because the script containing the folded stub - // was inlined monomorphically, there's no direct connection between the - // inner script and the outer script. We store the inner and outer scripts - // so that we know which outer script to notify if we successfully add a - // new case to the folded stub. - MonomorphicInlinedStubFolding, + // A GuardMultipleShapes instruction generated by the transpiler after stub + // folding. If this instruction bails out, we will return to baseline and see + // if we add a new case to the folded stub. If we do, this should not count as + // a bailout for the purpose of eventually invalidating this script. + // + // If the script containing the folded stub was inlined monomorphically, + // there's no direct connection between the inner script and the outer script. + // We store the inner and outer scripts so that we know which outer script to + // notify if we successfully add a new case to the folded stub. + StubFoldingGuardMultipleShapes, // An optimistic unbox on the cold path for a non-Value phi failed. If this // instruction bails out, we will invalidate the script and mark the @@ -207,8 +207,8 @@ inline const char* BailoutKindString(BailoutKind kind) { return "Unknown"; case BailoutKind::TranspiledCacheIR: return "TranspiledCacheIR"; - case BailoutKind::MonomorphicInlinedStubFolding: - return "MonomorphicInlinedStubFolding"; + case BailoutKind::StubFoldingGuardMultipleShapes: + return "StubFoldingGuardMultipleShapes"; case BailoutKind::SpeculativePhi: return "SpeculativePhi"; case BailoutKind::TypePolicy: diff --git a/js/src/jit/JitZone.h b/js/src/jit/JitZone.h @@ -130,12 +130,15 @@ class JitZone { mozilla::LinkedList<JitScript> jitScripts_; // The following two fields are a pair of associated scripts. If they are - // non-null, the child has been inlined into the parent, and we have bailed - // out due to a MonomorphicInlinedStubFolding bailout. If it wasn't - // trial-inlined, we need to track for the parent if we attach a new case to - // the corresponding folded stub which belongs to the child. - WeakHeapPtr<JSScript*> lastStubFoldingBailoutChild_; - WeakHeapPtr<JSScript*> lastStubFoldingBailoutParent_; + // non-null, we have bailed out from MGuardMultipleShapes. The inner and outer + // scripts are either the same script (when no inlining happened) or else the + // inner script was inlined into the outer script. + // + // This is used to distinguish a bailout from MGuardShapeList vs + // MGuardMultipleShapes, and for monomorphic inlining we need to track the + // outer script that inlined the inner script. + WeakHeapPtr<JSScript*> lastStubFoldingBailoutInner_; + WeakHeapPtr<JSScript*> lastStubFoldingBailoutOuter_; // The JitZone stores stubs to concatenate strings inline and perform RegExp // calls inline. These bake in zone specific pointers and can't be stored in @@ -227,22 +230,22 @@ class JitZone { inlinedCompilations_.remove(inlined); } - void noteStubFoldingBailout(JSScript* child, JSScript* parent) { - lastStubFoldingBailoutChild_ = child; - lastStubFoldingBailoutParent_ = parent; + void noteStubFoldingBailout(JSScript* inner, JSScript* outer) { + lastStubFoldingBailoutInner_ = inner; + lastStubFoldingBailoutOuter_ = outer; } - bool hasStubFoldingBailoutData(JSScript* child) const { - return lastStubFoldingBailoutChild_ && - lastStubFoldingBailoutChild_.get() == child && - lastStubFoldingBailoutParent_; + bool hasStubFoldingBailoutData(JSScript* inner) const { + return lastStubFoldingBailoutInner_ && + lastStubFoldingBailoutInner_.get() == inner && + lastStubFoldingBailoutOuter_; } - JSScript* stubFoldingBailoutParent() const { - MOZ_ASSERT(lastStubFoldingBailoutChild_); - return lastStubFoldingBailoutParent_.get(); + JSScript* stubFoldingBailoutOuter() const { + MOZ_ASSERT(lastStubFoldingBailoutInner_); + return lastStubFoldingBailoutOuter_.get(); } void clearStubFoldingBailoutData() { - lastStubFoldingBailoutChild_ = nullptr; - lastStubFoldingBailoutParent_ = nullptr; + lastStubFoldingBailoutInner_ = nullptr; + lastStubFoldingBailoutOuter_ = nullptr; } void registerJitScript(JitScript* script) { jitScripts_.insertBack(script); } diff --git a/js/src/jit/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp @@ -522,9 +522,7 @@ bool WarpCacheIRTranspiler::emitGuardMultipleShapes(ObjOperandId objId, } else { MInstruction* shapeList = objectStubField(shapesOffset); ins = MGuardMultipleShapes::New(alloc(), def, shapeList); - if (builder_->info().inlineScriptTree()->hasSharedICScript()) { - ins->setBailoutKind(BailoutKind::MonomorphicInlinedStubFolding); - } + ins->setBailoutKind(BailoutKind::StubFoldingGuardMultipleShapes); } add(ins);