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:
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);