ported from UXP: Issue #3092 - Refactor WASM compilation handling (a7a75b78)

(some portions are from forward commits to make it able to be compiled)
This commit is contained in:
2026-05-19 23:25:43 +08:00
parent 1f850cf78e
commit 995ff8e86c
4 changed files with 75 additions and 54 deletions
+12 -12
View File
@@ -20,6 +20,7 @@
#include "vm/SharedImmutableStringsCache.h"
#include "vm/Time.h"
#include "vm/TraceLogging.h"
#include "wasm/WasmGenerator.h"
#include "jscntxtinlines.h"
#include "jscompartmentinlines.h"
@@ -473,7 +474,8 @@ js::CancelOffThreadParses(JSRuntime* rt)
}
// Clean up any parse tasks which haven't been finished by the main thread.
GlobalHelperThreadState::ParseTaskVector& finished = HelperThreadState().parseFinishedList(lock);
GlobalHelperThreadState::ParseTaskVector& finished =
HelperThreadState().parseFinishedList(lock);
while (true) {
bool found = false;
for (size_t i = 0; i < finished.length(); i++) {
@@ -481,7 +483,8 @@ js::CancelOffThreadParses(JSRuntime* rt)
if (task->runtimeMatches(rt)) {
found = true;
AutoUnlockHelperThreadState unlock(lock);
HelperThreadState().cancelParseTask(rt->contextFromMainThread(), task->kind, task);
HelperThreadState().cancelParseTask(rt->contextFromMainThread(),
task->kind, task);
}
}
if (!found)
@@ -961,8 +964,7 @@ GlobalHelperThreadState::maxGCParallelThreads() const
bool
GlobalHelperThreadState::canStartWasmCompile(const AutoLockHelperThreadState& lock)
{
// Don't execute an wasm job if an earlier one failed.
if (wasmWorklist(lock).empty() || numWasmFailedJobs)
if (wasmWorklist(lock).empty())
return false;
// Honor the maximum allowed threads to compile wasm jobs at once,
@@ -1414,15 +1416,13 @@ HelperThread::handleWasmWorkload(AutoLockHelperThreadState& locked)
success = wasm::CompileFunction(task, &error);
}
// On success, try to move work to the finished list.
if (success)
success = HelperThreadState().wasmFinishedList(locked).append(task);
// Append the task to the finished queue owned by its module generator.
if (!success)
task->setFailed();
// On failure, note the failure for harvesting by the parent.
if (!success) {
HelperThreadState().noteWasmFailure(locked);
HelperThreadState().setWasmError(locked, Move(error));
}
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!task->finishedList()->append(task))
oomUnsafe.crash("HelperThread::handleWasmWorkload");
// Notify the main thread in case it's waiting.
HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
+2 -2
View File
@@ -90,8 +90,8 @@ class GlobalHelperThreadState
wasm::CompileTaskPtrVector wasmWorklist_, wasmFinishedList_;
public:
// For now, only allow a single parallel wasm compilation to happen at a
// time. This avoids race conditions on wasmWorklist/wasmFinishedList/etc.
// Helper-thread initiated wasm compilations are serialized to avoid the
// deadlock scenario described in WasmGenerator.cpp.
mozilla::Atomic<bool> wasmCompilationInProgress;
private:
+37 -39
View File
@@ -55,6 +55,7 @@ ModuleGenerator::ModuleGenerator(UniqueChars* error)
lastPatchedCallsite_(0),
startOfUnpatchedCallsites_(0),
parallel_(false),
parallelCompilationInProgressOnHelperThread_(false),
outstanding_(0),
currentTask_(nullptr),
batchedBytecode_(0),
@@ -74,18 +75,21 @@ ModuleGenerator::~ModuleGenerator()
AutoLockHelperThreadState lock;
while (true) {
CompileTaskPtrVector& worklist = HelperThreadState().wasmWorklist(lock);
MOZ_ASSERT(outstanding_ >= worklist.length());
outstanding_ -= worklist.length();
worklist.clear();
for (size_t i = worklist.length(); i > 0;) {
if (worklist[i - 1]->finishedList() == &finishedTasks_) {
HelperThreadState().remove(worklist, &i);
MOZ_ASSERT(outstanding_ > 0);
outstanding_--;
} else {
i--;
}
}
CompileTaskPtrVector& finished = HelperThreadState().wasmFinishedList(lock);
MOZ_ASSERT(outstanding_ >= finished.length());
outstanding_ -= finished.length();
finished.clear();
uint32_t numFailed = HelperThreadState().harvestFailedWasmJobs(lock);
MOZ_ASSERT(outstanding_ >= numFailed);
outstanding_ -= numFailed;
for (size_t i = finishedTasks_.length(); i > 0;) {
HelperThreadState().remove(finishedTasks_, &i);
MOZ_ASSERT(outstanding_ > 0);
outstanding_--;
}
if (!outstanding_)
break;
@@ -94,8 +98,10 @@ ModuleGenerator::~ModuleGenerator()
}
}
MOZ_ASSERT(HelperThreadState().wasmCompilationInProgress);
HelperThreadState().wasmCompilationInProgress = false;
if (parallelCompilationInProgressOnHelperThread_) {
MOZ_ASSERT(HelperThreadState().wasmCompilationInProgress);
HelperThreadState().wasmCompilationInProgress = false;
}
} else {
MOZ_ASSERT(!outstanding_);
}
@@ -252,17 +258,9 @@ ModuleGenerator::finishOutstandingTask()
while (true) {
MOZ_ASSERT(outstanding_ > 0);
if (HelperThreadState().wasmFailed(lock)) {
if (error_) {
MOZ_ASSERT(!*error_, "Should have stopped earlier");
*error_ = Move(HelperThreadState().harvestWasmError(lock));
}
return false;
}
if (!HelperThreadState().wasmFinishedList(lock).empty()) {
if (!finishedTasks_.empty()) {
outstanding_--;
task = HelperThreadState().wasmFinishedList(lock).popCopy();
task = finishedTasks_.popCopy();
break;
}
@@ -270,6 +268,9 @@ ModuleGenerator::finishOutstandingTask()
}
}
if (task->failed())
return false;
return finishTask(task);
}
@@ -435,6 +436,9 @@ ModuleGenerator::patchFarJumps(const TrapExitOffsetArray& trapExits, const Offse
bool
ModuleGenerator::finishTask(CompileTask* task)
{
if (task->failed())
return false;
masm_.haltingAlign(CodeAlignment);
// Before merging in the new function's code, if calls in a prior function
@@ -865,34 +869,25 @@ ModuleGenerator::startFuncDefs()
MOZ_ASSERT(!startedFuncDefs_);
MOZ_ASSERT(!finishedFuncDefs_);
// The wasmCompilationInProgress atomic ensures that there is only one
// parallel compilation in progress at a time. In the special case of
// asm.js, where the ModuleGenerator itself can be on a helper thread, this
// avoids the possibility of deadlock since at most 1 helper thread will be
// blocking on other helper threads and there are always >1 helper threads.
// With wasm, this restriction could be relaxed by moving the worklist state
// out of HelperThreadState since each independent compilation needs its own
// worklist pair. Alternatively, the deadlock could be avoided by having the
// ModuleGenerator thread make progress (on compile tasks) instead of
// blocking.
// Helper-thread initiated wasm compilations stay serialized so that we do
// not end up with multiple helper threads blocking on other helper threads.
// Main-thread compilations can still overlap because they drain their own
// finished-task queue and do not steal tasks from other generators.
GlobalHelperThreadState& threads = HelperThreadState();
MOZ_ASSERT(threads.threadCount > 1);
uint32_t numTasks;
if (CanUseExtraThreads() &&
threads.cpuCount > 1 &&
threads.wasmCompilationInProgress.compareExchange(false, true))
{
if (CanUseExtraThreads() && (!CurrentHelperThread() ||
threads.wasmCompilationInProgress.compareExchange(false, true))) {
#ifdef DEBUG
{
AutoLockHelperThreadState lock;
MOZ_ASSERT(!HelperThreadState().wasmFailed(lock));
MOZ_ASSERT(HelperThreadState().wasmWorklist(lock).empty());
MOZ_ASSERT(HelperThreadState().wasmFinishedList(lock).empty());
}
#endif
parallel_ = true;
parallelCompilationInProgressOnHelperThread_ = !!CurrentHelperThread();
numTasks = 2 * threads.maxWasmCompilationThreads();
} else {
numTasks = 1;
@@ -903,6 +898,9 @@ ModuleGenerator::startFuncDefs()
for (size_t i = 0; i < numTasks; i++)
tasks_.infallibleEmplaceBack(*env_, compileMode_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
for (auto& task : tasks_)
task.setFinishedList(&finishedTasks_);
if (!freeTasks_.reserve(numTasks))
return false;
for (size_t i = 0; i < numTasks; i++)
+24 -1
View File
@@ -142,6 +142,8 @@ class CompileTask
Maybe<jit::MacroAssembler> masm_;
FuncCompileUnitVector units_;
bool debugEnabled_;
CompileTaskPtrVector* finishedList_;
bool failed_;
CompileTask(const CompileTask&) = delete;
CompileTask& operator=(const CompileTask&) = delete;
@@ -156,10 +158,28 @@ class CompileTask
CompileTask(const ModuleEnvironment& env, CompileMode mode, size_t defaultChunkSize)
: env_(env),
mode_(mode),
lifo_(defaultChunkSize)
lifo_(defaultChunkSize),
finishedList_(nullptr),
failed_(false)
{
init();
}
void setFinishedList(CompileTaskPtrVector* finishedList) {
finishedList_ = finishedList;
}
CompileTaskPtrVector* finishedList() const {
MOZ_ASSERT(finishedList_);
return finishedList_;
}
void setFailed() {
failed_ = true;
}
bool failed() const {
return failed_;
}
LifoAlloc& lifo() {
return lifo_;
}
@@ -194,6 +214,7 @@ class CompileTask
masm_.reset();
alloc_.reset();
lifo_.releaseAll();
failed_ = false;
init();
return true;
@@ -238,9 +259,11 @@ class MOZ_STACK_CLASS ModuleGenerator
// Parallel compilation
bool parallel_;
bool parallelCompilationInProgressOnHelperThread_;
uint32_t outstanding_;
CompileTaskVector tasks_;
CompileTaskPtrVector freeTasks_;
CompileTaskPtrVector finishedTasks_;
UniqueFuncBytesVector freeFuncBytes_;
CompileTask* currentTask_;
uint32_t batchedBytecode_;