commit a5a3b6609dec82004b0047d40a83dab0031e16ab
parent 990dfa57ae137a925d86886b7fdb0a23301d5621
Author: Ryan Hunt <rhunt@eqrion.net>
Date: Thu, 18 Dec 2025 16:45:27 +0000
Bug 2002625 - wasm: Drop suspendedBy field and use SuspenderState for this. r=yury
Also add assertions to accessors around current state.
Differential Revision: https://phabricator.services.mozilla.com/D274189
Diffstat:
2 files changed, 78 insertions(+), 38 deletions(-)
diff --git a/js/src/wasm/WasmPI.cpp b/js/src/wasm/WasmPI.cpp
@@ -63,12 +63,15 @@ SuspenderObjectData::SuspenderObjectData(void* stackMemory)
suspendableFP_(nullptr),
suspendableSP_(static_cast<uint8_t*>(stackMemory) +
SuspendableStackPlusRedZoneSize),
- state_(SuspenderState::Initial),
- suspendedBy_(nullptr) {}
+ state_(SuspenderState::Initial) {}
void SuspenderObjectData::releaseStackMemory() {
- js_free(stackMemory_);
- stackMemory_ = nullptr;
+ MOZ_ASSERT(isMoribund() == !stackMemory_);
+ if (stackMemory_) {
+ js_free(stackMemory_);
+ stackMemory_ = nullptr;
+ setState(SuspenderState::Moribund);
+ }
}
# if defined(JS_SIMULATOR_ARM64)
@@ -159,8 +162,8 @@ static JitActivation* FindSuspendableStackActivation(
}
void TraceSuspendableStack(JSTracer* trc, const SuspenderObjectData& data) {
+ MOZ_ASSERT(data.isTraceable());
void* exitFP = data.suspendableExitFP();
- MOZ_ASSERT(data.traceable());
// Create and iterator for wasm frames:
// - If a stack entry for suspended stack exists, the data.suspendableFP()
@@ -168,12 +171,12 @@ void TraceSuspendableStack(JSTracer* trc, const SuspenderObjectData& data) {
// - Otherwise, the stack is the part of the main stack, the context
// JitActivation frames will be used to trace.
WasmFrameIter iter =
- data.hasStackEntry()
+ data.isSuspended()
? WasmFrameIter(
static_cast<FrameWithInstances*>(data.suspendableFP()),
data.suspendedReturnAddress())
: WasmFrameIter(FindSuspendableStackActivation(trc, data));
- MOZ_ASSERT_IF(data.hasStackEntry(), iter.currentFrameStackSwitched());
+ MOZ_ASSERT_IF(data.isSuspended(), iter.currentFrameStackSwitched());
uintptr_t highestByteVisitedInPrevWasmFrame = 0;
while (true) {
MOZ_ASSERT(!iter.done());
@@ -282,14 +285,12 @@ void SuspenderObject::finalize(JS::GCContext* gcx, JSObject* obj) {
return;
}
SuspenderObjectData* data = suspender.data();
- if (data->state() == SuspenderState::Moribund) {
- MOZ_RELEASE_ASSERT(!data->stackMemory());
- } else {
- // Cleaning stack memory and removing from suspenders_.
- data->releaseStackMemory();
+ if (!data->isMoribund()) {
gcx->runtime()->mainContextFromOwnThread()->wasm().suspenders_.remove(
&suspender);
}
+ data->releaseStackMemory();
+ MOZ_ASSERT(data->isMoribund());
js_free(data);
}
@@ -303,7 +304,7 @@ void SuspenderObject::trace(JSTracer* trc, JSObject* obj) {
// The SuspenderObjectData refers stacks frames that need to be traced
// only during major GC to determine if SuspenderObject content is
// reachable from JS.
- if (!data.traceable() || trc->isTenuringTracer()) {
+ if (!data.isTraceable() || trc->isTenuringTracer()) {
return;
}
TraceSuspendableStack(trc, data);
@@ -322,9 +323,11 @@ void SuspenderObject::setMoribund(JSContext* cx) {
MOZ_ASSERT(state() == SuspenderState::Active);
cx->wasm().leaveSuspendableStack(cx);
SuspenderObjectData* data = this->data();
- data->setState(SuspenderState::Moribund);
+ if (!data->isMoribund()) {
+ cx->wasm().suspenders_.remove(this);
+ }
data->releaseStackMemory();
- cx->wasm().suspenders_.remove(this);
+ MOZ_ASSERT(data->isMoribund());
}
void SuspenderObject::setActive(JSContext* cx) {
@@ -337,6 +340,11 @@ void SuspenderObject::setSuspended(JSContext* cx) {
cx->wasm().leaveSuspendableStack(cx);
}
+void SuspenderObject::setCalledOnMain(JSContext* cx) {
+ data()->setState(SuspenderState::CalledOnMain);
+ cx->wasm().leaveSuspendableStack(cx);
+}
+
void SuspenderObject::enter(JSContext* cx) {
MOZ_ASSERT(state() == SuspenderState::Initial);
setActive(cx);
@@ -345,7 +353,6 @@ void SuspenderObject::enter(JSContext* cx) {
void SuspenderObject::suspend(JSContext* cx) {
MOZ_ASSERT(state() == SuspenderState::Active);
setSuspended(cx);
- data()->setSuspendedBy(&cx->wasm());
if (cx->realm()->isDebuggee()) {
WasmFrameIter iter(cx->activation()->asJit());
@@ -365,7 +372,6 @@ void SuspenderObject::suspend(JSContext* cx) {
void SuspenderObject::resume(JSContext* cx) {
MOZ_ASSERT(state() == SuspenderState::Suspended);
setActive(cx);
- data()->setSuspendedBy(nullptr);
// Use barrier because object is being removed from the suspendable stack
// from roots.
gc::PreWriteBarrier(this);
@@ -387,7 +393,7 @@ void SuspenderObject::resume(JSContext* cx) {
}
void SuspenderObject::leave(JSContext* cx) {
- // We are exiting alternative stack if state is active,
+ // We are exiting suspended stack if state is active,
// otherwise the stack was just suspended.
switch (state()) {
case SuspenderState::Active: {
@@ -400,6 +406,7 @@ void SuspenderObject::leave(JSContext* cx) {
}
case SuspenderState::Initial:
case SuspenderState::Moribund:
+ case SuspenderState::CalledOnMain:
MOZ_CRASH();
}
}
@@ -419,10 +426,7 @@ bool CallOnMainStack(JSContext* cx, CallOnMainStackFn fn, void* data) {
SuspenderObjectData* stacks = suspender->data();
MOZ_ASSERT(suspender->state() == SuspenderState::Active);
- suspender->setSuspended(cx);
- // Keep suspendedBy not set -- the stack has no defined entry.
- // See TraceSuspendableStack for details.
- MOZ_RELEASE_ASSERT(suspender->data()->suspendedBy() == nullptr);
+ suspender->setCalledOnMain(cx);
# ifdef JS_SIMULATOR
# if defined(JS_SIMULATOR_ARM64) || defined(JS_SIMULATOR_ARM) || \
diff --git a/js/src/wasm/WasmPI.h b/js/src/wasm/WasmPI.h
@@ -115,10 +115,19 @@ class Context;
static const uint32_t SuspenderObjectDataSlot = 0;
enum SuspenderState {
+ // The suspender's stack hasn't been entered yet.
Initial,
+ // The suspender's stack has returned from the root frame and has been
+ // destroyed.
Moribund,
+ // The suspender's stack is active and is the currently active stack.
Active,
+ // The suspender's stack has been suspended and is no longer the active stack
+ // and isn't linked to the active stack.
Suspended,
+ // The suspender's stack has switched back to the main stack for a call. It
+ // is not the active call stack, but is linked to by the active stack.
+ CalledOnMain,
};
class SuspenderObjectData {
@@ -147,39 +156,65 @@ class SuspenderObjectData {
SuspenderState state_;
- // Identify context that is holding suspended stack, otherwise nullptr.
- Context* suspendedBy_;
-
public:
explicit SuspenderObjectData(void* stackMemory);
inline SuspenderState state() const { return state_; }
void setState(SuspenderState state) { state_ = state; }
- inline bool traceable() const {
+ // This suspender can be traced if it's not 'Initial' or 'Moribund'.
+ bool isTraceable() const {
return state_ == SuspenderState::Active ||
- state_ == SuspenderState::Suspended;
+ state_ == SuspenderState::Suspended ||
+ state_ == SuspenderState::CalledOnMain;
}
- inline bool hasStackEntry() const { return suspendedBy_ != nullptr; }
- inline Context* suspendedBy() const { return suspendedBy_; }
- void setSuspendedBy(Context* suspendedBy) { suspendedBy_ = suspendedBy; }
+ bool isMoribund() const { return state_ == SuspenderState::Moribund; }
+ bool isActive() const { return state_ == SuspenderState::Active; }
+ bool isSuspended() const { return state_ == SuspenderState::Suspended; }
+ bool isCalledOnMain() const { return state_ == SuspenderState::CalledOnMain; }
bool hasFramePointer(void* fp) const {
+ MOZ_ASSERT(!isMoribund());
return (uintptr_t)stackMemory_ <= (uintptr_t)fp &&
(uintptr_t)fp <
(uintptr_t)stackMemory_ + SuspendableStackPlusRedZoneSize;
}
- inline void* stackMemory() const { return stackMemory_; }
- inline void* mainFP() const { return mainFP_; }
- inline void* mainSP() const { return mainSP_; }
- inline void* mainExitFP() const { return mainExitFP_; }
- inline void* suspendableFP() const { return suspendableFP_; }
- inline void* suspendableSP() const { return suspendableSP_; }
- inline void* suspendableExitFP() const { return suspendableExitFP_; }
- inline void* suspendedReturnAddress() const {
+ void* stackMemory() const {
+ MOZ_ASSERT(!isMoribund());
+ return stackMemory_;
+ }
+
+ void* mainFP() const {
+ MOZ_ASSERT(isActive());
+ return mainFP_;
+ }
+ void* mainSP() const {
+ MOZ_ASSERT(isActive());
+ return mainSP_;
+ }
+ void* mainExitFP() const {
+ MOZ_ASSERT(isSuspended());
+ return mainExitFP_;
+ }
+ void* suspendableFP() const {
+ MOZ_ASSERT(isSuspended());
+ return suspendableFP_;
+ }
+ void* suspendableSP() const {
+ MOZ_ASSERT(isSuspended());
+ return suspendableSP_;
+ }
+ void* suspendedReturnAddress() const {
+ MOZ_ASSERT(isSuspended());
return suspendedReturnAddress_;
}
+ void* suspendableExitFP() const {
+ // We always have the root frame when we've been entered into, which is
+ // when we're traceable.
+ MOZ_ASSERT(isTraceable());
+ return suspendableExitFP_;
+ }
void releaseStackMemory();
@@ -273,6 +308,7 @@ class SuspenderObject : public NativeObject {
void setMoribund(JSContext* cx);
void setActive(JSContext* cx);
void setSuspended(JSContext* cx);
+ void setCalledOnMain(JSContext* cx);
void enter(JSContext* cx);
void suspend(JSContext* cx);