Bug 850070 - Part 2/2 - Parallelize OdinMonkey compilations. r=luke

This commit is contained in:
Sean Stangl 2013-03-19 15:24:22 -07:00
parent d5179d8716
commit 08c26b02b9
10 changed files with 483 additions and 66 deletions

View File

@ -1,5 +1,5 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99 ft=cpp:
* vim: set ts=4 sw=4 et tw=99 ft=cpp:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -297,7 +297,7 @@ class Vector : private AllocPolicy
#endif
/* Append operations guaranteed to succeed due to pre-reserved space. */
template <class U> void internalAppend(U t);
template <class U> void internalAppend(U u);
void internalAppendN(const T &t, size_t n);
template <class U> void internalAppend(const U *begin, size_t length);
template <class U, size_t O, class BP> void internalAppend(const Vector<U,O,BP> &other);
@ -395,8 +395,11 @@ class Vector : private AllocPolicy
/* mutators */
/* Given that the Vector is empty and has no inline storage, grow to |capacity|. */
bool initCapacity(size_t capacity);
/* If reserve(length() + N) succeeds, the N next appends are guaranteed to succeed. */
bool reserve(size_t capacity);
bool reserve(size_t request);
/*
* Destroy elements in the range [end() - incr, end()). Does not deallocate
@ -441,8 +444,8 @@ class Vector : private AllocPolicy
* Guaranteed-infallible append operations for use upon vectors whose
* memory has been pre-reserved.
*/
void infallibleAppend(const T &t) {
internalAppend(t);
template <class U> void infallibleAppend(const U &u) {
internalAppend(u);
}
void infallibleAppendN(const T &t, size_t n) {
internalAppendN(t, n);
@ -699,6 +702,25 @@ Vector<T,N,AP>::growStorageBy(size_t incr)
return Impl::growTo(*this, newCap);
}
template <class T, size_t N, class AP>
inline bool
Vector<T,N,AP>::initCapacity(size_t capacity)
{
JS_ASSERT(empty());
JS_ASSERT(usingInlineStorage());
if (capacity == 0)
return true;
T *newbuf = reinterpret_cast<T *>(this->malloc_(capacity * sizeof(T)));
if (!newbuf)
return false;
mBegin = newbuf;
mCapacity = capacity;
#ifdef DEBUG
mReserved = capacity;
#endif
return true;
}
template <class T, size_t N, class AP>
inline bool
Vector<T,N,AP>::reserve(size_t request)
@ -837,11 +859,11 @@ Vector<T,N,AP>::append(U t)
template <class T, size_t N, class AP>
template <class U>
JS_ALWAYS_INLINE void
Vector<T,N,AP>::internalAppend(U t)
Vector<T,N,AP>::internalAppend(U u)
{
JS_ASSERT(mLength + 1 <= mReserved);
JS_ASSERT(mReserved <= mCapacity);
new(endNoCheck()) T(t);
new(endNoCheck()) T(u);
++mLength;
}

View File

@ -6,6 +6,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jsmath.h"
#include "jsworkers.h"
#include "frontend/ParseNode.h"
#include "ion/AsmJS.h"
#include "ion/AsmJSModule.h"
@ -1029,7 +1031,7 @@ class ModuleCompiler
typedef Vector<AsmJSGlobalAccess> GlobalAccessVector;
JSContext * cx_;
IonContext ionContext_;
IonContext ictx_;
MacroAssembler masm_;
ScopedJSDeletePtr<AsmJSModule> module_;
@ -1066,8 +1068,8 @@ class ModuleCompiler
public:
ModuleCompiler(JSContext *cx, TokenStream &ts)
: cx_(cx),
ionContext_(cx->runtime), // TODO: This serves as a temp assertion.
masm_(),
ictx_(cx->runtime),
masm_(cx),
moduleFunctionName_(NULL),
globalArgumentName_(NULL),
importArgumentName_(NULL),
@ -4431,6 +4433,194 @@ CheckFunctionBodiesSequential(ModuleCompiler &m)
return true;
}
#ifdef JS_PARALLEL_COMPILATION
// State of compilation as tracked and updated by the main thread.
struct ParallelGroupState
{
WorkerThreadState &state;
Vector<AsmJSParallelTask> &tasks;
int32_t outstandingJobs; // Good work, jobs!
uint32_t compiledJobs;
ParallelGroupState(WorkerThreadState &state, Vector<AsmJSParallelTask> &tasks)
: state(state), tasks(tasks), outstandingJobs(0), compiledJobs(0)
{ }
};
// Block until a worker-assigned LifoAlloc becomes finished.
static AsmJSParallelTask *
GetFinishedCompilation(ModuleCompiler &m, ParallelGroupState &group)
{
AutoLockWorkerThreadState lock(m.cx()->runtime);
while (!group.state.asmJSWorkerFailed()) {
if (!group.state.asmJSFinishedList.empty()) {
group.outstandingJobs--;
return group.state.asmJSFinishedList.popCopy();
}
group.state.wait(WorkerThreadState::MAIN);
}
return NULL;
}
static bool
GenerateCodeForFinishedJob(ModuleCompiler &m, ParallelGroupState &group, AsmJSParallelTask **outTask)
{
// Block until a used LifoAlloc becomes available.
AsmJSParallelTask *task = GetFinishedCompilation(m, group);
if (!task)
return false;
// Perform code generation on the main thread.
if (!GenerateAsmJSCode(m, m.function(task->funcNum), *task->mir, *task->lir))
return false;
group.compiledJobs++;
// Clear the LifoAlloc for use by another worker.
TempAllocator &tempAlloc = task->mir->temp();
tempAlloc.TempAllocator::~TempAllocator();
task->lifo.releaseAll();
*outTask = task;
return true;
}
static inline bool
GetUnusedTask(ParallelGroupState &group, uint32_t funcNum, AsmJSParallelTask **outTask)
{
// Since functions are dispatched in order, if fewer than |numLifos| functions
// have been generated, then the |funcNum'th| LifoAlloc must never have been
// assigned to a worker thread.
if (funcNum >= group.tasks.length())
return false;
*outTask = &group.tasks[funcNum];
return true;
}
static bool
CheckFunctionBodiesParallelImpl(ModuleCompiler &m, ParallelGroupState &group)
{
JS_ASSERT(group.state.asmJSWorklist.empty());
JS_ASSERT(group.state.asmJSFinishedList.empty());
group.state.resetAsmJSFailureState();
// Dispatch work for each function.
for (uint32_t i = 0; i < m.numFunctions(); i++) {
ModuleCompiler::Func &func = m.function(i);
// Get exclusive access to an empty LifoAlloc from the thread group's pool.
AsmJSParallelTask *task = NULL;
if (!GetUnusedTask(group, i, &task) && !GenerateCodeForFinishedJob(m, group, &task))
return false;
// Generate MIR into the LifoAlloc on the main thread.
MIRGenerator *mir = CheckFunctionBody(m, func, task->lifo);
if (!mir)
return false;
// Perform optimizations and LIR generation on a worker thread.
task->init(i, mir);
if (!StartOffThreadAsmJSCompile(m.cx(), task))
return false;
group.outstandingJobs++;
}
// Block for all outstanding workers to complete.
while (group.outstandingJobs > 0) {
AsmJSParallelTask *ignored = NULL;
if (!GenerateCodeForFinishedJob(m, group, &ignored))
return false;
}
JS_ASSERT(group.outstandingJobs == 0);
JS_ASSERT(group.compiledJobs == m.numFunctions());
JS_ASSERT(group.state.asmJSWorklist.empty());
JS_ASSERT(group.state.asmJSFinishedList.empty());
JS_ASSERT(!group.state.asmJSWorkerFailed());
return true;
}
static void
CancelOutstandingJobs(ModuleCompiler &m, ParallelGroupState &group)
{
// This is failure-handling code, so it's not allowed to fail.
// The problem is that all memory for compilation is stored in LifoAllocs
// maintained in the scope of CheckFunctionBodiesParallel() -- so in order
// for that function to safely return, and thereby remove the LifoAllocs,
// none of that memory can be in use or reachable by workers.
JS_ASSERT(group.outstandingJobs >= 0);
if (!group.outstandingJobs)
return;
AutoLockWorkerThreadState lock(m.cx()->runtime);
// From the compiling tasks, eliminate those waiting for worker assignation.
group.outstandingJobs -= group.state.asmJSWorklist.length();
group.state.asmJSWorklist.clear();
// From the compiling tasks, eliminate those waiting for codegen.
group.outstandingJobs -= group.state.asmJSFinishedList.length();
group.state.asmJSFinishedList.clear();
// Eliminate tasks that failed without adding to the finished list.
group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
// Any remaining tasks are therefore undergoing active compilation.
JS_ASSERT(group.outstandingJobs >= 0);
while (group.outstandingJobs > 0) {
group.state.wait(WorkerThreadState::MAIN);
group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
group.outstandingJobs -= group.state.asmJSFinishedList.length();
group.state.asmJSFinishedList.clear();
}
JS_ASSERT(group.outstandingJobs == 0);
JS_ASSERT(group.state.asmJSWorklist.empty());
JS_ASSERT(group.state.asmJSFinishedList.empty());
}
static const size_t LIFO_ALLOC_PARALLEL_CHUNK_SIZE = 1 << 12;
static bool
CheckFunctionBodiesParallel(ModuleCompiler &m)
{
// Saturate all worker threads plus the main thread.
WorkerThreadState &state = *m.cx()->runtime->workerThreadState;
size_t numParallelJobs = state.numThreads + 1;
// Allocate scoped AsmJSParallelTask objects. Each contains a unique
// LifoAlloc that provides all necessary memory for compilation.
Vector<AsmJSParallelTask, 0> tasks(m.cx());
if (!tasks.initCapacity(numParallelJobs))
return false;
for (size_t i = 0; i < numParallelJobs; i++)
tasks.infallibleAppend(LIFO_ALLOC_PARALLEL_CHUNK_SIZE);
// With compilation memory in-scope, dispatch worker threads.
ParallelGroupState group(state, tasks);
if (!CheckFunctionBodiesParallelImpl(m, group)) {
CancelOutstandingJobs(m, group);
// If failure was triggered by a worker thread, report error.
int32_t maybeFailureIndex = state.maybeGetAsmJSFailedFunctionIndex();
if (maybeFailureIndex >= 0) {
ParseNode *fn = m.function(maybeFailureIndex).fn();
return m.fail("Internal compiler failure (probably out of memory)", fn);
}
// Otherwise, the error occurred on the main thread and was already reported.
return false;
}
return true;
}
#endif // JS_PARALLEL_COMPILATION
static RegisterSet AllRegs = RegisterSet(GeneralRegisterSet(Registers::AllMask),
FloatRegisterSet(FloatRegisters::AllMask));
static RegisterSet NonVolatileRegs = RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask),
@ -4958,8 +5148,18 @@ CheckModule(JSContext *cx, TokenStream &ts, ParseNode *fn, ScopedJSDeletePtr<Asm
m.setFirstPassComplete();
#ifdef JS_PARALLEL_COMPILATION
if (OffThreadCompilationEnabled(cx)) {
if (!CheckFunctionBodiesParallel(m))
return false;
} else {
if (!CheckFunctionBodiesSequential(m))
return false;
}
#else
if (!CheckFunctionBodiesSequential(m))
return false;
#endif
m.setSecondPassComplete();
@ -5001,6 +5201,11 @@ js::CompileAsmJS(JSContext *cx, TokenStream &ts, ParseNode *fn, HandleScript scr
if (!EnsureAsmJSSignalHandlersInstalled(cx->runtime))
return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Platform missing signal handler support");
# ifdef JS_PARALLEL_COMPILATION
if (!EnsureParallelCompilationInitialized(cx->runtime))
return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Failed initialization of compilation threads");
# endif
ScopedJSDeletePtr<AsmJSModule> module;
if (!CheckModule(cx, ts, fn, &module))
return !cx->isExceptionPending();

View File

@ -27,6 +27,7 @@ namespace js {
class SPSProfiler;
class AsmJSModule;
namespace frontend { struct TokenStream; struct ParseNode; }
namespace ion { class MIRGenerator; class LIRGraph; }
// Return whether asm.js optimization is inhibitted by the platform or
// dynamically disabled. (Exposed as JSNative for shell testing.)
@ -125,6 +126,28 @@ class AsmJSMachExceptionHandler
};
#endif
// Struct type for passing parallel compilation data between the main thread
// and compilation workers.
struct AsmJSParallelTask
{
LifoAlloc lifo; // Provider of all heap memory used for compilation.
uint32_t funcNum; // Index |i| of function in |Module.function(i)|.
ion::MIRGenerator *mir; // Passed from main thread to worker.
ion::LIRGraph *lir; // Passed from worker to main thread.
AsmJSParallelTask(size_t defaultChunkSize)
: lifo(defaultChunkSize),
funcNum(0), mir(NULL), lir(NULL)
{ }
void init(uint32_t newFuncNum, ion::MIRGenerator *newMir) {
funcNum = newFuncNum;
mir = newMir;
lir = NULL;
}
};
} // namespace js
#endif // jsion_asmjs_h__

View File

@ -197,7 +197,7 @@ IonRuntime::initialize(JSContext *cx)
FrameSizeClass class_ = FrameSizeClass::FromClass(id);
if (class_ == FrameSizeClass::ClassLimit())
break;
bailoutTables_.infallibleAppend(NULL);
bailoutTables_.infallibleAppend((IonCode *)NULL);
bailoutTables_[id] = generateBailoutTable(cx, id);
if (!bailoutTables_[id])
return false;
@ -1261,14 +1261,6 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc,
return abortReason;
}
static inline bool
OffThreadCompilationEnabled(JSContext *cx)
{
return js_IonOptions.parallelCompilation
&& cx->runtime->useHelperThreads()
&& cx->runtime->helperThreadCount() != 0;
}
static inline bool
OffThreadCompilationAvailable(JSContext *cx)
{

View File

@ -80,8 +80,11 @@ class MacroAssembler : public MacroAssemblerSpecific
if (cx)
constructRoot(cx);
if (!GetIonContext()->temp)
if (!GetIonContext()->temp) {
JS_ASSERT(cx);
alloc_.construct(cx);
}
#ifdef JS_CPU_ARM
initWithAllocator();
m_buffer.id = GetIonContext()->getNextAssemblerId();

View File

@ -25,7 +25,7 @@ AllocationIntegrityState::record()
if (!virtualRegisters.reserve(graph.numVirtualRegisters()))
return false;
for (size_t i = 0; i < graph.numVirtualRegisters(); i++)
virtualRegisters.infallibleAppend(NULL);
virtualRegisters.infallibleAppend((LDefinition *)NULL);
if (!blocks.reserve(graph.numBlocks()))
return false;

View File

@ -50,7 +50,7 @@ StupidAllocator::init()
if (!virtualRegisters.reserve(graph.numVirtualRegisters()))
return false;
for (size_t i = 0; i < graph.numVirtualRegisters(); i++)
virtualRegisters.infallibleAppend(NULL);
virtualRegisters.infallibleAppend((LDefinition *)NULL);
for (size_t i = 0; i < graph.numBlocks(); i++) {
LBlock *block = graph.getBlock(i);

View File

@ -261,7 +261,7 @@ class Label : public LabelBase
// Note: the condition is a hack to silence this assert when OOM testing,
// see bug 756614.
if (!js_IonOptions.parallelCompilation)
JS_ASSERT_IF(!GetIonContext()->cx->runtime->hadOutOfMemory, !used());
JS_ASSERT_IF(!GetIonContext()->runtime->hadOutOfMemory, !used());
#endif
}
};

View File

@ -9,6 +9,7 @@
#include "jsworkers.h"
#if JS_ION
# include "ion/AsmJS.h"
# include "ion/IonBuilder.h"
# include "ion/ExecutionModeInlines.h"
#endif
@ -19,22 +20,57 @@ using mozilla::DebugOnly;
#ifdef JS_PARALLEL_COMPILATION
bool
js::EnsureParallelCompilationInitialized(JSRuntime *rt)
{
if (rt->workerThreadState)
return true;
rt->workerThreadState = rt->new_<WorkerThreadState>();
if (!rt->workerThreadState)
return false;
if (!rt->workerThreadState->init(rt)) {
js_delete(rt->workerThreadState);
rt->workerThreadState = NULL;
return false;
}
return true;
}
bool
js::StartOffThreadAsmJSCompile(JSContext *cx, AsmJSParallelTask *asmData)
{
// Threads already initialized by the AsmJS compiler.
JS_ASSERT(cx->runtime->workerThreadState);
JS_ASSERT(asmData->mir);
JS_ASSERT(asmData->lir == NULL);
WorkerThreadState &state = *cx->runtime->workerThreadState;
JS_ASSERT(state.numThreads);
AutoLockWorkerThreadState lock(cx->runtime);
// Don't append this task if another failed.
if (state.asmJSWorkerFailed())
return false;
if (!state.asmJSWorklist.append(asmData))
return false;
state.notify(WorkerThreadState::WORKER);
return true;
}
bool
js::StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder)
{
JSRuntime *rt = cx->runtime;
if (!rt->workerThreadState) {
rt->workerThreadState = rt->new_<WorkerThreadState>();
if (!rt->workerThreadState)
return false;
if (!rt->workerThreadState->init(rt)) {
js_delete(rt->workerThreadState);
rt->workerThreadState = NULL;
return false;
}
}
WorkerThreadState &state = *cx->runtime->workerThreadState;
if (!EnsureParallelCompilationInitialized(rt))
return false;
WorkerThreadState &state = *cx->runtime->workerThreadState;
JS_ASSERT(state.numThreads);
AutoLockWorkerThreadState lock(rt);
@ -43,7 +79,6 @@ js::StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder)
return false;
state.notify(WorkerThreadState::WORKER);
return true;
}
@ -162,6 +197,7 @@ WorkerThreadState::init(JSRuntime *rt)
}
}
resetAsmJSFailureState();
return true;
}
@ -245,6 +281,14 @@ WorkerThreadState::notifyAll(CondVar which)
PR_NotifyAllCondVar((which == MAIN) ? mainWakeup : helperWakeup);
}
bool
WorkerThreadState::canStartAsmJSCompile()
{
// Don't execute an AsmJS job if an earlier one failed.
JS_ASSERT(isLocked());
return (!asmJSWorklist.empty() && !numAsmJSFailedJobs);
}
bool
WorkerThreadState::canStartIonCompile()
{
@ -288,6 +332,77 @@ WorkerThread::ThreadMain(void *arg)
static_cast<WorkerThread *>(arg)->threadLoop();
}
void
WorkerThread::handleAsmJSWorkload(WorkerThreadState &state)
{
JS_ASSERT(state.isLocked());
JS_ASSERT(state.canStartAsmJSCompile());
JS_ASSERT(!ionBuilder && !asmData);
asmData = state.asmJSWorklist.popCopy();
bool success = false;
state.unlock();
do {
ion::IonContext icx(asmData->mir->compartment, &asmData->mir->temp());
if (!OptimizeMIR(asmData->mir))
break;
asmData->lir = GenerateLIR(asmData->mir);
if (!asmData->lir)
break;
success = true;
} while(0);
state.lock();
// On failure, signal parent for harvesting in CancelOutstandingJobs().
if (!success) {
asmData = NULL;
state.noteAsmJSFailure(asmData->funcNum);
state.notify(WorkerThreadState::MAIN);
return;
}
// On success, move work to the finished list.
state.asmJSFinishedList.append(asmData);
asmData = NULL;
// Notify the main thread in case it's blocked waiting for a LifoAlloc.
state.notify(WorkerThreadState::MAIN);
}
void
WorkerThread::handleIonWorkload(WorkerThreadState &state)
{
JS_ASSERT(state.isLocked());
JS_ASSERT(state.canStartIonCompile());
JS_ASSERT(!ionBuilder && !asmData);
ionBuilder = state.ionWorklist.popCopy();
DebugOnly<ion::ExecutionMode> executionMode = ionBuilder->info().executionMode();
JS_ASSERT(GetIonScript(ionBuilder->script(), executionMode) == ION_COMPILING_SCRIPT);
state.unlock();
{
ion::IonContext ictx(ionBuilder->script()->compartment(), &ionBuilder->temp());
ionBuilder->setBackgroundCodegen(ion::CompileBackEnd(ionBuilder));
}
state.lock();
FinishOffThreadIonCompile(ionBuilder);
ionBuilder = NULL;
// Notify the main thread in case it is waiting for the compilation to finish.
state.notify(WorkerThreadState::MAIN);
// Ping the main thread so that the compiled code can be incorporated
// at the next operation callback.
runtime->triggerOperationCallback();
}
void
WorkerThread::threadLoop()
{
@ -298,9 +413,10 @@ WorkerThread::threadLoop()
js::TlsPerThreadData.set(threadData.addr());
while (true) {
JS_ASSERT(!ionBuilder);
JS_ASSERT(!ionBuilder && !asmData);
while (!state.canStartIonCompile()) {
// Block until an Ion or AsmJS task is available.
while (!state.canStartIonCompile() && !state.canStartAsmJSCompile()) {
if (terminate) {
state.unlock();
return;
@ -308,39 +424,23 @@ WorkerThread::threadLoop()
state.wait(WorkerThreadState::WORKER);
}
ionBuilder = state.ionWorklist.popCopy();
DebugOnly<ion::ExecutionMode> executionMode = ionBuilder->info().executionMode();
JS_ASSERT(GetIonScript(ionBuilder->script(), executionMode) == ION_COMPILING_SCRIPT);
state.unlock();
{
ion::IonContext ictx(ionBuilder->script()->compartment(), &ionBuilder->temp());
ionBuilder->setBackgroundCodegen(ion::CompileBackEnd(ionBuilder));
}
state.lock();
FinishOffThreadIonCompile(ionBuilder);
ionBuilder = NULL;
/*
* Notify the main thread in case it is waiting for the compilation to
* finish.
*/
state.notify(WorkerThreadState::MAIN);
/*
* Ping the main thread so that the compiled code can be incorporated
* at the next operation callback.
*/
runtime->triggerOperationCallback();
// Dispatch tasks, prioritizing AsmJS work.
if (state.canStartAsmJSCompile())
handleAsmJSWorkload(state);
else if (state.canStartIonCompile())
handleIonWorkload(state);
}
}
#else /* JS_PARALLEL_COMPILATION */
bool
js::StartOffThreadAsmJSCompile(JSContext *cx, AsmJSParallelTask *asmData)
{
JS_NOT_REACHED("Off thread compilation not available in non-THREADSAFE builds");
return false;
}
bool
js::StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder)
{

View File

@ -18,16 +18,27 @@
#include "jscntxt.h"
#include "jslock.h"
#include "ion/Ion.h"
namespace js {
namespace ion {
class IonBuilder;
}
inline bool
OffThreadCompilationEnabled(JSContext *cx)
{
return ion::js_IonOptions.parallelCompilation
&& cx->runtime->useHelperThreads()
&& cx->runtime->helperThreadCount() != 0;
}
#if defined(JS_THREADSAFE) && defined(JS_ION)
# define JS_PARALLEL_COMPILATION
struct WorkerThread;
struct AsmJSParallelTask;
/* Per-runtime state for off thread work items. */
class WorkerThreadState
@ -42,9 +53,19 @@ class WorkerThreadState
WORKER
};
/* Shared worklist for helper threads. */
/* Shared worklist for Ion worker threads. */
js::Vector<ion::IonBuilder*, 0, SystemAllocPolicy> ionWorklist;
/* Worklist for AsmJS worker threads. */
js::Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> asmJSWorklist;
/*
* Finished list for AsmJS worker threads.
* Simultaneous AsmJS compilations all service the same AsmJS module.
* The main thread must pick up finished optimizations and perform codegen.
*/
js::Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> asmJSFinishedList;
WorkerThreadState() { PodZero(this); }
~WorkerThreadState();
@ -61,8 +82,33 @@ class WorkerThreadState
void notify(CondVar which);
void notifyAll(CondVar which);
bool canStartAsmJSCompile();
bool canStartIonCompile();
uint32_t harvestFailedAsmJSJobs() {
JS_ASSERT(isLocked());
uint32_t n = numAsmJSFailedJobs;
numAsmJSFailedJobs = 0;
return n;
}
void noteAsmJSFailure(int32_t func) {
// Be mindful to signal the main thread after calling this function.
JS_ASSERT(isLocked());
if (asmJSFailedFunctionIndex < 0)
asmJSFailedFunctionIndex = func;
numAsmJSFailedJobs++;
}
bool asmJSWorkerFailed() const {
return bool(numAsmJSFailedJobs);
}
void resetAsmJSFailureState() {
numAsmJSFailedJobs = 0;
asmJSFailedFunctionIndex = -1;
}
int32_t maybeGetAsmJSFailedFunctionIndex() const {
return asmJSFailedFunctionIndex;
}
private:
/*
@ -80,6 +126,18 @@ class WorkerThreadState
/* Condvar to notify helper threads that they may be able to make progress. */
PRCondVar *helperWakeup;
/*
* Number of AsmJS workers that encountered failure for the active module.
* Their parent is logically the main thread, and this number serves for harvesting.
*/
uint32_t numAsmJSFailedJobs;
/*
* Function index |i| in |Module.function(i)| of first failed AsmJS function.
* -1 if no function has failed.
*/
int32_t asmJSFailedFunctionIndex;
};
/* Individual helper thread, one allocated per core. */
@ -96,8 +154,14 @@ struct WorkerThread
/* Any builder currently being compiled by Ion on this thread. */
ion::IonBuilder *ionBuilder;
/* Any AsmJS data currently being optimized by Ion on this thread. */
AsmJSParallelTask *asmData;
void destroy();
void handleAsmJSWorkload(WorkerThreadState &state);
void handleIonWorkload(WorkerThreadState &state);
static void ThreadMain(void *arg);
void threadLoop();
};
@ -106,6 +170,14 @@ struct WorkerThread
/* Methods for interacting with worker threads. */
/* Initialize worker threads unless already initialized. */
bool
EnsureParallelCompilationInitialized(JSRuntime *rt);
/* Perform MIR optimization and LIR generation on a single function. */
bool
StartOffThreadAsmJSCompile(JSContext *cx, AsmJSParallelTask *asmData);
/*
* Schedule an Ion compilation for a script, given a builder which has been
* generated and read everything needed from the VM state.