Bug 1277562 - Part 9: Add Wasm Tier 2 compilation tasks. r=luke

--HG--
extra : rebase_source : 2b95eddcf25d17445b1a377bd3017538b663179c
extra : source : da9e75d2e82c3e3564bb3e37230d384bcf7ffacf
This commit is contained in:
Lars T Hansen 2017-02-09 15:15:17 +01:00
Родитель 8d4d063c52
Коммит 691c2c0203
13 изменённых файлов: 461 добавлений и 86 удалений

Просмотреть файл

@ -66,6 +66,7 @@ enum ThreadType {
THREAD_TYPE_GCPARALLEL, // 7
THREAD_TYPE_PROMISE_TASK, // 8
THREAD_TYPE_ION_FREE, // 9
THREAD_TYPE_WASM_TIER2, // 10
THREAD_TYPE_MAX // Used to check shell function arguments
};

Просмотреть файл

@ -101,6 +101,24 @@ js::StartOffThreadWasmCompile(wasm::CompileTask* task, wasm::CompileMode mode)
return true;
}
bool
js::StartOffThreadWasmTier2Generator(wasm::Tier2GeneratorTask* task)
{
AutoLockHelperThreadState lock;
if (!HelperThreadState().wasmTier2GeneratorWorklist(lock).append(task))
return false;
HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
return true;
}
void
js::CancelOffThreadWasmTier2Generator()
{
// TODO: Implement this
}
bool
js::StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder)
{
@ -880,6 +898,7 @@ GlobalHelperThreadState::GlobalHelperThreadState()
void
GlobalHelperThreadState::finish()
{
CancelOffThreadWasmTier2Generator();
finishThreads();
// Make sure there are no Ion free tasks left. We check this here because,
@ -960,6 +979,7 @@ void
GlobalHelperThreadState::waitForAllThreads()
{
CancelOffThreadIonCompile();
CancelOffThreadWasmTier2Generator();
AutoLockHelperThreadState lock;
while (hasActiveThreads(lock))
@ -1061,6 +1081,12 @@ GlobalHelperThreadState::maxWasmCompilationThreads() const
return cpuCount;
}
size_t
GlobalHelperThreadState::maxWasmTier2GeneratorThreads() const
{
return MaxTier2GeneratorTasks;
}
size_t
GlobalHelperThreadState::maxParseThreads() const
{
@ -1109,14 +1135,46 @@ GlobalHelperThreadState::canStartWasmCompile(const AutoLockHelperThreadState& lo
if (wasmWorklist(lock, mode).empty() || wasmFailed(lock, mode))
return false;
// Honor the maximum allowed threads to compile wasm jobs at once,
// to avoid oversaturating the machine.
if (!checkTaskThreadLimit<wasm::CompileTask*>(maxWasmCompilationThreads()))
// For Tier1 and Once compilation, honor the maximum allowed threads to
// compile wasm jobs at once, to avoid oversaturating the machine.
//
// For Tier2 compilation we need to allow other things to happen too, so for
// now we only allow one thread.
//
// TODO: We should investigate more intelligent strategies, see bug 1380033.
//
// If Tier2 is very backlogged we must give priority to it, since the Tier2
// queue holds onto Tier1 tasks. Indeed if Tier2 is backlogged we will
// devote more resources to Tier2 and not start any Tier1 work at all.
bool tier2oversubscribed = wasmTier2GeneratorWorklist(lock).length() > 20;
size_t threads;
if (mode == wasm::CompileMode::Tier2) {
if (tier2oversubscribed)
threads = maxWasmCompilationThreads();
else
threads = 1;
} else {
if (tier2oversubscribed)
threads = 0;
else
threads = maxWasmCompilationThreads();
}
if (!threads || !checkTaskThreadLimit<wasm::CompileTask*>(threads))
return false;
return true;
}
bool
GlobalHelperThreadState::canStartWasmTier2Generator(const AutoLockHelperThreadState& lock)
{
return !wasmTier2GeneratorWorklist(lock).empty() &&
checkTaskThreadLimit<wasm::Tier2GeneratorTask*>(maxWasmTier2GeneratorThreads());
}
bool
GlobalHelperThreadState::canStartPromiseHelperTask(const AutoLockHelperThreadState& lock)
{
@ -1758,6 +1816,34 @@ HelperThread::handleWasmWorkload(AutoLockHelperThreadState& locked, wasm::Compil
currentTask.reset();
}
void
HelperThread::handleWasmTier2GeneratorWorkload(AutoLockHelperThreadState& locked)
{
MOZ_ASSERT(HelperThreadState().canStartWasmTier2Generator(locked));
MOZ_ASSERT(idle());
currentTask.emplace(HelperThreadState().wasmTier2GeneratorWorklist(locked).popCopy());
bool success = false;
wasm::Tier2GeneratorTask* task = wasmTier2GeneratorTask();
{
AutoUnlockHelperThreadState unlock(locked);
success = wasm::GenerateTier2(task);
}
// We silently ignore failures. Such failures must be resource exhaustion,
// because all error checking was performed by the initial compilation.
mozilla::Unused << success;
// During shutdown the main thread will wait for any ongoing (cancelled)
// tier-2 generation to shut down normally. To do so, it waits on the
// CONSUMER condition for the count of finished generators to rise.
HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
wasm::DeleteTier2GeneratorTask(task);
currentTask.reset();
}
void
HelperThread::handlePromiseHelperTaskWorkload(AutoLockHelperThreadState& locked)
{
@ -2205,6 +2291,8 @@ HelperThread::threadLoop()
} else if (HelperThreadState().canStartWasmCompile(lock, wasm::CompileMode::Tier2)) {
task = js::THREAD_TYPE_WASM;
tier = wasm::CompileMode::Tier2;
} else if (HelperThreadState().canStartWasmTier2Generator(lock)) {
task = js::THREAD_TYPE_WASM_TIER2;
} else {
task = js::THREAD_TYPE_NONE;
}
@ -2241,6 +2329,9 @@ HelperThread::threadLoop()
case js::THREAD_TYPE_ION_FREE:
handleIonFreeWorkload(lock);
break;
case js::THREAD_TYPE_WASM_TIER2:
handleWasmTier2GeneratorWorkload(lock);
break;
default:
MOZ_CRASH("No task to perform");
}

Просмотреть файл

@ -46,7 +46,9 @@ namespace wasm {
class FuncIR;
class FunctionCompileResults;
class CompileTask;
struct Tier2GeneratorTask;
typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
typedef Vector<Tier2GeneratorTask*, 0, SystemAllocPolicy> Tier2GeneratorTaskPtrVector;
} // namespace wasm
enum class ParseTaskKind
@ -64,6 +66,10 @@ class GlobalHelperThreadState
friend class AutoUnlockHelperThreadState;
public:
// A single tier-2 ModuleGenerator job spawns many compilation jobs, and we
// do not want to allow more than one such ModuleGenerator to run at a time.
static const size_t MaxTier2GeneratorTasks = 1;
// Number of CPUs to treat this machine as having when creating threads.
// May be accessed without locking.
size_t cpuCount;
@ -91,6 +97,7 @@ class GlobalHelperThreadState
// wasm worklist and finished jobs.
wasm::CompileTaskPtrVector wasmWorklist_tier1_, wasmFinishedList_tier1_;
wasm::CompileTaskPtrVector wasmWorklist_tier2_, wasmFinishedList_tier2_;
wasm::Tier2GeneratorTaskPtrVector wasmTier2GeneratorWorklist_;
// For now, only allow a single parallel wasm compilation at each tier to
// happen at a time. This avoids race conditions on
@ -147,6 +154,7 @@ class GlobalHelperThreadState
size_t maxIonCompilationThreads() const;
size_t maxUnpausedIonCompilationThreads() const;
size_t maxWasmCompilationThreads() const;
size_t maxWasmTier2GeneratorThreads() const;
size_t maxParseThreads() const;
size_t maxCompressionThreads() const;
size_t maxGCHelperThreads() const;
@ -229,6 +237,10 @@ class GlobalHelperThreadState
}
}
wasm::Tier2GeneratorTaskPtrVector& wasmTier2GeneratorWorklist(const AutoLockHelperThreadState&) {
return wasmTier2GeneratorWorklist_;
}
PromiseHelperTaskVector& promiseHelperTasks(const AutoLockHelperThreadState&) {
return promiseHelperTasks_;
}
@ -264,6 +276,7 @@ class GlobalHelperThreadState
}
bool canStartWasmCompile(const AutoLockHelperThreadState& lock, wasm::CompileMode mode);
bool canStartWasmTier2Generator(const AutoLockHelperThreadState& lock);
bool canStartPromiseHelperTask(const AutoLockHelperThreadState& lock);
bool canStartIonCompile(const AutoLockHelperThreadState& lock);
bool canStartIonFreeTask(const AutoLockHelperThreadState& lock);
@ -440,6 +453,7 @@ HelperThreadState()
typedef mozilla::Variant<jit::IonBuilder*,
wasm::CompileTask*,
wasm::Tier2GeneratorTask*,
PromiseHelperTask*,
ParseTask*,
SourceCompressionTask*,
@ -481,6 +495,10 @@ struct HelperThread
return maybeCurrentTaskAs<wasm::CompileTask*>();
}
wasm::Tier2GeneratorTask* wasmTier2GeneratorTask() {
return maybeCurrentTaskAs<wasm::Tier2GeneratorTask*>();
}
/* Any source being parsed/emitted on this thread. */
ParseTask* parseTask() {
return maybeCurrentTaskAs<ParseTask*>();
@ -516,6 +534,7 @@ struct HelperThread
}
void handleWasmWorkload(AutoLockHelperThreadState& locked, wasm::CompileMode mode);
void handleWasmTier2GeneratorWorkload(AutoLockHelperThreadState& locked);
void handlePromiseHelperTaskWorkload(AutoLockHelperThreadState& locked);
void handleIonWorkload(AutoLockHelperThreadState& locked);
void handleIonFreeWorkload(AutoLockHelperThreadState& locked);
@ -556,12 +575,25 @@ PauseCurrentHelperThread();
bool
StartOffThreadWasmCompile(wasm::CompileTask* task, wasm::CompileMode mode);
// Enqueues a wasm compilation task.
bool
StartOffThreadWasmTier2Generator(wasm::Tier2GeneratorTask* task);
// Cancel all background Wasm Tier-2 compilations.
void
CancelOffThreadWasmTier2Generator();
namespace wasm {
// Performs MIR optimization and LIR generation on one or several functions.
MOZ_MUST_USE bool
CompileFunction(CompileTask* task, UniqueChars* error);
MOZ_MUST_USE bool
GenerateTier2(Tier2GeneratorTask* task);
void
DeleteTier2GeneratorTask(Tier2GeneratorTask* task);
}
/*

Просмотреть файл

@ -43,6 +43,7 @@
_(ProcessExecutableRegion, 500) \
_(WasmCodeProfilingLabels, 500) \
_(OffThreadPromiseState, 500) \
_(WasmTier2GeneratorComplete, 500) \
\
_(TraceLoggerGraphState, 600) \
_(VTuneLock, 600)

Просмотреть файл

@ -1863,8 +1863,8 @@ class MOZ_STACK_CLASS ModuleValidator
return false;
}
CompileArgs args;
if (!args.initFromContext(cx_, Move(scriptedCaller)))
MutableCompileArgs args = cx_->new_<CompileArgs>();
if (!args || !args->initFromContext(cx_, Move(scriptedCaller)))
return false;
auto env = MakeUnique<ModuleEnvironment>(ModuleKind::AsmJS);
@ -1880,7 +1880,7 @@ class MOZ_STACK_CLASS ModuleValidator
env->minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
if (!mg_.init(Move(env), args, CompileMode::Once, asmJSMetadata_.get()))
if (!mg_.init(Move(env), *args, CompileMode::Once, asmJSMetadata_.get()))
return false;
return true;
@ -2402,11 +2402,11 @@ class MOZ_STACK_CLASS ModuleValidator
// asm.js does not have any wasm bytecode to save; view-source is
// provided through the ScriptSource.
SharedBytes bytes = js_new<ShareableBytes>();
SharedBytes bytes = cx_->new_<ShareableBytes>();
if (!bytes)
return nullptr;
return mg_.finish(*bytes);
return mg_.finishModule(*bytes);
}
};

Просмотреть файл

@ -393,6 +393,12 @@ class Metadata : public ShareableBase<Metadata>, public MetadataCacheablePod
const MetadataTier& metadata(Tier t) const;
MetadataTier& metadata(Tier t);
UniquePtr<MetadataTier> takeMetadata(Tier tier) {
MOZ_ASSERT(!hasTier2());
MOZ_ASSERT(metadata1_->tier == tier);
return Move(metadata1_);
}
SigWithIdVector sigIds;
GlobalDescVector globals;
TableDescVector tables;

Просмотреть файл

@ -18,6 +18,9 @@
#include "wasm/WasmCompile.h"
#include "mozilla/Maybe.h"
#include "mozilla/Unused.h"
#include "jsprf.h"
#include "wasm/WasmBaselineCompile.h"
@ -125,6 +128,12 @@ CompilerAvailability(ModuleKind kind, const CompileArgs& args, bool* baselineEna
*ionEnabled = true;
}
static bool
BackgroundWorkPossible()
{
return CanUseExtraThreads() && HelperThreadState().cpuCount > 1;
}
bool
wasm::GetDebugEnabled(const CompileArgs& args, ModuleKind kind)
{
@ -140,7 +149,7 @@ wasm::GetInitialCompileMode(const CompileArgs& args, ModuleKind kind)
bool baselineEnabled, debugEnabled, ionEnabled;
CompilerAvailability(kind, args, &baselineEnabled, &debugEnabled, &ionEnabled);
return (baselineEnabled && ionEnabled && !debugEnabled)
return BackgroundWorkPossible() && baselineEnabled && ionEnabled && !debugEnabled
? CompileMode::Tier1
: CompileMode::Once;
}
@ -168,33 +177,106 @@ wasm::GetTier(const CompileArgs& args, CompileMode compileMode, ModuleKind kind)
}
}
namespace js {
namespace wasm {
struct Tier2GeneratorTask
{
// The module that wants the results of the compilation
SharedModule module;
// The arguments for the compilation
SharedCompileArgs compileArgs;
Tier2GeneratorTask(Module& module, const CompileArgs& compileArgs)
: module(&module),
compileArgs(&compileArgs)
{}
};
}
}
static bool
Compile(ModuleGenerator& mg, const ShareableBytes& bytecode, const CompileArgs& args,
UniqueChars* error, CompileMode compileMode)
{
auto env = js::MakeUnique<ModuleEnvironment>();
if (!env)
return false;
Decoder d(bytecode.bytes, error);
if (!DecodeModuleEnvironment(d, env.get()))
return false;
if (!mg.init(Move(env), args, compileMode))
return false;
if (!DecodeCodeSection(d, mg))
return false;
if (!DecodeModuleTail(d, &mg.mutableEnv()))
return false;
return true;
}
SharedModule
wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error)
{
MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
Decoder d(bytecode.bytes, error);
auto env = js::MakeUnique<ModuleEnvironment>();
if (!env)
return nullptr;
if (!DecodeModuleEnvironment(d, env.get()))
return nullptr;
CompileMode compileMode = GetInitialCompileMode(args);
ModuleGenerator mg(error);
if (!mg.init(Move(env), args, compileMode))
return nullptr;
if (!DecodeCodeSection(d, mg))
return nullptr;
if (!DecodeModuleTail(d, &mg.mutableEnv()))
CompileMode mode = GetInitialCompileMode(args);
if (!::Compile(mg, bytecode, args, error, mode))
return nullptr;
MOZ_ASSERT(!*error, "unreported error in decoding");
return mg.finish(bytecode);
SharedModule module = mg.finishModule(bytecode);
if (!module)
return nullptr;
if (mode == CompileMode::Tier1) {
MOZ_ASSERT(BackgroundWorkPossible());
auto task = js::MakeUnique<Tier2GeneratorTask>(*module, args);
if (!task) {
module->unblockOnTier2GeneratorFinished(CompileMode::Once);
return nullptr;
}
if (!StartOffThreadWasmTier2Generator(&*task)) {
module->unblockOnTier2GeneratorFinished(CompileMode::Once);
return nullptr;
}
mozilla::Unused << task.release();
}
return module;
}
// This runs on a helper thread.
bool
wasm::GenerateTier2(Tier2GeneratorTask* task)
{
UniqueChars error;
ModuleGenerator mg(&error);
bool res =
::Compile(mg, task->module->bytecode(), *task->compileArgs, &error, CompileMode::Tier2) &&
mg.finishTier2(task->module->bytecode(), task->module);
task->module->unblockOnTier2GeneratorFinished(res ? CompileMode::Tier2 : CompileMode::Once);
return res;
}
void
wasm::DeleteTier2GeneratorTask(Tier2GeneratorTask* task)
{
js_delete(task);
}

Просмотреть файл

@ -35,7 +35,7 @@ struct ScriptedCaller
// Describes all the parameters that control wasm compilation.
struct CompileArgs
struct CompileArgs : ShareableBase<CompileArgs>
{
Assumptions assumptions;
ScriptedCaller scriptedCaller;
@ -57,6 +57,9 @@ struct CompileArgs
bool initFromContext(JSContext* cx, ScriptedCaller&& scriptedCaller);
};
typedef RefPtr<CompileArgs> MutableCompileArgs;
typedef RefPtr<const CompileArgs> SharedCompileArgs;
// Compile the given WebAssembly bytecode with the given arguments into a
// wasm::Module. On success, the Module is returned. On failure, the returned
// SharedModule pointer is null and either:
@ -75,10 +78,11 @@ bool
GetDebugEnabled(const CompileArgs& args, ModuleKind kind = ModuleKind::Wasm);
// Select the mode for the initial compilation of a module. The mode is "Tier1"
// precisely if both compilers are available and we're not debugging, and in
// that case, we'll compile twice, with the mode set to "Tier2" during the
// second compilation. Otherwise, the tier is "Once" and we'll compile once,
// with the appropriate compiler.
// precisely if both compilers are available, we're not debugging, and it is
// possible to compile in the background, and in that case, we'll compile twice,
// with the mode set to "Tier2" during the second (background) compilation.
// Otherwise, the tier is "Once" and we'll compile once, with the appropriate
// compiler.
CompileMode
GetInitialCompileMode(const CompileArgs& args, ModuleKind kind = ModuleKind::Wasm);

Просмотреть файл

@ -1133,22 +1133,13 @@ ModuleGenerator::generateBytecodeHash(const ShareableBytes& bytecode)
memcpy(metadata_->hash, hash, sizeof(ModuleHash));
}
SharedModule
ModuleGenerator::finish(const ShareableBytes& bytecode)
bool
ModuleGenerator::finishMetadata(const ShareableBytes& bytecode)
{
MOZ_ASSERT(!activeFuncDef_);
MOZ_ASSERT(finishedFuncDefs_);
if (!finishFuncExports())
return nullptr;
if (!finishCodegen())
return nullptr;
// Convert the CallSiteAndTargetVector (needed during generation) to a
// CallSiteVector (what is stored in the Module).
if (!metadataTier_->callSites.appendAll(masm_.callSites()))
return nullptr;
return false;
// The MacroAssembler has accumulated all the memory accesses during codegen.
metadataTier_->memoryAccesses = masm_.extractMemoryAccesses();
@ -1179,7 +1170,27 @@ ModuleGenerator::finish(const ShareableBytes& bytecode)
// For asm.js, the tables vector is over-allocated (to avoid resize during
// parallel copilation). Shrink it back down to fit.
if (isAsmJS() && !metadata_->tables.resize(numTables_))
return nullptr;
return false;
generateBytecodeHash(bytecode);
return true;
}
bool
ModuleGenerator::finishCommon(const ShareableBytes& bytecode)
{
MOZ_ASSERT(!activeFuncDef_);
MOZ_ASSERT(finishedFuncDefs_);
if (!finishFuncExports())
return false;
if (!finishCodegen())
return false;
if (!finishMetadata(bytecode))
return false;
// Assert CodeRanges are sorted.
#ifdef DEBUG
@ -1200,9 +1211,18 @@ ModuleGenerator::finish(const ShareableBytes& bytecode)
#endif
if (!finishLinkData())
return nullptr;
return false;
generateBytecodeHash(bytecode);
return true;
}
SharedModule
ModuleGenerator::finishModule(const ShareableBytes& bytecode)
{
MOZ_ASSERT(compileMode_ == CompileMode::Once || compileMode_ == CompileMode::Tier1);
if (!finishCommon(bytecode))
return nullptr;
UniqueConstCodeSegment codeSegment = CodeSegment::create(tier_,
masm_,
@ -1227,7 +1247,8 @@ ModuleGenerator::finish(const ShareableBytes& bytecode)
if (!code)
return nullptr;
return SharedModule(js_new<Module>(Move(assumptions_),
return SharedModule(js_new<Module>(compileMode_,
Move(assumptions_),
*code,
Move(maybeDebuggingBytes),
Move(linkData_),
@ -1238,6 +1259,31 @@ ModuleGenerator::finish(const ShareableBytes& bytecode)
bytecode));
}
bool
ModuleGenerator::finishTier2(const ShareableBytes& bytecode, SharedModule module)
{
MOZ_ASSERT(compileMode_ == CompileMode::Tier2);
if (!finishCommon(bytecode))
return false;
UniqueConstCodeSegment codeSegment = CodeSegment::create(tier_,
masm_,
bytecode,
*linkDataTier_,
*metadata_);
if (!codeSegment)
return false;
MOZ_ASSERT(!metadata_->debugEnabled);
module->finishTier2Generator(linkData_.takeLinkData(tier_),
metadata_->takeMetadata(tier_),
Move(codeSegment),
Move(env_));
return true;
}
bool
wasm::CompileFunction(CompileTask* task, UniqueChars* error)
{

Просмотреть файл

@ -201,6 +201,8 @@ class CompileTask
}
};
struct Tier2GeneratorTask;
// A ModuleGenerator encapsulates the creation of a wasm module. During the
// lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created
// and destroyed to compile the individual function bodies. After generating all
@ -268,6 +270,8 @@ class MOZ_STACK_CLASS ModuleGenerator
MOZ_MUST_USE bool finishCodegen();
MOZ_MUST_USE bool finishLinkData();
void generateBytecodeHash(const ShareableBytes& bytecode);
MOZ_MUST_USE bool finishMetadata(const ShareableBytes& bytecode);
MOZ_MUST_USE bool finishCommon(const ShareableBytes& bytecode);
MOZ_MUST_USE bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global);
@ -328,7 +332,11 @@ class MOZ_STACK_CLASS ModuleGenerator
MOZ_MUST_USE bool addExport(CacheableChars&& fieldChars, uint32_t funcIndex);
// Finish compilation of the given bytecode.
SharedModule finish(const ShareableBytes& bytecode);
SharedModule finishModule(const ShareableBytes& bytecode);
// Finish compilation of the given bytecode, installing tier-variant parts
// for Tier 2 into module.
MOZ_MUST_USE bool finishTier2(const ShareableBytes& bytecode, SharedModule module);
};
// A FunctionGenerator encapsulates the generation of a single function body.

Просмотреть файл

@ -385,12 +385,12 @@ wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj
if (!DescribeScriptedCaller(cx, &scriptedCaller))
return false;
CompileArgs compileArgs;
if (!compileArgs.initFromContext(cx, Move(scriptedCaller)))
MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
if (!compileArgs || !compileArgs->initFromContext(cx, Move(scriptedCaller)))
return false;
UniqueChars error;
SharedModule module = Compile(*bytecode, compileArgs, &error);
SharedModule module = Compile(*bytecode, *compileArgs, &error);
if (!module) {
if (error) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR,
@ -887,12 +887,12 @@ WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp)
if (!GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG, &bytecode))
return false;
CompileArgs compileArgs;
if (!InitCompileArgs(cx, &compileArgs))
MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
if (!compileArgs || !InitCompileArgs(cx, compileArgs.get()))
return false;
UniqueChars error;
SharedModule module = Compile(*bytecode, compileArgs, &error);
SharedModule module = Compile(*bytecode, *compileArgs, &error);
if (!module) {
if (error) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR,
@ -1899,7 +1899,8 @@ Reject(JSContext* cx, const CompileArgs& args, UniqueChars error, Handle<Promise
}
static bool
ResolveCompilation(JSContext* cx, Module& module, Handle<PromiseObject*> promise)
ResolveCompilation(JSContext* cx, Module& module, const CompileArgs& compileArgs,
Handle<PromiseObject*> promise)
{
RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
@ -1912,23 +1913,23 @@ ResolveCompilation(JSContext* cx, Module& module, Handle<PromiseObject*> promise
struct CompilePromiseTask : PromiseHelperTask
{
MutableBytes bytecode;
CompileArgs compileArgs;
UniqueChars error;
SharedModule module;
MutableBytes bytecode;
SharedCompileArgs compileArgs;
UniqueChars error;
SharedModule module;
CompilePromiseTask(JSContext* cx, Handle<PromiseObject*> promise)
: PromiseHelperTask(cx, promise)
{}
void execute() override {
module = Compile(*bytecode, compileArgs, &error);
module = Compile(*bytecode, *compileArgs, &error);
}
bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
return module
? ResolveCompilation(cx, *module, promise)
: Reject(cx, compileArgs, Move(error), promise);
? ResolveCompilation(cx, *module, *compileArgs, promise)
: Reject(cx, *compileArgs, Move(error), promise);
}
};
@ -1998,8 +1999,10 @@ WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp)
if (!GetBufferSource(cx, callArgs, "WebAssembly.compile", &task->bytecode))
return RejectWithPendingException(cx, promise, callArgs);
if (!InitCompileArgs(cx, &task->compileArgs))
MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
if (!compileArgs || !InitCompileArgs(cx, compileArgs))
return false;
task->compileArgs = compileArgs;
if (!StartOffThreadPromiseHelperTask(cx, Move(task)))
return false;
@ -2009,8 +2012,8 @@ WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp)
}
static bool
ResolveInstantiation(JSContext* cx, Module& module, HandleObject importObj,
Handle<PromiseObject*> promise)
ResolveInstantiation(JSContext* cx, Module& module, const CompileArgs& compileArgs,
HandleObject importObj, Handle<PromiseObject*> promise)
{
RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
@ -2040,24 +2043,26 @@ ResolveInstantiation(JSContext* cx, Module& module, HandleObject importObj,
struct InstantiatePromiseTask : PromiseHelperTask
{
MutableBytes bytecode;
CompileArgs compileArgs;
SharedCompileArgs compileArgs;
UniqueChars error;
SharedModule module;
PersistentRootedObject importObj;
InstantiatePromiseTask(JSContext* cx, Handle<PromiseObject*> promise, HandleObject importObj)
InstantiatePromiseTask(JSContext* cx, Handle<PromiseObject*> promise,
const CompileArgs& compileArgs, HandleObject importObj)
: PromiseHelperTask(cx, promise),
compileArgs(&compileArgs),
importObj(cx, importObj)
{}
void execute() override {
module = Compile(*bytecode, compileArgs, &error);
module = Compile(*bytecode, *compileArgs, &error);
}
bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
return module
? ResolveInstantiation(cx, *module, importObj, promise)
: Reject(cx, compileArgs, Move(error), promise);
? ResolveInstantiation(cx, *module, *compileArgs, importObj, promise)
: Reject(cx, *compileArgs, Move(error), promise);
}
};
@ -2105,16 +2110,17 @@ WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp)
if (!PromiseObject::resolve(cx, promise, resolutionValue))
return false;
} else {
auto task = cx->make_unique<InstantiatePromiseTask>(cx, promise, importObj);
MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
if (!compileArgs || !InitCompileArgs(cx, compileArgs.get()))
return false;
auto task = cx->make_unique<InstantiatePromiseTask>(cx, promise, *compileArgs, importObj);
if (!task || !task->init(cx))
return false;
if (!GetBufferSource(cx, firstArg, JSMSG_WASM_BAD_BUF_MOD_ARG, &task->bytecode))
return RejectWithPendingException(cx, promise, callArgs);
if (!InitCompileArgs(cx, &task->compileArgs))
return false;
if (!StartOffThreadPromiseHelperTask(cx, Move(task)))
return false;
}

Просмотреть файл

@ -21,6 +21,7 @@
#include "jsnspr.h"
#include "jit/JitOptions.h"
#include "threading/LockGuard.h"
#include "wasm/WasmCompile.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmJS.h"
@ -262,6 +263,10 @@ Module::compiledSerializedSize() const
if (metadata().debugEnabled)
return 0;
blockOnIonCompileFinished();
if (!code_->hasTier(Tier::Serialized))
return 0;
return assumptions_.serializedSize() +
linkData_.serializedSize() +
SerializedVectorSize(imports_) +
@ -279,9 +284,11 @@ Module::compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const
return;
}
// Assumption must be serialized at the beginning of the compiled bytes so
// that compiledAssumptionsMatch can detect a build-id mismatch before any
// other decoding occurs.
blockOnIonCompileFinished();
if (!code_->hasTier(Tier::Serialized)) {
MOZ_RELEASE_ASSERT(compiledSize == 0);
return;
}
uint8_t* cursor = compiledBegin;
cursor = assumptions_.serialize(cursor);
@ -294,6 +301,40 @@ Module::compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const
MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
}
void
Module::finishTier2Generator(UniqueLinkDataTier linkData2, UniqueMetadataTier metadata2,
UniqueConstCodeSegment code2, UniqueModuleEnvironment env2)
{
// Install the data in the data structures. They will not be visible yet.
metadata().setTier2(Move(metadata2));
linkData().setTier2(Move(linkData2));
code().setTier2(Move(code2));
for (uint32_t i = 0; i < elemSegments_.length(); i++)
elemSegments_[i].setTier2(Move(env2->elemSegments[i].elemCodeRangeIndices(Tier::Ion)));
// Set the flag atomically to make the tier 2 data visible everywhere at
// once.
metadata().commitTier2();
}
void
Module::blockOnIonCompileFinished() const
{
LockGuard<Mutex> l(tier2Lock_);
while (mode_ == CompileMode::Tier1 && !metadata().hasTier2())
tier2Cond_.wait(l);
}
void
Module::unblockOnTier2GeneratorFinished(CompileMode newMode) const
{
LockGuard<Mutex> l(tier2Lock_);
mode_ = newMode;
tier2Cond_.notify_all();
}
/* static */ bool
Module::assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin, size_t remain)
{
@ -367,9 +408,10 @@ Module::deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize,
MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
MOZ_RELEASE_ASSERT(!!maybeMetadata == code->metadata().isAsmJS());
return js_new<Module>(Move(assumptions),
return js_new<Module>(CompileMode::Tier2, // Serialized code is always Tier 2
Move(assumptions),
*code,
nullptr, // Serialized code is never debuggable
nullptr, // Serialized code is never debuggable
Move(linkData),
Move(imports),
Move(exports),
@ -463,10 +505,12 @@ wasm::DeserializeModule(PRFileDesc* bytecodeFile, PRFileDesc* maybeCompiledFile,
scriptedCaller.line = line;
scriptedCaller.column = column;
CompileArgs args(Assumptions(Move(buildId)), Move(scriptedCaller));
SharedCompileArgs args = js_new<CompileArgs>(Assumptions(Move(buildId)), Move(scriptedCaller));
if (!args)
return nullptr;
UniqueChars error;
return Compile(*bytecode, Move(args), &error);
return Compile(*bytecode, *args, &error);
}
/* virtual */ void

Просмотреть файл

@ -20,13 +20,18 @@
#define wasm_module_h
#include "js/TypeDecls.h"
#include "threading/ConditionVariable.h"
#include "threading/Mutex.h"
#include "vm/MutexIDs.h"
#include "wasm/WasmCode.h"
#include "wasm/WasmTable.h"
#include "wasm/WasmValidate.h"
namespace js {
namespace wasm {
struct CompileArgs;
// LinkData contains all the metadata necessary to patch all the locations
// that depend on the absolute address of a CodeSegment.
//
@ -95,6 +100,12 @@ class LinkData
const LinkDataTier& linkData(Tier tier) const;
LinkDataTier& linkData(Tier tier);
UniquePtr<LinkDataTier> takeLinkData(Tier tier) {
MOZ_ASSERT(!hasTier2());
MOZ_ASSERT(linkData1_->tier == tier);
return Move(linkData1_);
}
WASM_DECLARE_SERIALIZABLE(LinkData)
};
@ -130,6 +141,20 @@ class Module : public JS::WasmModule
mutable mozilla::Atomic<bool> codeIsBusy_;
// The lock guards the mode_ member, and the lock/cond pair are used to
// allow threads to wait for the availability of Ion code and signal the
// completion of tier-2 compilation; see blockOnIonCompileFinished and
// unblockOnTier2GeneratorFinished, below.
mutable Mutex tier2Lock_;
mutable ConditionVariable tier2Cond_;
// Access mode_ only under the lock. It will be changed from Tier1 to Tier2
// once Tier2 compilation is finished, and from Tier1 to Once if Tier2
// compilation is disabled (in testing modes) or cancelled.
mutable CompileMode mode_;
bool instantiateFunctions(JSContext* cx, Handle<FunctionVector> funcImports) const;
bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
bool instantiateTable(JSContext* cx,
@ -142,7 +167,8 @@ class Module : public JS::WasmModule
const ValVector& globalImports) const;
public:
Module(Assumptions&& assumptions,
Module(CompileMode mode,
Assumptions&& assumptions,
const Code& code,
UniqueConstBytes unlinkedCodeForDebugging,
LinkData&& linkData,
@ -160,18 +186,23 @@ class Module : public JS::WasmModule
dataSegments_(Move(dataSegments)),
elemSegments_(Move(elemSegments)),
bytecode_(&bytecode),
codeIsBusy_(false)
codeIsBusy_(false),
tier2Lock_(js::mutexid::WasmTier2GeneratorComplete),
mode_(mode)
{
MOZ_ASSERT_IF(metadata().debugEnabled, unlinkedCodeForDebugging_);
}
~Module() override { /* Note: can be called on any thread */ }
const Code& code() const { return *code_; }
const CodeSegment& codeSegment(Tier t) const { return code_->segment(t); }
const Metadata& metadata() const { return code_->metadata(); }
const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
const LinkData& linkData() const { return linkData_; }
const LinkDataTier& linkData(Tier t) const { return linkData_.linkData(t); }
const ImportVector& imports() const { return imports_; }
const ExportVector& exports() const { return exports_; }
const Bytes& bytecode() const { return bytecode_->bytes; }
const ShareableBytes& bytecode() const { return *bytecode_; }
uint32_t codeLength(Tier t) const { return code_->segment(t).length(); }
// Instantiate this module with the given imports:
@ -184,6 +215,29 @@ class Module : public JS::WasmModule
HandleObject instanceProto,
MutableHandleWasmInstanceObject instanceObj) const;
// Tiered compilation support: these run on the compilation thread.
// Will be invoked once the second-tier compilation of the module is
// finished; it is passed the tier-variant data for Tier 2, which it
// installs in the module and makes visible.
void finishTier2Generator(UniqueLinkDataTier linkData2, UniqueMetadataTier metadata2,
UniqueConstCodeSegment code2, UniqueModuleEnvironment env2);
// Wait until Ion-compiled code is available, which will be true either
// immediately (first-level compile was Ion and is already done), not at all
// (first-level compile was Baseline and there's not a second level), or
// later (ongoing second-level compilation). Once this returns, one can use
// code().hasTier() to check code availability - there is no guarantee that
// Ion code will be available, but if it isn't then it never will.
void blockOnIonCompileFinished() const;
// Signal all waiters that are waiting on tier-2 compilation to be done that
// they should wake up. They will be waiting in blockOnIonCompileFinished.
void unblockOnTier2GeneratorFinished(CompileMode newMode) const;
// Structured clone support:
size_t bytecodeSerializedSize() const override;