From 562b53c8b46f75dde836316e80722ff265e68cb2 Mon Sep 17 00:00:00 2001 From: win7-7 Date: Sun, 28 Dec 2025 16:48:39 +0200 Subject: [PATCH] 1317319 wasm: Factor out section decoding, Part 2 1317319: Factor out Table section decoding. 1317319: Factor out Global section decoding. 1317319: Factor out Export section decoding. 1317319: Factor out Start section decoding. 1317319: Factor out Elem section decoding. 1317319: Move sections decoding around to follow their order in the binary. 1317319: Rename ModuleGeneratorData to ModuleEnvironment. 1317319: Embed most section decoding under DecodeModuleEnvironment. 1317319: Account imported globals when checking against the maximum number of globals. 1317319: Fix BinaryToAST function validation. Fix error losing PrevEncodingVersion when refactoring for 1317319 part 2. 1317319: Tweak AstDecodeFunctionBody; --- js/src/wasm/AsmJS.cpp | 28 +- js/src/wasm/WasmBaselineCompile.cpp | 62 +- js/src/wasm/WasmBinaryFormat.cpp | 748 +++++++++++++++++------- js/src/wasm/WasmBinaryFormat.h | 106 ++-- js/src/wasm/WasmBinaryToAST.cpp | 860 ++++++++++++---------------- js/src/wasm/WasmCompile.cpp | 395 +------------ js/src/wasm/WasmGenerator.cpp | 344 +++++------ js/src/wasm/WasmGenerator.h | 253 +++++--- js/src/wasm/WasmIonCompile.cpp | 67 ++- js/src/wasm/WasmModule.cpp | 126 ---- js/src/wasm/WasmModule.h | 53 -- js/src/wasm/WasmTypes.cpp | 123 ++++ js/src/wasm/WasmTypes.h | 53 ++ 13 files changed, 1576 insertions(+), 1642 deletions(-) diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index fc0584c327..195bf89be9 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -39,6 +39,7 @@ #include "vm/Time.h" #include "vm/TypedArrayObject.h" #include "wasm/WasmBinaryFormat.h" +#include "wasm/WasmCompile.h" #include "wasm/WasmGenerator.h" #include "wasm/WasmInstance.h" #include "wasm/WasmJS.h" @@ -1464,7 +1465,6 @@ class MOZ_STACK_CLASS ModuleValidator importMap_(cx), arrayViews_(cx), atomicsPresent_(false), - mg_(ImportVector()), errorString_(nullptr), errorOffset_(UINT32_MAX), errorOverRecursed_(false) @@ -1562,20 +1562,20 @@ class MOZ_STACK_CLASS ModuleValidator if (!args.initFromContext(cx_, Move(scriptedCaller))) return false; - auto genData = MakeUnique(ModuleKind::AsmJS); - if (!genData || - !genData->sigs.resize(MaxSigs) || - !genData->funcSigs.resize(MaxFuncs) || - !genData->funcImportGlobalDataOffsets.resize(AsmJSMaxImports) || - !genData->tables.resize(MaxTables) || - !genData->asmJSSigToTableIndex.resize(MaxSigs)) + auto env = MakeUnique(ModuleKind::AsmJS); + if (!env || + !env->sigs.resize(MaxSigs) || + !env->funcSigs.resize(MaxFuncs) || + !env->funcImportGlobalDataOffsets.resize(AsmJSMaxImports) || + !env->tables.resize(MaxTables) || + !env->asmJSSigToTableIndex.resize(MaxSigs)) { return false; } - genData->minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0); + env->minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0); - if (!mg_.init(Move(genData), args, asmJSMetadata_.get())) + if (!mg_.init(Move(env), args, asmJSMetadata_.get())) return false; return true; @@ -1823,8 +1823,8 @@ class MOZ_STACK_CLASS ModuleValidator return false; // Declare which function is exported which gives us an index into the - // module FuncExportVector. - if (!mg_.addFuncExport(Move(fieldChars), func.index())) + // module ExportVector. + if (!mg_.addExport(Move(fieldChars), func.index())) return false; // The exported function might have already been exported in which case @@ -2053,7 +2053,9 @@ class MOZ_STACK_CLASS ModuleValidator if (!bytes) return nullptr; - return mg_.finish(*bytes); + return mg_.finish(*bytes, + DataSegmentVector(), + NameInBytecodeVector()); } }; diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index 643757cd26..a431bf68de 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -511,7 +511,13 @@ class BaseCompiler virtual void generate(MacroAssembler& masm) = 0; }; - const ModuleGeneratorData& mg_; + enum class LatentOp { + None, + Compare, + Eqz + }; + + const ModuleEnvironment& env_; BaseOpIter iter_; const FuncBytes& func_; size_t lastReadCallSite_; @@ -584,7 +590,7 @@ class BaseCompiler // More members: see the stk_ and ctl_ vectors, defined below. public: - BaseCompiler(const ModuleGeneratorData& mg, + BaseCompiler(const ModuleEnvironment& env, Decoder& decoder, const FuncBytes& func, const ValTypeVector& locals, @@ -1992,8 +1998,8 @@ class BaseCompiler void beginFunction() { JitSpew(JitSpew_Codegen, "# Emitting wasm baseline code"); - SigIdDesc sigId = mg_.funcSigs[func_.index()]->id; - GenerateFunctionPrologue(masm, localSize_, sigId, &compileResults_.offsets()); + SigIdDesc sigId = env_.funcSigs[func_.index()]->id; + GenerateFunctionPrologue(masm, localSize_, sigId, &offsets_); MOZ_ASSERT(masm.framePushed() == uint32_t(localSize_)); @@ -2346,12 +2352,12 @@ class BaseCompiler { loadI32(WasmTableCallIndexReg, indexVal); - const SigWithId& sig = mg_.sigs[sigIndex]; + const SigWithId& sig = env_.sigs[sigIndex]; CalleeDesc callee; if (isCompilingAsmJS()) { MOZ_ASSERT(sig.id.kind() == SigIdDesc::Kind::None); - const TableDesc& table = mg_.tables[mg_.asmJSSigToTableIndex[sigIndex]]; + const TableDesc& table = env_.tables[env_.asmJSSigToTableIndex[sigIndex]]; MOZ_ASSERT(IsPowerOfTwo(table.limits.initial)); masm.andPtr(Imm32((table.limits.initial - 1)), WasmTableCallIndexReg); @@ -2359,8 +2365,8 @@ class BaseCompiler callee = CalleeDesc::asmJSTable(table); } else { MOZ_ASSERT(sig.id.kind() != SigIdDesc::Kind::None); - MOZ_ASSERT(mg_.tables.length() == 1); - const TableDesc& table = mg_.tables[0]; + MOZ_ASSERT(env_.tables.length() == 1); + const TableDesc& table = env_.tables[0]; callee = CalleeDesc::wasmTable(table, sig.id); } @@ -4014,7 +4020,7 @@ class BaseCompiler } bool isCompilingAsmJS() const { - return mg_.kind == ModuleKind::AsmJS; + return env_.kind == ModuleKind::AsmJS; } TrapOffset trapOffset() const { @@ -6022,8 +6028,8 @@ BaseCompiler::emitCall() sync(); - const Sig& sig = *mg_.funcSigs[funcIndex]; - bool import = mg_.funcIsImport(funcIndex); + const Sig& sig = *env_.funcSigs[funcIndex]; + bool import = env_.funcIsImport(funcIndex); uint32_t numArgs = sig.args().length(); size_t stackSpace = stackConsumed(numArgs); @@ -6038,7 +6044,7 @@ BaseCompiler::emitCall() return false; if (import) - callImport(mg_.funcImportGlobalDataOffsets[funcIndex], baselineCall); + callImport(env_.funcImportGlobalDataOffsets[funcIndex], baselineCall); else callDefinition(funcIndex, baselineCall); @@ -6076,7 +6082,7 @@ BaseCompiler::emitCallIndirect(bool oldStyle) sync(); - const SigWithId& sig = mg_.sigs[sigIndex]; + const SigWithId& sig = env_.sigs[sigIndex]; // new style: Stack: ... arg1 .. argn callee // old style: Stack: ... callee arg1 .. argn @@ -6459,13 +6465,13 @@ bool BaseCompiler::emitGetGlobal() { uint32_t id; - if (!iter_.readGetGlobal(mg_.globals, &id)) + if (!iter_.readGetGlobal(env_.globals, &id)) return false; if (deadCode_) return true; - const GlobalDesc& global = mg_.globals[id]; + const GlobalDesc& global = env_.globals[id]; if (global.isConstant()) { Val value = global.constantValue(); @@ -6531,7 +6537,7 @@ BaseCompiler::emitSetGlobal() if (deadCode_) return true; - const GlobalDesc& global = mg_.globals[id]; + const GlobalDesc& global = env_.globals[id]; switch (global.type()) { case ValType::I32: { @@ -6565,12 +6571,23 @@ BaseCompiler::emitSetGlobal() return true; } +bool +BaseCompiler::emitSetGlobal() +{ + uint32_t id; + Nothing unused_value; + if (!iter_.readSetGlobal(env_.globals, &id, &unused_value)) + return false; + + return emitSetOrTeeGlobal(id); +} + bool BaseCompiler::emitTeeGlobal() { uint32_t id; Nothing unused_value; - if (!iter_.readTeeGlobal(mg_.globals, &id, &unused_value)) + if (!iter_.readTeeGlobal(env_.globals, &id, &unused_value)) return false; if (deadCode_) @@ -8031,12 +8048,13 @@ BaseCompiler::emitFunction() return true; } -BaseCompiler::BaseCompiler(const ModuleGeneratorData& mg, +BaseCompiler::BaseCompiler(const ModuleEnvironment& env, Decoder& decoder, const FuncBytes& func, const ValTypeVector& locals, - FuncCompileResults& compileResults) - : mg_(mg), + TempAllocator* alloc, + MacroAssembler* masm) + : env_(env), iter_(decoder, func.lineOrBytecode()), func_(func), lastReadCallSite_(0), @@ -8262,7 +8280,7 @@ js::wasm::BaselineCompileFunction(IonCompileTask* task) ValTypeVector locals; if (!locals.appendAll(func.sig().args())) return false; - if (!DecodeLocalEntries(d, task->mg().kind, &locals)) + if (!DecodeLocalEntries(d, task->env().kind, &locals)) return false; // The MacroAssembler will sometimes access the jitContext. @@ -8271,7 +8289,7 @@ js::wasm::BaselineCompileFunction(IonCompileTask* task) // One-pass baseline compilation. - BaseCompiler f(task->mg(), d, func, locals, results); + BaseCompiler f(task->env(), d, func, locals, &task->alloc(), &task->masm()); if (!f.init()) return false; diff --git a/js/src/wasm/WasmBinaryFormat.cpp b/js/src/wasm/WasmBinaryFormat.cpp index 92b634c583..34dc95eb03 100644 --- a/js/src/wasm/WasmBinaryFormat.cpp +++ b/js/src/wasm/WasmBinaryFormat.cpp @@ -28,16 +28,67 @@ using namespace js::wasm; using mozilla::CheckedInt; -bool -wasm::DecodePreamble(Decoder& d) -{ - uint32_t u32; - if (!d.readFixedU32(&u32) || u32 != MagicNumber) - return d.fail("failed to match magic number"); +// Decoder implementation. - if (!d.readFixedU32(&u32) || (u32 != EncodingVersion && u32 != PrevEncodingVersion)) { - return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32, - u32, EncodingVersion); +bool +Decoder::fail(const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + UniqueChars str(JS_vsmprintf(msg, ap)); + va_end(ap); + if (!str) + return false; + + return fail(Move(str)); +} + +bool +Decoder::fail(UniqueChars msg) +{ + MOZ_ASSERT(error_); + UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s", currentOffset(), msg.get())); + if (!strWithOffset) + return false; + + *error_ = Move(strWithOffset); + return false; +} + +// Misc helpers. + +bool +wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals) +{ + uint32_t numLocalEntries = 0; + ValType prev = ValType(TypeCode::Limit); + for (ValType t : locals) { + if (t != prev) { + numLocalEntries++; + prev = t; + } + } + + if (!e.writeVarU32(numLocalEntries)) + return false; + + if (numLocalEntries) { + prev = locals[0]; + uint32_t count = 1; + for (uint32_t i = 1; i < locals.length(); i++, count++) { + if (prev != locals[i]) { + if (!e.writeVarU32(count)) + return false; + if (!e.writeValType(prev)) + return false; + prev = locals[i]; + count = 0; + } + } + if (!e.writeVarU32(count)) + return false; + if (!e.writeValType(prev)) + return false; } return true; @@ -64,7 +115,49 @@ DecodeValType(Decoder& d, ModuleKind kind, ValType* type) } bool -wasm::DecodeTypeSection(Decoder& d, SigWithIdVector* sigs) +wasm::DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals) +{ + uint32_t numLocalEntries; + if (!d.readVarU32(&numLocalEntries)) + return d.fail("failed to read number of local entries"); + + for (uint32_t i = 0; i < numLocalEntries; i++) { + uint32_t count; + if (!d.readVarU32(&count)) + return d.fail("failed to read local entry count"); + + if (MaxLocals - locals->length() < count) + return d.fail("too many locals"); + + ValType type; + if (!DecodeValType(d, kind, &type)) + return false; + + if (!locals->appendN(type, count)) + return false; + } + + return true; +} + +// Section macros. + +static bool +DecodePreamble(Decoder& d) +{ + uint32_t u32; + if (!d.readFixedU32(&u32) || u32 != MagicNumber) + return d.fail("failed to match magic number"); + + if (!d.readFixedU32(&u32) || (u32 != EncodingVersion && u32 != PrevEncodingVersion)) { + return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32, + u32, EncodingVersion); + } + return true; +} + +static bool +DecodeTypeSection(Decoder& d, SigWithIdVector* sigs) { uint32_t sectionStart, sectionSize; if (!d.startSection(SectionId::Type, §ionStart, §ionSize, "type")) @@ -129,8 +222,8 @@ wasm::DecodeTypeSection(Decoder& d, SigWithIdVector* sigs) return true; } -UniqueChars -wasm::DecodeName(Decoder& d) +static UniqueChars +DecodeName(Decoder& d) { uint32_t numBytes; if (!d.readVarU32(&numBytes)) @@ -162,8 +255,38 @@ DecodeSignatureIndex(Decoder& d, const SigWithIdVector& sigs, uint32_t* sigIndex return true; } -bool -wasm::DecodeTableLimits(Decoder& d, TableDescVector* tables) +static bool +DecodeLimits(Decoder& d, Limits* limits) +{ + uint32_t flags; + if (!d.readVarU32(&flags)) + return d.fail("expected flags"); + + if (flags & ~uint32_t(0x1)) + return d.fail("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x1))); + + if (!d.readVarU32(&limits->initial)) + return d.fail("expected initial length"); + + if (flags & 0x1) { + uint32_t maximum; + if (!d.readVarU32(&maximum)) + return d.fail("expected maximum length"); + + if (limits->initial > maximum) { + return d.fail("memory size minimum must not be greater than maximum; " + "maximum length %" PRIu32 " is less than initial length %" PRIu32, + maximum, limits->initial); + } + + limits->maximum.emplace(maximum); + } + + return true; +} + +static bool +DecodeTableLimits(Decoder& d, TableDescVector* tables) { uint32_t elementType; if (!d.readVarU32(&elementType)) @@ -176,14 +299,17 @@ wasm::DecodeTableLimits(Decoder& d, TableDescVector* tables) if (!DecodeLimits(d, &limits)) return false; + if (limits.initial > MaxTableElems) + return d.fail("too many table elements"); + if (tables->length()) return d.fail("already have default table"); return tables->emplaceBack(TableKind::AnyFunction, limits); } -bool -wasm::GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable) +static bool +GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable) { switch (type) { case ValType::I32: @@ -205,9 +331,56 @@ wasm::GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable) } static bool -DecodeImport(Decoder& d, const SigWithIdVector& sigs, Uint32Vector* funcSigIndices, - GlobalDescVector* globals, TableDescVector* tables, Maybe* memory, - ImportVector* imports) +DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable) +{ + if (!DecodeValType(d, ModuleKind::Wasm, type)) + return false; + + uint32_t flags; + if (!d.readVarU32(&flags)) + return d.fail("expected global flags"); + + if (flags & ~uint32_t(GlobalTypeImmediate::AllowedMask)) + return d.fail("unexpected bits set in global flags"); + + *isMutable = flags & uint32_t(GlobalTypeImmediate::IsMutable); + return true; +} + +static bool +DecodeMemoryLimits(Decoder& d, ModuleEnvironment* env) +{ + if (env->usesMemory()) + return d.fail("already have default memory"); + + Limits memory; + if (!DecodeLimits(d, &memory)) + return false; + + CheckedInt initialBytes = memory.initial; + initialBytes *= PageSize; + if (!initialBytes.isValid() || initialBytes.value() > uint32_t(INT32_MAX)) + return d.fail("initial memory size too big"); + + memory.initial = initialBytes.value(); + + if (memory.maximum) { + CheckedInt maximumBytes = *memory.maximum; + maximumBytes *= PageSize; + if (!maximumBytes.isValid()) + return d.fail("maximum memory size too big"); + + memory.maximum = Some(maximumBytes.value()); + } + + env->memoryUsage = MemoryUsage::Unshared; + env->minMemoryLength = memory.initial; + env->maxMemoryLength = memory.maximum; + return true; +} + +static bool +DecodeImport(Decoder& d, ModuleEnvironment* env) { UniqueChars moduleName = DecodeName(d); if (!moduleName) @@ -226,22 +399,21 @@ DecodeImport(Decoder& d, const SigWithIdVector& sigs, Uint32Vector* funcSigIndic switch (importKind) { case DefinitionKind::Function: { uint32_t sigIndex; - if (!DecodeSignatureIndex(d, sigs, &sigIndex)) + if (!DecodeSignatureIndex(d, env->sigs, &sigIndex)) return false; - if (!funcSigIndices->append(sigIndex)) + if (!env->funcSigs.append(&env->sigs[sigIndex])) return false; break; } case DefinitionKind::Table: { - if (!DecodeTableLimits(d, tables)) + if (!DecodeTableLimits(d, &env->tables)) return false; + env->tables.back().external = true; break; } case DefinitionKind::Memory: { - Limits limits; - if (!DecodeMemoryLimits(d, !!*memory, &limits)) + if (!DecodeMemoryLimits(d, env)) return false; - memory->emplace(limits); break; } case DefinitionKind::Global: { @@ -251,7 +423,7 @@ DecodeImport(Decoder& d, const SigWithIdVector& sigs, Uint32Vector* funcSigIndic return false; if (!GlobalIsJSCompatible(d, type, isMutable)) return false; - if (!globals->append(GlobalDesc(type, isMutable, globals->length()))) + if (!env->globals.append(GlobalDesc(type, isMutable, env->globals.length()))) return false; break; } @@ -259,13 +431,11 @@ DecodeImport(Decoder& d, const SigWithIdVector& sigs, Uint32Vector* funcSigIndic return d.fail("unsupported import kind"); } - return imports->emplaceBack(Move(moduleName), Move(funcName), importKind); + return env->imports.emplaceBack(Move(moduleName), Move(funcName), importKind); } -bool -wasm::DecodeImportSection(Decoder& d, const SigWithIdVector& sigs, Uint32Vector* funcSigIndices, - GlobalDescVector* globals, TableDescVector* tables, Maybe* memory, - ImportVector* imports) +static bool +DecodeImportSection(Decoder& d, ModuleEnvironment* env) { uint32_t sectionStart, sectionSize; if (!d.startSection(SectionId::Import, §ionStart, §ionSize, "import")) @@ -281,19 +451,22 @@ wasm::DecodeImportSection(Decoder& d, const SigWithIdVector& sigs, Uint32Vector* return d.fail("too many imports"); for (uint32_t i = 0; i < numImports; i++) { - if (!DecodeImport(d, sigs, funcSigIndices, globals, tables, memory, imports)) + if (!DecodeImport(d, env)) return false; } if (!d.finishSection(sectionStart, sectionSize, "import")) return false; + // The global data offsets will be filled in by ModuleGenerator::init. + if (!env->funcImportGlobalDataOffsets.resize(env->funcSigs.length())) + return false; + return true; } -bool -wasm::DecodeFunctionSection(Decoder& d, const SigWithIdVector& sigs, size_t numImportedFunc, - Uint32Vector* funcSigIndexes) +static bool +DecodeFunctionSection(Decoder& d, ModuleEnvironment* env) { uint32_t sectionStart, sectionSize; if (!d.startSection(SectionId::Function, §ionStart, §ionSize, "function")) @@ -305,19 +478,19 @@ wasm::DecodeFunctionSection(Decoder& d, const SigWithIdVector& sigs, size_t numI if (!d.readVarU32(&numDefs)) return d.fail("expected number of function definitions"); - CheckedInt numFuncs = numImportedFunc; + CheckedInt numFuncs = env->funcSigs.length(); numFuncs += numDefs; if (!numFuncs.isValid() || numFuncs.value() > MaxFuncs) return d.fail("too many functions"); - if (!funcSigIndexes->reserve(numDefs)) + if (!env->funcSigs.reserve(numFuncs.value())) return false; for (uint32_t i = 0; i < numDefs; i++) { uint32_t sigIndex; - if (!DecodeSignatureIndex(d, sigs, &sigIndex)) + if (!DecodeSignatureIndex(d, env->sigs, &sigIndex)) return false; - funcSigIndexes->infallibleAppend(sigIndex); + env->funcSigs.infallibleAppend(&env->sigs[sigIndex]); } if (!d.finishSection(sectionStart, sectionSize, "function")) @@ -326,89 +499,59 @@ wasm::DecodeFunctionSection(Decoder& d, const SigWithIdVector& sigs, size_t numI return true; } -bool -wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals) +static bool +DecodeTableSection(Decoder& d, TableDescVector* tables) { - uint32_t numLocalEntries = 0; - ValType prev = ValType(TypeCode::Limit); - for (ValType t : locals) { - if (t != prev) { - numLocalEntries++; - prev = t; - } - } + uint32_t sectionStart, sectionSize; + if (!d.startSection(SectionId::Table, §ionStart, §ionSize, "table")) + return false; + if (sectionStart == Decoder::NotStarted) + return true; - if (!e.writeVarU32(numLocalEntries)) + uint32_t numTables; + if (!d.readVarU32(&numTables)) + return d.fail("failed to read number of tables"); + + if (numTables != 1) + return d.fail("the number of tables must be exactly one"); + + if (!DecodeTableLimits(d, tables)) return false; - if (numLocalEntries) { - prev = locals[0]; - uint32_t count = 1; - for (uint32_t i = 1; i < locals.length(); i++, count++) { - if (prev != locals[i]) { - if (!e.writeVarU32(count)) - return false; - if (!e.writeValType(prev)) - return false; - prev = locals[i]; - count = 0; - } - } - if (!e.writeVarU32(count)) - return false; - if (!e.writeValType(prev)) - return false; - } - - return true; -} - -bool -wasm::DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals) -{ - uint32_t numLocalEntries; - if (!d.readVarU32(&numLocalEntries)) - return d.fail("failed to read number of local entries"); - - for (uint32_t i = 0; i < numLocalEntries; i++) { - uint32_t count; - if (!d.readVarU32(&count)) - return d.fail("failed to read local entry count"); - - if (MaxLocals - locals->length() < count) - return d.fail("too many locals"); - - ValType type; - if (!DecodeValType(d, kind, &type)) - return false; - - if (!locals->appendN(type, count)) - return false; - } - - return true; -} - -bool -wasm::DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable) -{ - if (!DecodeValType(d, ModuleKind::Wasm, type)) + if (!d.finishSection(sectionStart, sectionSize, "table")) return false; - uint32_t flags; - if (!d.readVarU32(&flags)) - return d.fail("expected global flags"); - - if (flags & ~uint32_t(GlobalTypeImmediate::AllowedMask)) - return d.fail("unexpected bits set in global flags"); - - *isMutable = flags & uint32_t(GlobalTypeImmediate::IsMutable); return true; } -bool -wasm::DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected, - InitExpr* init) +static bool +DecodeMemorySection(Decoder& d, ModuleEnvironment* env) +{ + uint32_t sectionStart, sectionSize; + if (!d.startSection(SectionId::Memory, §ionStart, §ionSize, "memory")) + return false; + if (sectionStart == Decoder::NotStarted) + return true; + + uint32_t numMemories; + if (!d.readVarU32(&numMemories)) + return d.fail("failed to read number of memories"); + + if (numMemories != 1) + return d.fail("the number of memories must be exactly one"); + + if (!DecodeMemoryLimits(d, env)) + return false; + + if (!d.finishSection(sectionStart, sectionSize, "memory")) + return false; + + return true; +} + +static bool +DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected, + InitExpr* init) { uint16_t op; if (!d.readOp(&op)) @@ -469,39 +612,290 @@ wasm::DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, V return true; } -bool -wasm::DecodeLimits(Decoder& d, Limits* limits) +static bool +DecodeGlobalSection(Decoder& d, GlobalDescVector* globals) { - uint32_t flags; - if (!d.readVarU32(&flags)) - return d.fail("expected flags"); + uint32_t sectionStart, sectionSize; + if (!d.startSection(SectionId::Global, §ionStart, §ionSize, "global")) + return false; + if (sectionStart == Decoder::NotStarted) + return true; - if (flags & ~uint32_t(0x1)) - return d.fail("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x1))); + uint32_t numDefs; + if (!d.readVarU32(&numDefs)) + return d.fail("expected number of globals"); - if (!d.readVarU32(&limits->initial)) - return d.fail("expected initial length"); + uint32_t numGlobals = globals->length() + numDefs; + if (numGlobals > MaxGlobals) + return d.fail("too many globals"); - if (flags & 0x1) { - uint32_t maximum; - if (!d.readVarU32(&maximum)) - return d.fail("expected maximum length"); + if (!globals->reserve(numGlobals)) + return false; - if (limits->initial > maximum) { - return d.fail("memory size minimum must not be greater than maximum; " - "maximum length %" PRIu32 " is less than initial length %" PRIu32, - maximum, limits->initial); + for (uint32_t i = 0; i < numDefs; i++) { + ValType type; + bool isMutable; + if (!DecodeGlobalType(d, &type, &isMutable)) + return false; + + InitExpr initializer; + if (!DecodeInitializerExpression(d, *globals, type, &initializer)) + return false; + + globals->infallibleAppend(GlobalDesc(initializer, isMutable)); + } + + if (!d.finishSection(sectionStart, sectionSize, "global")) + return false; + + return true; +} + +typedef HashSet CStringSet; + +static UniqueChars +DecodeExportName(Decoder& d, CStringSet* dupSet) +{ + UniqueChars exportName = DecodeName(d); + if (!exportName) { + d.fail("expected valid export name"); + return nullptr; + } + + CStringSet::AddPtr p = dupSet->lookupForAdd(exportName.get()); + if (p) { + d.fail("duplicate export"); + return nullptr; + } + + if (!dupSet->add(p, exportName.get())) + return nullptr; + + return Move(exportName); +} + +static bool +DecodeExport(Decoder& d, ModuleEnvironment* env, CStringSet* dupSet) +{ + UniqueChars fieldName = DecodeExportName(d, dupSet); + if (!fieldName) + return false; + + uint32_t exportKind; + if (!d.readVarU32(&exportKind)) + return d.fail("failed to read export kind"); + + switch (DefinitionKind(exportKind)) { + case DefinitionKind::Function: { + uint32_t funcIndex; + if (!d.readVarU32(&funcIndex)) + return d.fail("expected function index"); + + if (funcIndex >= env->numFuncs()) + return d.fail("exported function index out of bounds"); + + return env->exports.emplaceBack(Move(fieldName), funcIndex, DefinitionKind::Function); + } + case DefinitionKind::Table: { + uint32_t tableIndex; + if (!d.readVarU32(&tableIndex)) + return d.fail("expected table index"); + + if (tableIndex >= env->tables.length()) + return d.fail("exported table index out of bounds"); + + MOZ_ASSERT(env->tables.length() == 1); + env->tables[0].external = true; + + return env->exports.emplaceBack(Move(fieldName), DefinitionKind::Table); + } + case DefinitionKind::Memory: { + uint32_t memoryIndex; + if (!d.readVarU32(&memoryIndex)) + return d.fail("expected memory index"); + + if (memoryIndex > 0 || !env->usesMemory()) + return d.fail("exported memory index out of bounds"); + + return env->exports.emplaceBack(Move(fieldName), DefinitionKind::Memory); + } + case DefinitionKind::Global: { + uint32_t globalIndex; + if (!d.readVarU32(&globalIndex)) + return d.fail("expected global index"); + + if (globalIndex >= env->globals.length()) + return d.fail("exported global index out of bounds"); + + const GlobalDesc& global = env->globals[globalIndex]; + if (!GlobalIsJSCompatible(d, global.type(), global.isMutable())) + return false; + + return env->exports.emplaceBack(Move(fieldName), globalIndex, DefinitionKind::Global); + } + default: + return d.fail("unexpected export kind"); + } + + MOZ_CRASH("unreachable"); +} + +static bool +DecodeExportSection(Decoder& d, ModuleEnvironment* env) +{ + uint32_t sectionStart, sectionSize; + if (!d.startSection(SectionId::Export, §ionStart, §ionSize, "export")) + return false; + if (sectionStart == Decoder::NotStarted) + return true; + + CStringSet dupSet; + if (!dupSet.init()) + return false; + + uint32_t numExports; + if (!d.readVarU32(&numExports)) + return d.fail("failed to read number of exports"); + + if (numExports > MaxExports) + return d.fail("too many exports"); + + for (uint32_t i = 0; i < numExports; i++) { + if (!DecodeExport(d, env, &dupSet)) + return false; + } + + if (!d.finishSection(sectionStart, sectionSize, "export")) + return false; + + return true; +} + +static bool +DecodeStartSection(Decoder& d, ModuleEnvironment* env) +{ + uint32_t sectionStart, sectionSize; + if (!d.startSection(SectionId::Start, §ionStart, §ionSize, "start")) + return false; + if (sectionStart == Decoder::NotStarted) + return true; + + uint32_t funcIndex; + if (!d.readVarU32(&funcIndex)) + return d.fail("failed to read start func index"); + + if (funcIndex >= env->numFuncs()) + return d.fail("unknown start function"); + + const Sig& sig = *env->funcSigs[funcIndex]; + if (!IsVoid(sig.ret())) + return d.fail("start function must not return anything"); + + if (sig.args().length()) + return d.fail("start function must be nullary"); + + env->startFuncIndex = Some(funcIndex); + + if (!d.finishSection(sectionStart, sectionSize, "start")) + return false; + + return true; +} + +static bool +DecodeElemSection(Decoder& d, ModuleEnvironment* env) +{ + uint32_t sectionStart, sectionSize; + if (!d.startSection(SectionId::Elem, §ionStart, §ionSize, "elem")) + return false; + if (sectionStart == Decoder::NotStarted) + return true; + + uint32_t numSegments; + if (!d.readVarU32(&numSegments)) + return d.fail("failed to read number of elem segments"); + + if (numSegments > MaxElemSegments) + return d.fail("too many elem segments"); + + for (uint32_t i = 0; i < numSegments; i++) { + uint32_t tableIndex; + if (!d.readVarU32(&tableIndex)) + return d.fail("expected table index"); + + MOZ_ASSERT(env->tables.length() <= 1); + if (tableIndex >= env->tables.length()) + return d.fail("table index out of range"); + + InitExpr offset; + if (!DecodeInitializerExpression(d, env->globals, ValType::I32, &offset)) + return false; + + uint32_t numElems; + if (!d.readVarU32(&numElems)) + return d.fail("expected segment size"); + + Uint32Vector elemFuncIndices; + if (!elemFuncIndices.resize(numElems)) + return false; + + for (uint32_t i = 0; i < numElems; i++) { + if (!d.readVarU32(&elemFuncIndices[i])) + return d.fail("failed to read element function index"); + if (elemFuncIndices[i] >= env->numFuncs()) + return d.fail("table element out of range"); } - limits->maximum.emplace(maximum); + if (!env->elemSegments.emplaceBack(0, offset, Move(elemFuncIndices))) + return false; + + env->tables[env->elemSegments.back().tableIndex].external = true; } + if (!d.finishSection(sectionStart, sectionSize, "elem")) + return false; + return true; } bool -wasm::DecodeDataSection(Decoder& d, bool usesMemory, uint32_t minMemoryByteLength, - const GlobalDescVector& globals, DataSegmentVector* segments) +wasm::DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env) +{ + if (!DecodePreamble(d)) + return false; + + if (!DecodeTypeSection(d, &env->sigs)) + return false; + + if (!DecodeImportSection(d, env)) + return false; + + if (!DecodeFunctionSection(d, env)) + return false; + + if (!DecodeTableSection(d, &env->tables)) + return false; + + if (!DecodeMemorySection(d, env)) + return false; + + if (!DecodeGlobalSection(d, &env->globals)) + return false; + + if (!DecodeExportSection(d, env)) + return false; + + if (!DecodeStartSection(d, env)) + return false; + + if (!DecodeElemSection(d, env)) + return false; + + return true; +} + +bool +wasm::DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVector* segments) { uint32_t sectionStart, sectionSize; if (!d.startSection(SectionId::Data, §ionStart, §ionSize, "data")) @@ -509,7 +903,7 @@ wasm::DecodeDataSection(Decoder& d, bool usesMemory, uint32_t minMemoryByteLengt if (sectionStart == Decoder::NotStarted) return true; - if (!usesMemory) + if (!env.usesMemory()) return d.fail("data section requires a memory section"); uint32_t numSegments; @@ -528,7 +922,7 @@ wasm::DecodeDataSection(Decoder& d, bool usesMemory, uint32_t minMemoryByteLengt return d.fail("linear memory index must currently be 0"); DataSegment seg; - if (!DecodeInitializerExpression(d, globals, ValType::I32, &seg.offset)) + if (!DecodeInitializerExpression(d, env.globals, ValType::I32, &seg.offset)) return false; if (!d.readVarU32(&seg.length)) @@ -549,63 +943,6 @@ wasm::DecodeDataSection(Decoder& d, bool usesMemory, uint32_t minMemoryByteLengt return true; } -bool -wasm::DecodeMemoryLimits(Decoder& d, bool hasMemory, Limits* memory) -{ - if (hasMemory) - return d.fail("already have default memory"); - - if (!DecodeLimits(d, memory)) - return false; - - CheckedInt initialBytes = memory->initial; - initialBytes *= PageSize; - if (!initialBytes.isValid() || initialBytes.value() > uint32_t(INT32_MAX)) - return d.fail("initial memory size too big"); - - memory->initial = initialBytes.value(); - - if (memory->maximum) { - CheckedInt maximumBytes = *memory->maximum; - maximumBytes *= PageSize; - if (!maximumBytes.isValid()) - return d.fail("maximum memory size too big"); - - memory->maximum = Some(maximumBytes.value()); - } - - return true; -} - -bool -wasm::DecodeMemorySection(Decoder& d, bool hasMemory, Limits* memory, bool *present) -{ - *present = false; - - uint32_t sectionStart, sectionSize; - if (!d.startSection(SectionId::Memory, §ionStart, §ionSize, "memory")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - *present = true; - - uint32_t numMemories; - if (!d.readVarU32(&numMemories)) - return d.fail("failed to read number of memories"); - - if (numMemories != 1) - return d.fail("the number of memories must be exactly one"); - - if (!DecodeMemoryLimits(d, hasMemory, memory)) - return false; - - if (!d.finishSection(sectionStart, sectionSize, "memory")) - return false; - - return true; -} - bool wasm::DecodeUnknownSections(Decoder& d) { @@ -615,29 +952,4 @@ wasm::DecodeUnknownSections(Decoder& d) } return true; -} - -bool -Decoder::fail(const char* msg, ...) -{ - va_list ap; - va_start(ap, msg); - UniqueChars str(JS_vsmprintf(msg, ap)); - va_end(ap); - if (!str) - return false; - - return fail(Move(str)); -} - -bool -Decoder::fail(UniqueChars msg) -{ - MOZ_ASSERT(error_); - UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s", currentOffset(), msg.get())); - if (!strWithOffset) - return false; - - *error_ = Move(strWithOffset); - return false; -} +} \ No newline at end of file diff --git a/js/src/wasm/WasmBinaryFormat.h b/js/src/wasm/WasmBinaryFormat.h index 0279597cb8..17ea83be2e 100644 --- a/js/src/wasm/WasmBinaryFormat.h +++ b/js/src/wasm/WasmBinaryFormat.h @@ -19,7 +19,7 @@ #ifndef wasm_binary_format_h #define wasm_binary_format_h -#include "wasm/WasmTypes.h" +#include "wasm/WasmCode.h" namespace js { namespace wasm { @@ -584,61 +584,87 @@ class Decoder // Misc helpers. -UniqueChars -DecodeName(Decoder& d); - -MOZ_MUST_USE bool -DecodeTableLimits(Decoder& d, TableDescVector* tables); - -MOZ_MUST_USE bool -GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable); - -MOZ_MUST_USE bool +[[nodiscard]] bool EncodeLocalEntries(Encoder& d, const ValTypeVector& locals); MOZ_MUST_USE bool DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals); -MOZ_MUST_USE bool -DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable); +// ModuleEnvironment contains all the state necessary to validate, process or +// render functions. It is created by decoding all the sections before the wasm +// code section and then used immutably during. When compiling a module using a +// ModuleGenerator, the ModuleEnvironment holds state shared between the +// ModuleGenerator thread and background compile threads. All the threads +// are given a read-only view of the ModuleEnvironment, thus preventing race +// conditions. -MOZ_MUST_USE bool -DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected, - InitExpr* init); +struct ModuleEnvironment +{ + ModuleKind kind; + MemoryUsage memoryUsage; + mozilla::Atomic minMemoryLength; + Maybe maxMemoryLength; -MOZ_MUST_USE bool -DecodeLimits(Decoder& d, Limits* limits); + SigWithIdVector sigs; + SigWithIdPtrVector funcSigs; + Uint32Vector funcImportGlobalDataOffsets; + GlobalDescVector globals; + TableDescVector tables; + Uint32Vector asmJSSigToTableIndex; + ImportVector imports; + ExportVector exports; + Maybe startFuncIndex; + ElemSegmentVector elemSegments; -MOZ_MUST_USE bool -DecodeMemoryLimits(Decoder& d, bool hasMemory, Limits* memory); + explicit ModuleEnvironment(ModuleKind kind = ModuleKind::Wasm) + : kind(kind), + memoryUsage(MemoryUsage::None), + minMemoryLength(0) + {} + + size_t numFuncs() const { + // asm.js pre-reserves a bunch of function index space which is + // incrementally filled in during function-body validation. Thus, there + // are a few possible interpretations of numFuncs() (total index space + // size vs. exact number of imports/definitions encountered so far) and + // to simplify things we simply only define this quantity for wasm. + MOZ_ASSERT(!isAsmJS()); + return funcSigs.length(); + } + size_t numFuncDefs() const { + // asm.js overallocates the length of funcSigs and in general does not + // know the number of function definitions until it's done compiling. + MOZ_ASSERT(!isAsmJS()); + return funcSigs.length() - funcImportGlobalDataOffsets.length(); + } + bool usesMemory() const { + return UsesMemory(memoryUsage); + } + + bool isAsmJS() const { + return kind == ModuleKind::AsmJS; + } + bool funcIsImport(uint32_t funcIndex) const { + return funcIndex < funcImportGlobalDataOffsets.length(); + } + uint32_t funcIndexToSigIndex(uint32_t funcIndex) const { + return funcSigs[funcIndex] - sigs.begin(); + } +}; + +typedef UniquePtr UniqueModuleEnvironment; // Section macros. -MOZ_MUST_USE bool -DecodePreamble(Decoder& d); +[[nodiscard]] bool +DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env); -MOZ_MUST_USE bool -DecodeTypeSection(Decoder& d, SigWithIdVector* sigs); - -MOZ_MUST_USE bool -DecodeImportSection(Decoder& d, const SigWithIdVector& sigs, Uint32Vector* funcSigIndices, - GlobalDescVector* globals, TableDescVector* tables, Maybe* memory, - ImportVector* imports); - -MOZ_MUST_USE bool -DecodeFunctionSection(Decoder& d, const SigWithIdVector& sigs, size_t numImportedFunc, - Uint32Vector* funcSigIndexes); +[[nodiscard]] bool +DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVector* segments); MOZ_MUST_USE bool DecodeUnknownSections(Decoder& d); -MOZ_MUST_USE bool -DecodeDataSection(Decoder& d, bool usesMemory, uint32_t minMemoryByteLength, - const GlobalDescVector& globals, DataSegmentVector* segments); - -MOZ_MUST_USE bool -DecodeMemorySection(Decoder& d, bool hasMemory, Limits* memory, bool* present); - } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmBinaryToAST.cpp b/js/src/wasm/WasmBinaryToAST.cpp index 8d6db7b357..bc3eca7f47 100644 --- a/js/src/wasm/WasmBinaryToAST.cpp +++ b/js/src/wasm/WasmBinaryToAST.cpp @@ -80,7 +80,6 @@ typedef OpIter AstDecodeOpIter; class AstDecodeContext { public: - typedef AstVector AstIndexVector; typedef AstVector AstDecodeStack; typedef AstVector DepthStack; @@ -90,13 +89,13 @@ class AstDecodeContext bool generateNames; private: + ModuleEnvironment env_; + AstModule& module_; - AstIndexVector funcDefSigs_; AstDecodeOpIter *iter_; AstDecodeStack exprs_; DepthStack depths_; const ValTypeVector* locals_; - GlobalDescVector globals_; AstNameVector blockLabels_; uint32_t currentLabelIndex_; ExprType retType_; @@ -109,7 +108,6 @@ class AstDecodeContext d(d), generateNames(generateNames), module_(module), - funcDefSigs_(lifo), iter_(nullptr), exprs_(lifo), depths_(lifo), @@ -119,8 +117,9 @@ class AstDecodeContext retType_(ExprType::Limit) {} + ModuleEnvironment& env() { return env_; } + AstModule& module() { return module_; } - AstIndexVector& funcDefSigs() { return funcDefSigs_; } AstDecodeOpIter& iter() { return *iter_; } AstDecodeStack& exprs() { return exprs_; } DepthStack& depths() { return depths_; } @@ -130,28 +129,17 @@ class AstDecodeContext ExprType retType() const { return retType_; } const ValTypeVector& locals() const { return *locals_; } - bool addGlobalDesc(ValType type, bool isMutable, bool isImport) { - if (isImport) - return globals_.append(GlobalDesc(type, isMutable, globals_.length())); - // No need to have the precise init expr value; we just need the right - // type. - Val dummy; - switch (type) { - case ValType::I32: dummy = Val(uint32_t(0)); break; - case ValType::I64: dummy = Val(uint64_t(0)); break; - case ValType::F32: dummy = Val(RawF32(0.f)); break; - case ValType::F64: dummy = Val(RawF64(0.0)); break; - default: return false; - } - return globals_.append(GlobalDesc(InitExpr(dummy), isMutable)); - } - const GlobalDescVector& globalDescs() const { return globals_; } - void popBack() { return exprs().popBack(); } AstDecodeStackItem popCopy() { return exprs().popCopy(); } AstDecodeStackItem& top() { return exprs().back(); } MOZ_MUST_USE bool push(AstDecodeStackItem item) { return exprs().append(item); } + bool checkHasMemory() { + if (!module().hasMemory()) + return iter().fail("can't touch memory without memory"); + return true; + } + bool needFirst() { for (size_t i = depths().back(); i < exprs().length(); ++i) { if (!exprs()[i].expr->isVoid()) @@ -265,11 +253,11 @@ GenerateRef(AstDecodeContext& c, const AstName& prefix, uint32_t index, AstRef* } static bool -AstDecodeCallArgs(AstDecodeContext& c, const AstSig& sig, AstExprVector* funcArgs) +AstDecodeCallArgs(AstDecodeContext& c, const SigWithId& sig, AstExprVector* funcArgs) { MOZ_ASSERT(c.iter().inReachableCode()); - const AstValTypeVector& args = sig.args(); + const ValTypeVector& args = sig.args(); uint32_t numArgs = args.length(); if (!funcArgs->resize(numArgs)) @@ -288,7 +276,7 @@ AstDecodeCallArgs(AstDecodeContext& c, const AstSig& sig, AstExprVector* funcArg } static bool -AstDecodeCallReturn(AstDecodeContext& c, const AstSig& sig) +AstDecodeCallReturn(AstDecodeContext& c, const SigWithId& sig) { return c.iter().readCallReturn(sig.ret()); } @@ -328,24 +316,19 @@ AstDecodeCall(AstDecodeContext& c) if (!c.iter().inReachableCode()) return true; - uint32_t sigIndex; + if (funcIndex >= c.env().numFuncs()) + return c.iter().fail("callee index out of range"); + AstRef funcRef; if (funcIndex < c.module().numFuncImports()) { AstImport* import = c.module().imports()[funcIndex]; - sigIndex = import->funcSig().index(); funcRef = AstRef(import->name()); } else { - uint32_t funcDefIndex = funcIndex - c.module().numFuncImports(); - if (funcDefIndex >= c.funcDefSigs().length()) - return c.iter().fail("callee index out of range"); - - sigIndex = c.funcDefSigs()[funcDefIndex]; - if (!GenerateRef(c, AstName(u"func"), funcIndex, &funcRef)) return false; } - const AstSig* sig = c.module().sigs()[sigIndex]; + const SigWithId* sig = c.env().funcSigs[funcIndex]; AstExprVector args(c.lifo); if (!AstDecodeCallArgs(c, *sig, &args)) @@ -371,6 +354,9 @@ AstDecodeCall(AstDecodeContext& c) static bool AstDecodeCallIndirect(AstDecodeContext& c) { + if (!c.env().tables.length()) + return c.iter().fail("can't call_indirect without a table"); + uint32_t sigIndex; if (!c.iter().readCallIndirect(&sigIndex, nullptr)) return false; @@ -387,21 +373,20 @@ AstDecodeCallIndirect(AstDecodeContext& c) if (!GenerateRef(c, AstName(u"type"), sigIndex, &sigRef)) return false; - const AstSig* sig = c.module().sigs()[sigIndex]; + const SigWithId& sig = c.env().sigs[sigIndex]; AstExprVector args(c.lifo); - if (!AstDecodeCallArgs(c, *sig, &args)) + if (!AstDecodeCallArgs(c, sig, &args)) return false; - if (!AstDecodeCallReturn(c, *sig)) + if (!AstDecodeCallReturn(c, sig)) return false; - AstCallIndirect* call = new(c.lifo) AstCallIndirect(sigRef, sig->ret(), - Move(args), index.expr); + AstCallIndirect* call = new(c.lifo) AstCallIndirect(sigRef, sig.ret(), Move(args), index.expr); if (!call) return false; AstExpr* result = call; - if (IsVoid(sig->ret())) + if (IsVoid(sig.ret())) result = c.handleVoidExpr(call); if (!c.push(AstDecodeStackItem(result))) @@ -758,6 +743,9 @@ AstDecodeLoadStoreAddress(const LinearMemoryAddress& addr, const AstDec static bool AstDecodeLoad(AstDecodeContext& c, ValType type, uint32_t byteSize, Op op) { + if (!c.checkHasMemory()) + return false; + LinearMemoryAddress addr; if (!c.iter().readLoad(type, byteSize, &addr)) return false; @@ -777,6 +765,9 @@ AstDecodeLoad(AstDecodeContext& c, ValType type, uint32_t byteSize, Op op) static bool AstDecodeStore(AstDecodeContext& c, ValType type, uint32_t byteSize, Op op) { + if (!c.checkHasMemory()) + return false; + LinearMemoryAddress addr; if (!c.iter().readStore(type, byteSize, &addr, nullptr)) return false; @@ -945,7 +936,7 @@ static bool AstDecodeGetGlobal(AstDecodeContext& c) { uint32_t globalId; - if (!c.iter().readGetGlobal(c.globalDescs(), &globalId)) + if (!c.iter().readGetGlobal(c.env().globals, &globalId)) return false; AstRef globalRef; @@ -966,7 +957,7 @@ static bool AstDecodeSetGlobal(AstDecodeContext& c) { uint32_t globalId; - if (!c.iter().readSetGlobal(c.globalDescs(), &globalId, nullptr)) + if (!c.iter().readSetGlobal(c.env().globals, &globalId, nullptr)) return false; AstDecodeStackItem value = c.popCopy(); @@ -1436,360 +1427,8 @@ AstDecodeExpr(AstDecodeContext& c) return true; } -/*****************************************************************************/ -// wasm decoding and generation - static bool -AstDecodeTypeSection(AstDecodeContext& c, SigWithIdVector* sigs) -{ - if (!DecodeTypeSection(c.d, sigs)) - return false; - - for (size_t sigIndex = 0; sigIndex < sigs->length(); sigIndex++) { - const Sig& sig = (*sigs)[sigIndex]; - - AstValTypeVector args(c.lifo); - if (!args.appendAll(sig.args())) - return false; - - AstSig sigNoName(Move(args), sig.ret()); - AstName sigName; - if (!GenerateName(c, AstName(u"type"), sigIndex, &sigName)) - return false; - - AstSig* astSig = new(c.lifo) AstSig(sigName, Move(sigNoName)); - if (!astSig || !c.module().append(astSig)) - return false; - } - - return true; -} - -static AstName -ToAstName(AstDecodeContext& c, const UniqueChars& name) -{ - size_t len = strlen(name.get()); - char16_t* buffer = static_cast(c.lifo.alloc(len * sizeof(char16_t))); - if (!buffer) - return AstName(); - - for (size_t i = 0; i < len; i++) - buffer[i] = name.get()[i]; - - return AstName(buffer, len); -} - -static bool -AstDecodeImportSection(AstDecodeContext& c, const SigWithIdVector& sigs) -{ - Uint32Vector funcSigIndices; - GlobalDescVector globals; - TableDescVector tables; - Maybe memory; - ImportVector imports; - if (!DecodeImportSection(c.d, sigs, &funcSigIndices, &globals, &tables, &memory, &imports)) - return false; - - size_t lastFunc = 0; - size_t lastGlobal = 0; - size_t lastTable = 0; - size_t lastMemory = 0; - - for (size_t importIndex = 0; importIndex < imports.length(); importIndex++) { - const Import& import = imports[importIndex]; - - AstName moduleName = ToAstName(c, import.module); - AstName fieldName = ToAstName(c, import.field); - - AstImport* ast = nullptr; - switch (import.kind) { - case DefinitionKind::Function: { - AstName importName; - if (!GenerateName(c, AstName(u"import"), lastFunc, &importName)) - return false; - - AstRef sigRef; - if (!GenerateRef(c, AstName(u"type"), funcSigIndices[lastFunc], &sigRef)) - return false; - - ast = new(c.lifo) AstImport(importName, moduleName, fieldName, sigRef); - lastFunc++; - break; - } - case DefinitionKind::Global: { - AstName importName; - if (!GenerateName(c, AstName(u"global"), lastGlobal, &importName)) - return false; - - const GlobalDesc& global = globals[lastGlobal]; - ValType type = global.type(); - bool isMutable = global.isMutable(); - - if (!c.addGlobalDesc(type, isMutable, /* import */ true)) - return false; - - ast = new(c.lifo) AstImport(importName, moduleName, fieldName, - AstGlobal(importName, type, isMutable)); - lastGlobal++; - break; - } - case DefinitionKind::Table: { - AstName importName; - if (!GenerateName(c, AstName(u"table"), lastTable, &importName)) - return false; - - ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Table, - tables[lastTable].limits); - lastTable++; - break; - } - case DefinitionKind::Memory: { - AstName importName; - if (!GenerateName(c, AstName(u"memory"), lastMemory, &importName)) - return false; - - ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Memory, - *memory); - lastMemory++; - break; - } - } - - if (!ast || !c.module().append(ast)) - return false; - } - - return true; -} - -static bool -AstDecodeFunctionSection(AstDecodeContext& c, const SigWithIdVector& sigs) -{ - Uint32Vector funcSigIndexes; - if (!DecodeFunctionSection(c.d, sigs, c.module().numFuncImports(), &funcSigIndexes)) - return false; - - return c.funcDefSigs().appendAll(funcSigIndexes); -} - -static bool -AstDecodeTableSection(AstDecodeContext& c) -{ - uint32_t sectionStart, sectionSize; - if (!c.d.startSection(SectionId::Table, §ionStart, §ionSize, "table")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - uint32_t numTables; - if (!c.d.readVarU32(&numTables)) - return c.d.fail("failed to read number of tables"); - - if (numTables != 1) - return c.d.fail("the number of tables must be exactly one"); - - uint32_t typeConstructorValue; - if (!c.d.readVarU32(&typeConstructorValue)) - return c.d.fail("expected type constructor kind"); - - if (typeConstructorValue != uint32_t(TypeCode::AnyFunc)) - return c.d.fail("unknown type constructor kind"); - - Limits table; - if (!DecodeLimits(c.d, &table)) - return false; - - if (table.initial > MaxTableElems) - return c.d.fail("too many table elements"); - - if (c.module().hasTable()) - return c.d.fail("already have a table"); - - AstName name; - if (!GenerateName(c, AstName(u"table"), c.module().tables().length(), &name)) - return false; - - if (!c.module().addTable(name, table)) - return false; - - if (!c.d.finishSection(sectionStart, sectionSize, "table")) - return false; - - return true; -} - -static bool -AstDecodeName(AstDecodeContext& c, AstName* name) -{ - uint32_t length; - if (!c.d.readVarU32(&length)) - return false; - - const uint8_t* bytes; - if (!c.d.readBytes(length, &bytes)) - return false; - - char16_t* buffer = static_cast(c.lifo.alloc(length * sizeof(char16_t))); - for (size_t i = 0; i < length; i++) - buffer[i] = bytes[i]; - - *name = AstName(buffer, length); - return true; -} - -static bool -AstDecodeMemorySection(AstDecodeContext& c) -{ - bool present; - Limits memory; - if (!DecodeMemorySection(c.d, c.module().hasMemory(), &memory, &present)) - return false; - - if (present) { - AstName name; - if (!GenerateName(c, AstName(u"memory"), c.module().memories().length(), &name)) - return false; - if (!c.module().addMemory(name, memory)) - return false; - } - - return true; -} - -static AstExpr* -ToAstExpr(AstDecodeContext& c, const InitExpr& initExpr) -{ - switch (initExpr.kind()) { - case InitExpr::Kind::Constant: { - return new(c.lifo) AstConst(Val(initExpr.val())); - } - case InitExpr::Kind::GetGlobal: { - AstRef globalRef; - if (!GenerateRef(c, AstName(u"global"), initExpr.globalIndex(), &globalRef)) - return nullptr; - return new(c.lifo) AstGetGlobal(globalRef); - } - } - return nullptr; -} - -static bool -AstDecodeInitializerExpression(AstDecodeContext& c, ValType type, AstExpr** init) -{ - InitExpr initExpr; - if (!DecodeInitializerExpression(c.d, c.globalDescs(), type, &initExpr)) - return false; - - *init = ToAstExpr(c, initExpr); - return !!*init; -} - -static bool -AstDecodeGlobal(AstDecodeContext& c, uint32_t i, AstGlobal* global) -{ - AstName name; - if (!GenerateName(c, AstName(u"global"), i, &name)) - return false; - - ValType type; - bool isMutable; - if (!DecodeGlobalType(c.d, &type, &isMutable)) - return false; - - AstExpr* init; - if (!AstDecodeInitializerExpression(c, type, &init)) - return false; - - if (!c.addGlobalDesc(type, isMutable, /* import */ false)) - return false; - - *global = AstGlobal(name, type, isMutable, Some(init)); - return true; -} - -static bool -AstDecodeGlobalSection(AstDecodeContext& c) -{ - uint32_t sectionStart, sectionSize; - if (!c.d.startSection(SectionId::Global, §ionStart, §ionSize, "global")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - uint32_t numGlobals; - if (!c.d.readVarU32(&numGlobals)) - return c.d.fail("expected number of globals"); - - uint32_t numImported = c.globalDescs().length(); - - for (uint32_t i = 0; i < numGlobals; i++) { - auto* global = new(c.lifo) AstGlobal; - if (!AstDecodeGlobal(c, i + numImported, global)) - return false; - if (!c.module().append(global)) - return false; - } - - if (!c.d.finishSection(sectionStart, sectionSize, "global")) - return false; - - return true; -} - -static bool -AstDecodeExport(AstDecodeContext& c, AstExport** export_) -{ - AstName fieldName; - if (!AstDecodeName(c, &fieldName)) - return c.d.fail("expected export name"); - - uint32_t kindValue; - if (!c.d.readVarU32(&kindValue)) - return c.d.fail("expected export kind"); - - uint32_t index; - if (!c.d.readVarU32(&index)) - return c.d.fail("expected export internal index"); - - *export_ = new(c.lifo) AstExport(fieldName, DefinitionKind(kindValue), AstRef(index)); - if (!*export_) - return false; - - return true; -} - -static bool -AstDecodeExportSection(AstDecodeContext& c) -{ - uint32_t sectionStart, sectionSize; - if (!c.d.startSection(SectionId::Export, §ionStart, §ionSize, "export")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - uint32_t numExports; - if (!c.d.readVarU32(&numExports)) - return c.d.fail("failed to read number of exports"); - - if (numExports > MaxExports) - return c.d.fail("too many exports"); - - for (uint32_t i = 0; i < numExports; i++) { - AstExport* export_ = nullptr; - if (!AstDecodeExport(c, &export_)) - return false; - if (!c.module().append(export_)) - return false; - } - - if (!c.d.finishSection(sectionStart, sectionSize, "export")) - return false; - - return true; -} - -static bool -AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcDefIndex, AstFunc** func) +AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcIndex, AstFunc** func) { uint32_t offset = c.d.currentOffset(); uint32_t bodySize; @@ -1802,34 +1441,32 @@ AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcDefIndex, AstFunc** func const uint8_t* bodyBegin = c.d.currentPosition(); const uint8_t* bodyEnd = bodyBegin + bodySize; - AstDecodeOpIter iter(c.d); - - uint32_t sigIndex = c.funcDefSigs()[funcDefIndex]; - const AstSig* sig = c.module().sigs()[sigIndex]; - - AstValTypeVector vars(c.lifo); - AstNameVector localsNames(c.lifo); - AstExprVector body(c.lifo); + const SigWithId* sig = c.env().funcSigs[funcIndex]; ValTypeVector locals; if (!locals.appendAll(sig->args())) return false; if (!DecodeLocalEntries(c.d, ModuleKind::Wasm, &locals)) - return c.d.fail("failed decoding local entries"); + return false; + AstDecodeOpIter iter(c.d); c.startFunction(&iter, &locals, sig->ret()); AstName funcName; - if (!GenerateName(c, AstName(u"func"), c.module().numFuncImports() + funcDefIndex, &funcName)) + if (!GenerateName(c, AstName(u"func"), funcIndex, &funcName)) return false; uint32_t numParams = sig->args().length(); uint32_t numLocals = locals.length(); + + AstValTypeVector vars(c.lifo); for (uint32_t i = numParams; i < numLocals; i++) { if (!vars.append(locals[i])) return false; } + + AstNameVector localsNames(c.lifo); for (uint32_t i = 0; i < numLocals; i++) { AstName varName; if (!GenerateName(c, AstName(u"var"), i, &varName)) @@ -1855,8 +1492,8 @@ AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcDefIndex, AstFunc** func } } - for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); - i != e; ++i) { + AstExprVector body(c.lifo); + for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); i != e; ++i) { if (!body.append(i->expr)) return false; } @@ -1870,6 +1507,8 @@ AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcDefIndex, AstFunc** func if (c.d.currentPosition() != bodyEnd) return c.d.fail("function body length mismatch"); + size_t sigIndex = c.env().funcIndexToSigIndex(funcIndex); + AstRef sigRef; if (!GenerateRef(c, AstName(u"type"), sigIndex, &sigRef)) return false; @@ -1882,6 +1521,309 @@ AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcDefIndex, AstFunc** func return true; } +/*****************************************************************************/ +// wasm decoding and generation + +static bool +AstCreateSignatures(AstDecodeContext& c) +{ + SigWithIdVector& sigs = c.env().sigs; + + for (size_t sigIndex = 0; sigIndex < sigs.length(); sigIndex++) { + const Sig& sig = sigs[sigIndex]; + + AstValTypeVector args(c.lifo); + if (!args.appendAll(sig.args())) + return false; + + AstSig sigNoName(Move(args), sig.ret()); + AstName sigName; + if (!GenerateName(c, AstName(u"type"), sigIndex, &sigName)) + return false; + + AstSig* astSig = new(c.lifo) AstSig(sigName, Move(sigNoName)); + if (!astSig || !c.module().append(astSig)) + return false; + } + + return true; +} + +static bool +ToAstName(AstDecodeContext& c, const char* name, AstName* out) +{ + size_t len = strlen(name); + char16_t* buffer = static_cast(c.lifo.alloc(len * sizeof(char16_t))); + if (!buffer) + return false; + + for (size_t i = 0; i < len; i++) + buffer[i] = name[i]; + + *out = AstName(buffer, len); + return true; +} + +static bool +AstCreateImports(AstDecodeContext& c) +{ + Uint32Vector funcSigIndices; + + size_t lastFunc = 0; + size_t lastGlobal = 0; + size_t lastTable = 0; + size_t lastMemory = 0; + + Maybe memory; + if (c.env().usesMemory()) { + Limits limits; + limits.initial = c.env().minMemoryLength; + limits.maximum = c.env().maxMemoryLength; + memory = Some(limits); + } + + for (size_t importIndex = 0; importIndex < c.env().imports.length(); importIndex++) { + const Import& import = c.env().imports[importIndex]; + + AstName moduleName; + if (!ToAstName(c, import.module.get(), &moduleName)) + return false; + AstName fieldName; + if (!ToAstName(c, import.field.get(), &fieldName)) + return false; + + AstImport* ast = nullptr; + switch (import.kind) { + case DefinitionKind::Function: { + AstName importName; + if (!GenerateName(c, AstName(u"import"), lastFunc, &importName)) + return false; + + size_t sigIndex = c.env().funcIndexToSigIndex(lastFunc); + + AstRef sigRef; + if (!GenerateRef(c, AstName(u"type"), sigIndex, &sigRef)) + return false; + + ast = new(c.lifo) AstImport(importName, moduleName, fieldName, sigRef); + lastFunc++; + break; + } + case DefinitionKind::Global: { + AstName importName; + if (!GenerateName(c, AstName(u"global"), lastGlobal, &importName)) + return false; + + const GlobalDesc& global = c.env().globals[lastGlobal]; + ValType type = global.type(); + bool isMutable = global.isMutable(); + + ast = new(c.lifo) AstImport(importName, moduleName, fieldName, + AstGlobal(importName, type, isMutable)); + lastGlobal++; + break; + } + case DefinitionKind::Table: { + AstName importName; + if (!GenerateName(c, AstName(u"table"), lastTable, &importName)) + return false; + + ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Table, + c.env().tables[lastTable].limits); + lastTable++; + break; + } + case DefinitionKind::Memory: { + AstName importName; + if (!GenerateName(c, AstName(u"memory"), lastMemory, &importName)) + return false; + + ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Memory, + *memory); + lastMemory++; + break; + } + } + + if (!ast || !c.module().append(ast)) + return false; + } + + return true; +} + +static bool +AstCreateTables(AstDecodeContext& c) +{ + size_t numImported = c.module().tables().length(); + + for (size_t i = numImported; i < c.env().tables.length(); i++) { + AstName name; + if (!GenerateName(c, AstName(u"table"), i, &name)) + return false; + + if (!c.module().addTable(name, c.env().tables[i].limits)) + return false; + } + + return true; +} + +static bool +AstCreateMemory(AstDecodeContext& c) +{ + bool importedMemory = !!c.module().memories().length(); + if (!c.env().usesMemory() || importedMemory) + return true; + + AstName name; + if (!GenerateName(c, AstName(u"memory"), c.module().memories().length(), &name)) + return false; + + Limits limits; + limits.initial = c.env().minMemoryLength; + limits.maximum = c.env().maxMemoryLength; + return c.module().addMemory(name, limits); +} + +static AstExpr* +ToAstExpr(AstDecodeContext& c, const InitExpr& initExpr) +{ + switch (initExpr.kind()) { + case InitExpr::Kind::Constant: { + return new(c.lifo) AstConst(Val(initExpr.val())); + } + case InitExpr::Kind::GetGlobal: { + AstRef globalRef; + if (!GenerateRef(c, AstName(u"global"), initExpr.globalIndex(), &globalRef)) + return nullptr; + return new(c.lifo) AstGetGlobal(globalRef); + } + } + return nullptr; +} + +static bool +AstCreateGlobals(AstDecodeContext& c) +{ + for (uint32_t i = 0; i < c.env().globals.length(); i++) { + const GlobalDesc& global = c.env().globals[i]; + if (global.isImport()) + continue; + + AstName name; + if (!GenerateName(c, AstName(u"global"), i, &name)) + return false; + +AstExpr* init = global.isConstant() + ? new(c.lifo) AstConst(global.constantValue()) + : ToAstExpr(c, global.initExpr()); + if (!init) + return false; + + auto* g = new(c.lifo) AstGlobal(name, global.type(), global.isMutable(), Some(init)); + if (!g || !c.module().append(g)) + return false; + } + + return true; +} + +static bool +AstCreateExports(AstDecodeContext& c) +{ + for (const Export& exp : c.env().exports) { + size_t index; + switch (exp.kind()) { + case DefinitionKind::Function: index = exp.funcIndex(); break; + case DefinitionKind::Global: index = exp.globalIndex(); break; + case DefinitionKind::Memory: index = 0; break; + case DefinitionKind::Table: index = 0; break; + } + + AstName name; + if (!ToAstName(c, exp.fieldName(), &name)) + return false; + + AstExport* e = new(c.lifo) AstExport(name, exp.kind(), AstRef(index)); + if (!e || !c.module().append(e)) + return false; + } + + return true; +} + +static bool +AstCreateStartFunc(AstDecodeContext &c) +{ + if (!c.env().startFuncIndex) + return true; + + AstRef funcRef; + if (!GenerateRef(c, AstName(u"func"), *c.env().startFuncIndex, &funcRef)) + return false; + + c.module().setStartFunc(AstStartFunc(funcRef)); + + return true; +} + +static bool +AstCreateElems(AstDecodeContext &c) +{ + for (const ElemSegment& seg : c.env().elemSegments) { + AstRefVector elems(c.lifo); + if (!elems.reserve(seg.elemFuncIndices.length())) + return false; + + for (uint32_t i : seg.elemFuncIndices) + elems.infallibleAppend(AstRef(i)); + + AstExpr* offset = ToAstExpr(c, seg.offset); + if (!offset) + return false; + + AstElemSegment* segment = new(c.lifo) AstElemSegment(offset, Move(elems)); + if (!segment || !c.module().append(segment)) + return false; + } + + return true; +} + +static bool +AstDecodeEnvironment(AstDecodeContext& c) +{ + if (!DecodeModuleEnvironment(c.d, &c.env())) + return false; + + if (!AstCreateSignatures(c)) + return false; + + if (!AstCreateImports(c)) + return false; + + if (!AstCreateTables(c)) + return false; + + if (!AstCreateMemory(c)) + return false; + + if (!AstCreateGlobals(c)) + return false; + + if (!AstCreateExports(c)) + return false; + + if (!AstCreateStartFunc(c)) + return false; + + if (!AstCreateElems(c)) + return false; + + return true; +} + static bool AstDecodeCodeSection(AstDecodeContext &c) { @@ -1890,22 +1832,22 @@ AstDecodeCodeSection(AstDecodeContext &c) return false; if (sectionStart == Decoder::NotStarted) { - if (c.funcDefSigs().length() != 0) + if (c.env().numFuncDefs() != 0) return c.d.fail("expected function bodies"); - return false; + return true; } uint32_t numFuncBodies; if (!c.d.readVarU32(&numFuncBodies)) return c.d.fail("expected function body count"); - if (numFuncBodies != c.funcDefSigs().length()) + if (numFuncBodies != c.env().numFuncDefs()) return c.d.fail("function body count does not match function signature count"); for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncBodies; funcDefIndex++) { AstFunc* func; - if (!AstDecodeFunctionBody(c, funcDefIndex, &func)) + if (!AstDecodeFunctionBody(c, c.module().numFuncImports() + funcDefIndex, &func)) return false; if (!c.module().append(func)) return false; @@ -1923,13 +1865,10 @@ static const size_t WRAP_DATA_BYTES = 30; static bool AstDecodeDataSection(AstDecodeContext &c) { - DataSegmentVector segments; - bool hasMemory = c.module().hasMemory(); - MOZ_ASSERT(c.module().memories().length() <= 1, "at most one memory in MVP"); - uint32_t memByteLength = hasMemory ? c.module().memories()[0].limits.initial : 0; - if (!DecodeDataSection(c.d, hasMemory, memByteLength, c.globalDescs(), &segments)) + DataSegmentVector segments; + if (!DecodeDataSection(c.d, c.env(), &segments)) return false; for (DataSegment& s : segments) { @@ -1957,83 +1896,6 @@ AstDecodeDataSection(AstDecodeContext &c) return true; } -static bool -AstDecodeElemSection(AstDecodeContext &c) -{ - uint32_t sectionStart, sectionSize; - if (!c.d.startSection(SectionId::Elem, §ionStart, §ionSize, "elem")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - uint32_t numElems; - if (!c.d.readVarU32(&numElems)) - return c.d.fail("failed to read number of table elements"); - - for (uint32_t i = 0; i < numElems; i++) { - uint32_t tableIndex; - if (!c.d.readVarU32(&tableIndex)) - return c.d.fail("expected table index for element"); - - if (tableIndex != 0) - return c.d.fail("non-zero table index for element"); - - AstExpr* offset; - if (!AstDecodeInitializerExpression(c, ValType::I32, &offset)) - return false; - - uint32_t count; - if (!c.d.readVarU32(&count)) - return c.d.fail("expected element count"); - - AstRefVector elems(c.lifo); - if (!elems.resize(count)) - return false; - - for (uint32_t i = 0; i < count; i++) { - uint32_t index; - if (!c.d.readVarU32(&index)) - return c.d.fail("expected element index"); - - elems[i] = AstRef(index); - } - - AstElemSegment* segment = new(c.lifo) AstElemSegment(offset, Move(elems)); - if (!segment || !c.module().append(segment)) - return false; - } - - if (!c.d.finishSection(sectionStart, sectionSize, "elem")) - return false; - - return true; -} - -static bool -AstDecodeStartSection(AstDecodeContext &c) -{ - uint32_t sectionStart, sectionSize; - if (!c.d.startSection(SectionId::Start, §ionStart, §ionSize, "start")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - uint32_t funcIndex; - if (!c.d.readVarU32(&funcIndex)) - return c.d.fail("failed to read start func index"); - - AstRef funcRef; - if (!GenerateRef(c, AstName(u"func"), funcIndex, &funcRef)) - return false; - - c.module().setStartFunc(AstStartFunc(funcRef)); - - if (!c.d.finishSection(sectionStart, sectionSize, "start")) - return false; - - return true; -} - bool wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length, LifoAlloc& lifo, AstModule** module) @@ -2046,17 +1908,7 @@ wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length, Decoder d(bytes, bytes + length, &error); AstDecodeContext c(cx, lifo, d, *result, true); - SigWithIdVector sigs; - if (!DecodePreamble(d) || - !AstDecodeTypeSection(c, &sigs) || - !AstDecodeImportSection(c, sigs) || - !AstDecodeFunctionSection(c, sigs) || - !AstDecodeTableSection(c) || - !AstDecodeMemorySection(c) || - !AstDecodeGlobalSection(c) || - !AstDecodeExportSection(c) || - !AstDecodeStartSection(c) || - !AstDecodeElemSection(c) || + if (!AstDecodeEnvironment(c) || !AstDecodeCodeSection(c) || !AstDecodeDataSection(c) || !DecodeUnknownSections(c.d)) diff --git a/js/src/wasm/WasmCompile.cpp b/js/src/wasm/WasmCompile.cpp index ec518637d4..42ae5418f3 100644 --- a/js/src/wasm/WasmCompile.cpp +++ b/js/src/wasm/WasmCompile.cpp @@ -419,246 +419,6 @@ DecodeFunctionBodyExprs(FunctionDecoder& f) #undef CHECK } -static bool -DecodeImportSection(Decoder& d, ModuleGeneratorData* init, ImportVector* imports) -{ - Maybe memory; - Uint32Vector funcSigIndices; - if (!DecodeImportSection(d, init->sigs, &funcSigIndices, &init->globals, &init->tables, &memory, - imports)) - return false; - - for (uint32_t sigIndex : funcSigIndices) { - if (!init->funcSigs.append(&init->sigs[sigIndex])) - return false; - } - - // The global data offsets will be filled in by ModuleGenerator::init. - if (!init->funcImportGlobalDataOffsets.resize(init->funcSigs.length())) - return false; - - if (memory) { - init->memoryUsage = MemoryUsage::Unshared; - init->minMemoryLength = memory->initial; - init->maxMemoryLength = memory->maximum; - } - - return true; -} - -static bool -DecodeFunctionSection(Decoder& d, ModuleGeneratorData* init) -{ - Uint32Vector funcSigIndexes; - if (!DecodeFunctionSection(d, init->sigs, init->funcSigs.length(), &funcSigIndexes)) - return false; - - if (!init->funcSigs.reserve(init->funcSigs.length() + funcSigIndexes.length())) - return false; - - for (uint32_t sigIndex : funcSigIndexes) - init->funcSigs.infallibleAppend(&init->sigs[sigIndex]); - - return true; -} - -static bool -DecodeTableSection(Decoder& d, ModuleGeneratorData* init) -{ - uint32_t sectionStart, sectionSize; - if (!d.startSection(SectionId::Table, §ionStart, §ionSize, "table")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - uint32_t numTables; - if (!d.readVarU32(&numTables)) - return d.fail("failed to read number of tables"); - - if (numTables != 1) - return d.fail("the number of tables must be exactly one"); - - if (!DecodeTableLimits(d, &init->tables)) - return false; - - if (!d.finishSection(sectionStart, sectionSize, "table")) - return false; - - return true; -} - -static bool -DecodeMemorySection(Decoder& d, ModuleGeneratorData* init) -{ - bool present; - Limits memory; - if (!DecodeMemorySection(d, UsesMemory(init->memoryUsage), &memory, &present)) - return false; - - if (present) { - init->memoryUsage = MemoryUsage::Unshared; - init->minMemoryLength = memory.initial; - init->maxMemoryLength = memory.maximum; - } - - return true; -} - -static bool -DecodeGlobalSection(Decoder& d, ModuleGeneratorData* init) -{ - uint32_t sectionStart, sectionSize; - if (!d.startSection(SectionId::Global, §ionStart, §ionSize, "global")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - uint32_t numDefs; - if (!d.readVarU32(&numDefs)) - return d.fail("expected number of globals"); - - CheckedInt numGlobals = init->globals.length(); - numGlobals += numDefs; - if (!numGlobals.isValid() || numGlobals.value() > MaxGlobals) - return d.fail("too many globals"); - - for (uint32_t i = 0; i < numDefs; i++) { - ValType type; - bool isMutable; - if (!DecodeGlobalType(d, &type, &isMutable)) - return false; - - InitExpr initializer; - if (!DecodeInitializerExpression(d, init->globals, type, &initializer)) - return false; - - if (!init->globals.append(GlobalDesc(initializer, isMutable))) - return false; - } - - if (!d.finishSection(sectionStart, sectionSize, "global")) - return false; - - return true; -} - -typedef HashSet CStringSet; - -static UniqueChars -DecodeExportName(Decoder& d, CStringSet* dupSet) -{ - UniqueChars exportName = DecodeName(d); - if (!exportName) { - d.fail("expected valid export name"); - return nullptr; - } - - CStringSet::AddPtr p = dupSet->lookupForAdd(exportName.get()); - if (p) { - d.fail("duplicate export"); - return nullptr; - } - - if (!dupSet->add(p, exportName.get())) - return nullptr; - - return Move(exportName); -} - -static bool -DecodeExport(Decoder& d, ModuleGenerator& mg, CStringSet* dupSet) -{ - UniqueChars fieldName = DecodeExportName(d, dupSet); - if (!fieldName) - return false; - - uint32_t exportKind; - if (!d.readVarU32(&exportKind)) - return d.fail("failed to read export kind"); - - switch (DefinitionKind(exportKind)) { - case DefinitionKind::Function: { - uint32_t funcIndex; - if (!d.readVarU32(&funcIndex)) - return d.fail("expected export internal index"); - - if (funcIndex >= mg.numFuncs()) - return d.fail("exported function index out of bounds"); - - return mg.addFuncExport(Move(fieldName), funcIndex); - } - case DefinitionKind::Table: { - uint32_t tableIndex; - if (!d.readVarU32(&tableIndex)) - return d.fail("expected table index"); - - if (tableIndex >= mg.tables().length()) - return d.fail("exported table index out of bounds"); - - return mg.addTableExport(Move(fieldName)); - } - case DefinitionKind::Memory: { - uint32_t memoryIndex; - if (!d.readVarU32(&memoryIndex)) - return d.fail("expected memory index"); - - if (memoryIndex > 0 || !mg.usesMemory()) - return d.fail("exported memory index out of bounds"); - - return mg.addMemoryExport(Move(fieldName)); - } - case DefinitionKind::Global: { - uint32_t globalIndex; - if (!d.readVarU32(&globalIndex)) - return d.fail("expected global index"); - - if (globalIndex >= mg.globals().length()) - return d.fail("exported global index out of bounds"); - - const GlobalDesc& global = mg.globals()[globalIndex]; - if (!GlobalIsJSCompatible(d, global.type(), global.isMutable())) - return false; - - return mg.addGlobalExport(Move(fieldName), globalIndex); - } - default: - return d.fail("unexpected export kind"); - } - - MOZ_CRASH("unreachable"); -} - -static bool -DecodeExportSection(Decoder& d, ModuleGenerator& mg) -{ - uint32_t sectionStart, sectionSize; - if (!d.startSection(SectionId::Export, §ionStart, §ionSize, "export")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - CStringSet dupSet; - if (!dupSet.init()) - return false; - - uint32_t numExports; - if (!d.readVarU32(&numExports)) - return d.fail("failed to read number of exports"); - - if (numExports > MaxExports) - return d.fail("too many exports"); - - for (uint32_t i = 0; i < numExports; i++) { - if (!DecodeExport(d, mg, &dupSet)) - return false; - } - - if (!d.finishSection(sectionStart, sectionSize, "export")) - return false; - - return true; -} - static bool DecodeFunctionBody(Decoder& d, ModuleGenerator& mg, uint32_t funcIndex) { @@ -706,37 +466,7 @@ DecodeFunctionBody(Decoder& d, ModuleGenerator& mg, uint32_t funcIndex) return mg.finishFuncDef(funcIndex, &fg); } -static bool -DecodeStartSection(Decoder& d, ModuleGenerator& mg) -{ - uint32_t sectionStart, sectionSize; - if (!d.startSection(SectionId::Start, §ionStart, §ionSize, "start")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - uint32_t funcIndex; - if (!d.readVarU32(&funcIndex)) - return d.fail("failed to read start func index"); - - if (funcIndex >= mg.numFuncs()) - return d.fail("unknown start function"); - - const Sig& sig = mg.funcSig(funcIndex); - if (!IsVoid(sig.ret())) - return d.fail("start function must not return anything"); - - if (sig.args().length()) - return d.fail("start function must be nullary"); - - if (!mg.setStartFunction(funcIndex)) - return false; - - if (!d.finishSection(sectionStart, sectionSize, "start")) - return false; - - return true; -} +// Section decoding. static bool DecodeCodeSection(Decoder& d, ModuleGenerator& mg) @@ -773,62 +503,8 @@ DecodeCodeSection(Decoder& d, ModuleGenerator& mg) return mg.finishFuncDefs(); } -static bool -DecodeElemSection(Decoder& d, ModuleGenerator& mg) -{ - uint32_t sectionStart, sectionSize; - if (!d.startSection(SectionId::Elem, §ionStart, §ionSize, "elem")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - uint32_t numSegments; - if (!d.readVarU32(&numSegments)) - return d.fail("failed to read number of elem segments"); - - if (numSegments > MaxElemSegments) - return d.fail("too many elem segments"); - - for (uint32_t i = 0; i < numSegments; i++) { - uint32_t tableIndex; - if (!d.readVarU32(&tableIndex)) - return d.fail("expected table index"); - - MOZ_ASSERT(mg.tables().length() <= 1); - if (tableIndex >= mg.tables().length()) - return d.fail("table index out of range"); - - InitExpr offset; - if (!DecodeInitializerExpression(d, mg.globals(), ValType::I32, &offset)) - return false; - - uint32_t numElems; - if (!d.readVarU32(&numElems)) - return d.fail("expected segment size"); - - Uint32Vector elemFuncIndices; - if (!elemFuncIndices.resize(numElems)) - return false; - - for (uint32_t i = 0; i < numElems; i++) { - if (!d.readVarU32(&elemFuncIndices[i])) - return d.fail("failed to read element function index"); - if (elemFuncIndices[i] >= mg.numFuncs()) - return d.fail("table element out of range"); - } - - if (!mg.addElemSegment(offset, Move(elemFuncIndices))) - return false; - } - - if (!d.finishSection(sectionStart, sectionSize, "elem")) - return false; - - return true; -} - static void -MaybeDecodeNameSectionBody(Decoder& d, ModuleGenerator& mg) +MaybeDecodeNameSectionBody(Decoder& d, NameInBytecodeVector* pfuncNames) { // For simplicity, ignore all failures, even OOM. Failure will simply result // in the names section not being included for this module. @@ -840,6 +516,8 @@ MaybeDecodeNameSectionBody(Decoder& d, ModuleGenerator& mg) if (numFuncNames > MaxFuncs) return; + // Use a local vector (and not pfuncNames) since it could result in a + // partially initialized result in case of failure in the middle. NameInBytecodeVector funcNames; if (!funcNames.resize(numFuncNames)) return; @@ -870,22 +548,11 @@ MaybeDecodeNameSectionBody(Decoder& d, ModuleGenerator& mg) } } - mg.setFuncNames(Move(funcNames)); + *pfuncNames = Move(funcNames); } static bool -DecodeDataSection(Decoder& d, ModuleGenerator& mg) -{ - DataSegmentVector dataSegments; - if (!DecodeDataSection(d, mg.usesMemory(), mg.minMemoryLength(), mg.globals(), &dataSegments)) - return false; - - mg.setDataSegments(Move(dataSegments)); - return true; -} - -static bool -DecodeNameSection(Decoder& d, ModuleGenerator& mg) +DecodeNameSection(Decoder& d, NameInBytecodeVector* funcNames) { uint32_t sectionStart, sectionSize; if (!d.startUserDefinedSection(NameSectionName, §ionStart, §ionSize)) @@ -895,7 +562,7 @@ DecodeNameSection(Decoder& d, ModuleGenerator& mg) // Once started, user-defined sections do not report validation errors. - MaybeDecodeNameSectionBody(d, mg); + MaybeDecodeNameSectionBody(d, funcNames); d.finishUserDefinedSection(sectionStart, sectionSize); return true; @@ -916,52 +583,26 @@ wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueCha Decoder d(bytecode.begin(), bytecode.end(), error); - auto init = js::MakeUnique(); - if (!init) + auto env = js::MakeUnique(); + if (!env) return nullptr; - if (!DecodePreamble(d)) + if (!DecodeModuleEnvironment(d, env.get())) return nullptr; - if (!DecodeTypeSection(d, &init->sigs)) - return nullptr; - - ImportVector imports; - if (!::DecodeImportSection(d, init.get(), &imports)) - return nullptr; - - if (!::DecodeFunctionSection(d, init.get())) - return nullptr; - - if (!DecodeTableSection(d, init.get())) - return nullptr; - - if (!::DecodeMemorySection(d, init.get())) - return nullptr; - - if (!DecodeGlobalSection(d, init.get())) - return nullptr; - - ModuleGenerator mg(Move(imports)); - if (!mg.init(Move(init), args)) - return nullptr; - - if (!DecodeExportSection(d, mg)) - return nullptr; - - if (!DecodeStartSection(d, mg)) - return nullptr; - - if (!DecodeElemSection(d, mg)) + ModuleGenerator mg; + if (!mg.init(Move(env), args)) return nullptr; if (!DecodeCodeSection(d, mg)) return nullptr; - if (!::DecodeDataSection(d, mg)) + DataSegmentVector dataSegments; + if (!DecodeDataSection(d, mg.env(), &dataSegments)) return nullptr; - if (!DecodeNameSection(d, mg)) + NameInBytecodeVector funcNames; + if (!DecodeNameSection(d, &funcNames)) return nullptr; if (!DecodeUnknownSections(d)) @@ -969,5 +610,7 @@ wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueCha MOZ_ASSERT(!*error, "unreported error in decoding"); - return mg.finish(bytecode); + return mg.finish(bytecode, + Move(dataSegments), + Move(funcNames)); } diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp index daff135077..422cf3a652 100644 --- a/js/src/wasm/WasmGenerator.cpp +++ b/js/src/wasm/WasmGenerator.cpp @@ -24,6 +24,7 @@ #include #include "wasm/WasmBaselineCompile.h" +#include "wasm/WasmCompile.h" #include "wasm/WasmIonCompile.h" #include "wasm/WasmStubs.h" @@ -43,9 +44,8 @@ static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024; static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024; static const uint32_t BAD_CODE_RANGE = UINT32_MAX; -ModuleGenerator::ModuleGenerator(ImportVector&& imports) +ModuleGenerator::ModuleGenerator() : alwaysBaseline_(false), - imports_(Move(imports)), numSigs_(0), numTables_(0), lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE), @@ -99,101 +99,122 @@ ModuleGenerator::~ModuleGenerator() } bool -ModuleGenerator::init(UniqueModuleGeneratorData shared, const CompileArgs& args, +ModuleGenerator::initAsmJS(Metadata* asmJSMetadata) +{ + MOZ_ASSERT(env_->isAsmJS()); + + metadata_ = asmJSMetadata; + MOZ_ASSERT(isAsmJS()); + + // For asm.js, the Vectors in ModuleEnvironment are max-sized reservations + // and will be initialized in a linear order via init* functions as the + // module is generated. + + MOZ_ASSERT(env_->sigs.length() == MaxSigs); + MOZ_ASSERT(env_->tables.length() == MaxTables); + MOZ_ASSERT(env_->asmJSSigToTableIndex.length() == MaxSigs); + + return true; +} + +bool +ModuleGenerator::initWasm() +{ + MOZ_ASSERT(!env_->isAsmJS()); + + metadata_ = js_new(); + if (!metadata_) + return false; + + MOZ_ASSERT(!isAsmJS()); + + // For wasm, the Vectors are correctly-sized and already initialized. + + numSigs_ = env_->sigs.length(); + numTables_ = env_->tables.length(); + + for (size_t i = 0; i < env_->funcImportGlobalDataOffsets.length(); i++) { + env_->funcImportGlobalDataOffsets[i] = linkData_.globalDataLength; + linkData_.globalDataLength += sizeof(FuncImportTls); + if (!addFuncImport(*env_->funcSigs[i], env_->funcImportGlobalDataOffsets[i])) + return false; + } + + for (TableDesc& table : env_->tables) { + if (!allocateGlobalBytes(sizeof(TableTls), sizeof(void*), &table.globalDataOffset)) + return false; + } + + for (uint32_t i = 0; i < numSigs_; i++) { + SigWithId& sig = env_->sigs[i]; + if (SigIdDesc::isGlobal(sig)) { + uint32_t globalDataOffset; + if (!allocateGlobalBytes(sizeof(void*), sizeof(void*), &globalDataOffset)) + return false; + + sig.id = SigIdDesc::global(sig, globalDataOffset); + + Sig copy; + if (!copy.clone(sig)) + return false; + + if (!metadata_->sigIds.emplaceBack(Move(copy), sig.id)) + return false; + } else { + sig.id = SigIdDesc::immediate(sig); + } + } + + for (GlobalDesc& global : env_->globals) { + if (global.isConstant()) + continue; + if (!allocateGlobal(&global)) + return false; + } + + for (const Export& exp : env_->exports) { + if (exp.kind() == DefinitionKind::Function) { + if (!exportedFuncs_.put(exp.funcIndex())) + return false; + } + } + + if (env_->startFuncIndex) { + metadata_->startFuncIndex.emplace(*env_->startFuncIndex); + if (!exportedFuncs_.put(*env_->startFuncIndex)) + return false; + } + + return true; +} + +bool +ModuleGenerator::init(UniqueModuleEnvironment env, const CompileArgs& args, Metadata* maybeAsmJSMetadata) { - shared_ = Move(shared); + env_ = Move(env); + + linkData_.globalDataLength = AlignBytes(InitialGlobalDataBytes, sizeof(void*)); + alwaysBaseline_ = args.alwaysBaseline; + if (!funcToCodeRange_.appendN(BAD_CODE_RANGE, env_->funcSigs.length())) + return false; + + if (!assumptions_.clone(args.assumptions)) + return false; if (!exportedFuncs_.init()) return false; - if (!funcToCodeRange_.appendN(BAD_CODE_RANGE, shared_->funcSigs.length())) + if (env_->isAsmJS() ? !initAsmJS(maybeAsmJSMetadata) : !initWasm()) return false; - linkData_.globalDataLength = AlignBytes(InitialGlobalDataBytes, sizeof(void*));; - - // asm.js passes in an AsmJSMetadata subclass to use instead. - if (maybeAsmJSMetadata) { - metadata_ = maybeAsmJSMetadata; - MOZ_ASSERT(isAsmJS()); - } else { - metadata_ = js_new(); - if (!metadata_) - return false; - MOZ_ASSERT(!isAsmJS()); - } - if (args.scriptedCaller.filename) { metadata_->filename = DuplicateString(args.scriptedCaller.filename.get()); if (!metadata_->filename) return false; } - if (!assumptions_.clone(args.assumptions)) - return false; - - // For asm.js, the Vectors in ModuleGeneratorData are max-sized reservations - // and will be initialized in a linear order via init* functions as the - // module is generated. For wasm, the Vectors are correctly-sized and - // already initialized. - - if (!isAsmJS()) { - numSigs_ = shared_->sigs.length(); - numTables_ = shared_->tables.length(); - - for (size_t i = 0; i < shared_->funcImportGlobalDataOffsets.length(); i++) { - shared_->funcImportGlobalDataOffsets[i] = linkData_.globalDataLength; - linkData_.globalDataLength += sizeof(FuncImportTls); - if (!addFuncImport(*shared_->funcSigs[i], shared_->funcImportGlobalDataOffsets[i])) - return false; - } - - for (const Import& import : imports_) { - if (import.kind == DefinitionKind::Table) { - MOZ_ASSERT(shared_->tables.length() == 1); - shared_->tables[0].external = true; - break; - } - } - - for (TableDesc& table : shared_->tables) { - if (!allocateGlobalBytes(sizeof(TableTls), sizeof(void*), &table.globalDataOffset)) - return false; - } - - for (uint32_t i = 0; i < numSigs_; i++) { - SigWithId& sig = shared_->sigs[i]; - if (SigIdDesc::isGlobal(sig)) { - uint32_t globalDataOffset; - if (!allocateGlobalBytes(sizeof(void*), sizeof(void*), &globalDataOffset)) - return false; - - sig.id = SigIdDesc::global(sig, globalDataOffset); - - Sig copy; - if (!copy.clone(sig)) - return false; - - if (!metadata_->sigIds.emplaceBack(Move(copy), sig.id)) - return false; - } else { - sig.id = SigIdDesc::immediate(sig); - } - } - - for (GlobalDesc& global : shared_->globals) { - if (global.isConstant()) - continue; - if (!allocateGlobal(&global)) - return false; - } - } else { - MOZ_ASSERT(shared_->sigs.length() == MaxSigs); - MOZ_ASSERT(shared_->tables.length() == MaxTables); - MOZ_ASSERT(shared_->asmJSSigToTableIndex.length() == MaxSigs); - } - return true; } @@ -407,8 +428,8 @@ ModuleGenerator::finishFuncExports() // In addition to all the functions that were explicitly exported, any // element of an exported table is also exported. - for (ElemSegment& elems : elemSegments_) { - if (shared_->tables[elems.tableIndex].external) { + for (ElemSegment& elems : env_->elemSegments) { + if (env_->tables[elems.tableIndex].external) { for (uint32_t funcIndex : elems.elemFuncIndices) { if (!exportedFuncs_.put(funcIndex)) return false; @@ -682,12 +703,20 @@ ModuleGenerator::addGlobal(ValType type, bool isConst, uint32_t* index) MOZ_ASSERT(isAsmJS()); MOZ_ASSERT(!startedFuncDefs_); - *index = shared_->globals.length(); + *index = env_->globals.length(); GlobalDesc global(type, !isConst, *index); if (!allocateGlobal(&global)) return false; - return shared_->globals.append(global); + return env_->globals.append(global); +} + +bool +ModuleGenerator::addExport(CacheableChars&& fieldName, uint32_t funcIndex) +{ + MOZ_ASSERT(isAsmJS()); + return env_->exports.emplaceBack(Move(fieldName), funcIndex, DefinitionKind::Function) && + exportedFuncs_.put(funcIndex); } void @@ -697,42 +726,42 @@ ModuleGenerator::initSig(uint32_t sigIndex, Sig&& sig) MOZ_ASSERT(sigIndex == numSigs_); numSigs_++; - MOZ_ASSERT(shared_->sigs[sigIndex] == Sig()); - shared_->sigs[sigIndex] = Move(sig); + MOZ_ASSERT(env_->sigs[sigIndex] == Sig()); + env_->sigs[sigIndex] = Move(sig); } const SigWithId& ModuleGenerator::sig(uint32_t index) const { MOZ_ASSERT(index < numSigs_); - return shared_->sigs[index]; + return env_->sigs[index]; } void ModuleGenerator::initFuncSig(uint32_t funcIndex, uint32_t sigIndex) { MOZ_ASSERT(isAsmJS()); - MOZ_ASSERT(!shared_->funcSigs[funcIndex]); + MOZ_ASSERT(!env_->funcSigs[funcIndex]); - shared_->funcSigs[funcIndex] = &shared_->sigs[sigIndex]; + env_->funcSigs[funcIndex] = &env_->sigs[sigIndex]; } void ModuleGenerator::initMemoryUsage(MemoryUsage memoryUsage) { MOZ_ASSERT(isAsmJS()); - MOZ_ASSERT(shared_->memoryUsage == MemoryUsage::None); + MOZ_ASSERT(env_->memoryUsage == MemoryUsage::None); - shared_->memoryUsage = memoryUsage; + env_->memoryUsage = memoryUsage; } void ModuleGenerator::bumpMinMemoryLength(uint32_t newMinMemoryLength) { MOZ_ASSERT(isAsmJS()); - MOZ_ASSERT(newMinMemoryLength >= shared_->minMemoryLength); + MOZ_ASSERT(newMinMemoryLength >= env_->minMemoryLength); - shared_->minMemoryLength = newMinMemoryLength; + env_->minMemoryLength = newMinMemoryLength; } bool @@ -740,15 +769,15 @@ ModuleGenerator::initImport(uint32_t funcIndex, uint32_t sigIndex) { MOZ_ASSERT(isAsmJS()); - MOZ_ASSERT(!shared_->funcSigs[funcIndex]); - shared_->funcSigs[funcIndex] = &shared_->sigs[sigIndex]; + MOZ_ASSERT(!env_->funcSigs[funcIndex]); + env_->funcSigs[funcIndex] = &env_->sigs[sigIndex]; uint32_t globalDataOffset; if (!allocateGlobalBytes(sizeof(FuncImportTls), sizeof(void*), &globalDataOffset)) return false; - MOZ_ASSERT(!shared_->funcImportGlobalDataOffsets[funcIndex]); - shared_->funcImportGlobalDataOffsets[funcIndex] = globalDataOffset; + MOZ_ASSERT(!env_->funcImportGlobalDataOffsets[funcIndex]); + env_->funcImportGlobalDataOffsets[funcIndex] = globalDataOffset; MOZ_ASSERT(funcIndex == metadata_->funcImports.length()); return addFuncImport(sig(sigIndex), globalDataOffset); @@ -769,7 +798,7 @@ ModuleGenerator::numFuncDefs() const // asm.js overallocates the length of funcSigs and in general does not know // the number of function definitions until it's done compiling. MOZ_ASSERT(!isAsmJS()); - return shared_->funcSigs.length() - numFuncImports(); + return env_->funcSigs.length() - numFuncImports(); } uint32_t @@ -781,73 +810,14 @@ ModuleGenerator::numFuncs() const // exact number of imports/definitions encountered so far) and to simplify // things we simply only define this quantity for wasm. MOZ_ASSERT(!isAsmJS()); - return shared_->funcSigs.length(); + return env_->funcSigs.length(); } const SigWithId& ModuleGenerator::funcSig(uint32_t funcIndex) const { - MOZ_ASSERT(shared_->funcSigs[funcIndex]); - return *shared_->funcSigs[funcIndex]; -} - -bool -ModuleGenerator::addFuncExport(UniqueChars fieldName, uint32_t funcIndex) -{ - return exportedFuncs_.put(funcIndex) && - exports_.emplaceBack(Move(fieldName), funcIndex, DefinitionKind::Function); -} - -bool -ModuleGenerator::addTableExport(UniqueChars fieldName) -{ - MOZ_ASSERT(!startedFuncDefs_); - MOZ_ASSERT(shared_->tables.length() == 1); - shared_->tables[0].external = true; - return exports_.emplaceBack(Move(fieldName), DefinitionKind::Table); -} - -bool -ModuleGenerator::addMemoryExport(UniqueChars fieldName) -{ - return exports_.emplaceBack(Move(fieldName), DefinitionKind::Memory); -} - -bool -ModuleGenerator::addGlobalExport(UniqueChars fieldName, uint32_t globalIndex) -{ - return exports_.emplaceBack(Move(fieldName), globalIndex, DefinitionKind::Global); -} - -bool -ModuleGenerator::setStartFunction(uint32_t funcIndex) -{ - metadata_->startFuncIndex.emplace(funcIndex); - return exportedFuncs_.put(funcIndex); -} - -bool -ModuleGenerator::addElemSegment(InitExpr offset, Uint32Vector&& elemFuncIndices) -{ - MOZ_ASSERT(!isAsmJS()); - MOZ_ASSERT(!startedFuncDefs_); - MOZ_ASSERT(shared_->tables.length() == 1); - - for (uint32_t funcIndex : elemFuncIndices) { - if (funcIndex < numFuncImports()) { - shared_->tables[0].external = true; - break; - } - } - - return elemSegments_.emplaceBack(0, offset, Move(elemFuncIndices)); -} - -void -ModuleGenerator::setDataSegments(DataSegmentVector&& segments) -{ - MOZ_ASSERT(dataSegments_.empty()); - dataSegments_ = Move(segments); + MOZ_ASSERT(env_->funcSigs[funcIndex]); + return *env_->funcSigs[funcIndex]; } bool @@ -889,7 +859,7 @@ ModuleGenerator::startFuncDefs() if (!tasks_.initCapacity(numTasks)) return false; for (size_t i = 0; i < numTasks; i++) - tasks_.infallibleEmplaceBack(*shared_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE); + tasks_.infallibleEmplaceBack(*env_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE); if (!freeTasks_.reserve(numTasks)) return false; @@ -1016,7 +986,7 @@ ModuleGenerator::finishFuncDefs() // Complete element segments with the code range index of every element, now // that all functions have been compiled. - for (ElemSegment& elems : elemSegments_) { + for (ElemSegment& elems : env_->elemSegments) { Uint32Vector& codeRangeIndices = elems.elemCodeRangeIndices; MOZ_ASSERT(codeRangeIndices.empty()); @@ -1030,13 +1000,6 @@ ModuleGenerator::finishFuncDefs() return true; } -void -ModuleGenerator::setFuncNames(NameInBytecodeVector&& funcNames) -{ - MOZ_ASSERT(metadata_->funcNames.empty()); - metadata_->funcNames = Move(funcNames); -} - bool ModuleGenerator::initSigTableLength(uint32_t sigIndex, uint32_t length) { @@ -1044,10 +1007,10 @@ ModuleGenerator::initSigTableLength(uint32_t sigIndex, uint32_t length) MOZ_ASSERT(length != 0); MOZ_ASSERT(length <= MaxTableElems); - MOZ_ASSERT(shared_->asmJSSigToTableIndex[sigIndex] == 0); - shared_->asmJSSigToTableIndex[sigIndex] = numTables_; + MOZ_ASSERT(env_->asmJSSigToTableIndex[sigIndex] == 0); + env_->asmJSSigToTableIndex[sigIndex] = numTables_; - TableDesc& table = shared_->tables[numTables_++]; + TableDesc& table = env_->tables[numTables_++]; table.kind = TableKind::TypedFunction; table.limits.initial = length; table.limits.maximum = Some(length); @@ -1060,8 +1023,8 @@ ModuleGenerator::initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncInd MOZ_ASSERT(isAsmJS()); MOZ_ASSERT(finishedFuncDefs_); - uint32_t tableIndex = shared_->asmJSSigToTableIndex[sigIndex]; - MOZ_ASSERT(shared_->tables[tableIndex].limits.initial == elemFuncIndices.length()); + uint32_t tableIndex = env_->asmJSSigToTableIndex[sigIndex]; + MOZ_ASSERT(env_->tables[tableIndex].limits.initial == elemFuncIndices.length()); Uint32Vector codeRangeIndices; if (!codeRangeIndices.resize(elemFuncIndices.length())) @@ -1070,15 +1033,16 @@ ModuleGenerator::initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncInd codeRangeIndices[i] = funcToCodeRange_[elemFuncIndices[i]]; InitExpr offset(Val(uint32_t(0))); - if (!elemSegments_.emplaceBack(tableIndex, offset, Move(elemFuncIndices))) + if (!env_->elemSegments.emplaceBack(tableIndex, offset, Move(elemFuncIndices))) return false; - elemSegments_.back().elemCodeRangeIndices = Move(codeRangeIndices); + env_->elemSegments.back().elemCodeRangeIndices = Move(codeRangeIndices); return true; } SharedModule -ModuleGenerator::finish(const ShareableBytes& bytecode) +ModuleGenerator::finish(const ShareableBytes& bytecode, DataSegmentVector&& dataSegments, + NameInBytecodeVector&& funcNames) { MOZ_ASSERT(!activeFuncDef_); MOZ_ASSERT(finishedFuncDefs_); @@ -1115,17 +1079,19 @@ ModuleGenerator::finish(const ShareableBytes& bytecode) if (!metadata_->callSites.appendAll(masm_.callSites())) return nullptr; + metadata_->funcNames = Move(funcNames); + // The MacroAssembler has accumulated all the memory accesses during codegen. metadata_->memoryAccesses = masm_.extractMemoryAccesses(); metadata_->memoryPatches = masm_.extractMemoryPatches(); metadata_->boundsChecks = masm_.extractBoundsChecks(); - // Copy over data from the ModuleGeneratorData. - metadata_->memoryUsage = shared_->memoryUsage; - metadata_->minMemoryLength = shared_->minMemoryLength; - metadata_->maxMemoryLength = shared_->maxMemoryLength; - metadata_->tables = Move(shared_->tables); - metadata_->globals = Move(shared_->globals); + // Copy over data from the ModuleEnvironment. + metadata_->memoryUsage = env_->memoryUsage; + metadata_->minMemoryLength = env_->minMemoryLength; + metadata_->maxMemoryLength = env_->maxMemoryLength; + metadata_->tables = Move(env_->tables); + metadata_->globals = Move(env_->globals); // These Vectors can get large and the excess capacity can be significant, // so realloc them down to size. @@ -1156,10 +1122,10 @@ ModuleGenerator::finish(const ShareableBytes& bytecode) return SharedModule(js_new(Move(assumptions_), Move(code), Move(linkData_), - Move(imports_), - Move(exports_), - Move(dataSegments_), - Move(elemSegments_), + Move(env_->imports), + Move(env_->exports), + Move(dataSegments), + Move(env_->elemSegments), *metadata_, bytecode)); } diff --git a/js/src/wasm/WasmGenerator.h b/js/src/wasm/WasmGenerator.h index 85b23664a2..9fa5863d1e 100644 --- a/js/src/wasm/WasmGenerator.h +++ b/js/src/wasm/WasmGenerator.h @@ -19,50 +19,176 @@ #define wasm_generator_h #include "jit/MacroAssembler.h" -#include "wasm/WasmCompile.h" +#include "wasm/WasmBinaryFormat.h" #include "wasm/WasmModule.h" namespace js { namespace wasm { +struct ModuleEnvironment; + +typedef Vector MIRTypeVector; +typedef jit::ABIArgIter ABIArgMIRTypeIter; +typedef jit::ABIArgIter ABIArgValTypeIter; + +struct CompileArgs; + class FunctionGenerator; -// The ModuleGeneratorData holds all the state shared between the -// ModuleGenerator thread and background compile threads. The background -// threads are given a read-only view of the ModuleGeneratorData and the -// ModuleGenerator is careful to initialize, and never subsequently mutate, -// any given datum before being read by a background thread. In particular, -// once created, the Vectors are never resized. +// The FuncBytes class represents a single, concurrently-compilable function. +// A FuncBytes object is composed of the wasm function body bytes along with the +// ambient metadata describing the function necessary to compile it. -struct ModuleGeneratorData +class FuncBytes { - ModuleKind kind; - MemoryUsage memoryUsage; - mozilla::Atomic minMemoryLength; - Maybe maxMemoryLength; + Bytes bytes_; + uint32_t index_; + const SigWithId* sig_; + uint32_t lineOrBytecode_; + Uint32Vector callSiteLineNums_; - SigWithIdVector sigs; - SigWithIdPtrVector funcSigs; - Uint32Vector funcImportGlobalDataOffsets; - GlobalDescVector globals; - TableDescVector tables; - Uint32Vector asmJSSigToTableIndex; - - explicit ModuleGeneratorData(ModuleKind kind = ModuleKind::Wasm) - : kind(kind), - memoryUsage(MemoryUsage::None), - minMemoryLength(0) + public: + FuncBytes() + : index_(UINT32_MAX), + sig_(nullptr), + lineOrBytecode_(UINT32_MAX) {} - bool isAsmJS() const { - return kind == ModuleKind::AsmJS; + Bytes& bytes() { + return bytes_; } - bool funcIsImport(uint32_t funcIndex) const { - return funcIndex < funcImportGlobalDataOffsets.length(); + [[nodiscard]] bool addCallSiteLineNum(uint32_t lineno) { + return callSiteLineNums_.append(lineno); + } + void setLineOrBytecode(uint32_t lineOrBytecode) { + MOZ_ASSERT(lineOrBytecode_ == UINT32_MAX); + lineOrBytecode_ = lineOrBytecode; + } + void setFunc(uint32_t index, const SigWithId* sig) { + MOZ_ASSERT(index_ == UINT32_MAX); + MOZ_ASSERT(sig_ == nullptr); + index_ = index; + sig_ = sig; + } + void reset() { + bytes_.clear(); + index_ = UINT32_MAX; + sig_ = nullptr; + lineOrBytecode_ = UINT32_MAX; + callSiteLineNums_.clear(); + } + + const Bytes& bytes() const { return bytes_; } + uint32_t index() const { return index_; } + const SigWithId& sig() const { return *sig_; } + uint32_t lineOrBytecode() const { return lineOrBytecode_; } + const Uint32Vector& callSiteLineNums() const { return callSiteLineNums_; } +}; + +typedef UniquePtr UniqueFuncBytes; +typedef Vector UniqueFuncBytesVector; + +enum class CompileMode +{ + Baseline, + Ion +}; + +// FuncCompileUnit contains all the data necessary to produce and store the +// results of a single function's compilation. + +class FuncCompileUnit +{ + UniqueFuncBytes func_; + CompileMode mode_; + FuncOffsets offsets_; + + DebugOnly finished_; + + public: + FuncCompileUnit(UniqueFuncBytes func, CompileMode mode) + : func_(Move(func)), + mode_(mode), + finished_(false) + {} + + const FuncBytes& func() const { return *func_; } + CompileMode mode() const { return mode_; } + FuncOffsets offsets() const { MOZ_ASSERT(finished_); return offsets_; } + + void finish(FuncOffsets offsets) { + MOZ_ASSERT(!finished_); + offsets_ = offsets; + finished_ = true; + } + + UniqueFuncBytes recycle() { + MOZ_ASSERT(finished_); + func_->reset(); + return Move(func_); } }; -typedef UniquePtr UniqueModuleGeneratorData; +typedef Vector FuncCompileUnitVector; + +// A CompileTask represents the task of compiling a batch of functions. It is +// filled with a certain number of function's bodies that are sent off to a +// compilation helper thread, which fills in the resulting code offsets, and +// finally sent back to the validation thread. To save time allocating and +// freeing memory, CompileTasks are reset() and reused. + +class CompileTask +{ + const ModuleEnvironment& env_; + LifoAlloc lifo_; + Maybe alloc_; + Maybe masm_; + FuncCompileUnitVector units_; + + CompileTask(const CompileTask&) = delete; + CompileTask& operator=(const CompileTask&) = delete; + + void init() { + alloc_.emplace(&lifo_); + masm_.emplace(jit::MacroAssembler::WasmToken(), *alloc_); + } + + public: + CompileTask(const ModuleEnvironment& env, size_t defaultChunkSize) + : env_(env), + lifo_(defaultChunkSize) + { + init(); + } + LifoAlloc& lifo() { + return lifo_; + } + jit::TempAllocator& alloc() { + return *alloc_; + } + const ModuleEnvironment& env() const { + return env_; + } + jit::MacroAssembler& masm() { + return *masm_; + } + FuncCompileUnitVector& units() { + return units_; + } + bool reset(UniqueFuncBytesVector* freeFuncBytes) { + for (FuncCompileUnit& unit : units_) { + if (!freeFuncBytes->emplaceBack(Move(unit.recycle()))) + return false; + } + units_.clear(); + masm_.reset(); + alloc_.reset(); + lifo_.releaseAll(); + + init(); + return true; + } +}; // A ModuleGenerator encapsulates the creation of a wasm module. During the // lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created @@ -84,13 +210,9 @@ class MOZ_STACK_CLASS ModuleGenerator Assumptions assumptions_; LinkData linkData_; MutableMetadata metadata_; - ExportVector exports_; - ImportVector imports_; - DataSegmentVector dataSegments_; - ElemSegmentVector elemSegments_; // Data scoped to the ModuleGenerator's lifetime - UniqueModuleGeneratorData shared_; + UniqueModuleEnvironment env_; uint32_t numSigs_; uint32_t numTables_; LifoAlloc lifo_; @@ -116,70 +238,65 @@ class MOZ_STACK_CLASS ModuleGenerator bool funcIsCompiled(uint32_t funcIndex) const; const CodeRange& funcCodeRange(uint32_t funcIndex) const; - MOZ_MUST_USE bool patchCallSites(TrapExitOffsetArray* maybeTrapExits = nullptr); - MOZ_MUST_USE bool patchFarJumps(const TrapExitOffsetArray& trapExits); - MOZ_MUST_USE bool finishTask(IonCompileTask* task); - MOZ_MUST_USE bool finishOutstandingTask(); - MOZ_MUST_USE bool finishFuncExports(); - MOZ_MUST_USE bool finishCodegen(); - MOZ_MUST_USE bool finishLinkData(Bytes& code); - 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); +public: + uint32_t numFuncImports() const; +private: + [[nodiscard]] bool patchCallSites(TrapExitOffsetArray* maybeTrapExits = nullptr); + [[nodiscard]] bool patchFarJumps(const TrapExitOffsetArray& trapExits); + [[nodiscard]] bool finishTask(CompileTask* task); + [[nodiscard]] bool finishOutstandingTask(); + [[nodiscard]] bool finishFuncExports(); + [[nodiscard]] bool finishCodegen(); + [[nodiscard]] bool finishLinkData(Bytes& code); + [[nodiscard]] bool addFuncImport(const Sig& sig, uint32_t globalDataOffset); + [[nodiscard]] bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff); + [[nodiscard]] bool allocateGlobal(GlobalDesc* global); + + [[nodiscard]] bool initAsmJS(Metadata* asmJSMetadata); + [[nodiscard]] bool initWasm(); + + [[nodiscard]] bool launchBatchCompile(); public: - explicit ModuleGenerator(ImportVector&& imports); + explicit ModuleGenerator(); ~ModuleGenerator(); - MOZ_MUST_USE bool init(UniqueModuleGeneratorData shared, const CompileArgs& args, + [[nodiscard]] bool init(UniqueModuleEnvironment env, const CompileArgs& args, Metadata* maybeAsmJSMetadata = nullptr); + const ModuleEnvironment& env() const { return *env_; } + bool isAsmJS() const { return metadata_->kind == ModuleKind::AsmJS; } jit::MacroAssembler& masm() { return masm_; } // Memory: - bool usesMemory() const { return UsesMemory(shared_->memoryUsage); } - uint32_t minMemoryLength() const { return shared_->minMemoryLength; } + bool usesMemory() const { return env_->usesMemory(); } + uint32_t minMemoryLength() const { return env_->minMemoryLength; } // Tables: uint32_t numTables() const { return numTables_; } - const TableDescVector& tables() const { return shared_->tables; } + const TableDescVector& tables() const { return env_->tables; } // Signatures: uint32_t numSigs() const { return numSigs_; } const SigWithId& sig(uint32_t sigIndex) const; const SigWithId& funcSig(uint32_t funcIndex) const; + const SigWithIdPtrVector& funcSigs() const { return env_->funcSigs; } // Globals: - const GlobalDescVector& globals() const { return shared_->globals; } + const GlobalDescVector& globals() const { return env_->globals; } // Functions declarations: uint32_t numFuncImports() const; uint32_t numFuncDefs() const; uint32_t numFuncs() const; - // Exports: - MOZ_MUST_USE bool addFuncExport(UniqueChars fieldName, uint32_t funcIndex); - MOZ_MUST_USE bool addTableExport(UniqueChars fieldName); - MOZ_MUST_USE bool addMemoryExport(UniqueChars fieldName); - MOZ_MUST_USE bool addGlobalExport(UniqueChars fieldName, uint32_t globalIndex); - // Function definitions: MOZ_MUST_USE bool startFuncDefs(); MOZ_MUST_USE bool startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg); MOZ_MUST_USE bool finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg); MOZ_MUST_USE bool finishFuncDefs(); - // Start function: - bool setStartFunction(uint32_t funcIndex); - - // Segments: - void setDataSegments(DataSegmentVector&& segments); - MOZ_MUST_USE bool addElemSegment(InitExpr offset, Uint32Vector&& elemFuncIndices); - - // Function names: - void setFuncNames(NameInBytecodeVector&& funcNames); - // asm.js lazy initialization: void initSig(uint32_t sigIndex, Sig&& sig); void initFuncSig(uint32_t funcIndex, uint32_t sigIndex); @@ -188,12 +305,14 @@ class MOZ_STACK_CLASS ModuleGenerator MOZ_MUST_USE bool initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices); void initMemoryUsage(MemoryUsage memoryUsage); void bumpMinMemoryLength(uint32_t newMinMemoryLength); - MOZ_MUST_USE bool addGlobal(ValType type, bool isConst, uint32_t* index); + [[nodiscard]] bool addGlobal(ValType type, bool isConst, uint32_t* index); + [[nodiscard]] bool addExport(CacheableChars&& fieldChars, uint32_t funcIndex); // Finish compilation, provided the list of imports and source bytecode. // Both these Vectors may be empty (viz., b/c asm.js does different things // for imports and source). - SharedModule finish(const ShareableBytes& bytecode); + SharedModule finish(const ShareableBytes& bytecode, DataSegmentVector&& dataSegments, + NameInBytecodeVector&& funcNames); }; // A FunctionGenerator encapsulates the generation of a single function body. diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index 34ea7a22ba..8c31274b83 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -147,7 +147,7 @@ class FunctionCompiler typedef Vector ControlFlowPatchsVector; typedef Vector CallCompileStateVector; - const ModuleGeneratorData& mg_; + const ModuleEnvironment& env_; IonOpIter iter_; const FuncBytes& func_; const ValTypeVector& locals_; @@ -172,13 +172,12 @@ class FunctionCompiler MWasmParameter* tlsPointer_; public: - FunctionCompiler(const ModuleGeneratorData& mg, + FunctionCompiler(const ModuleEnvironment& env, Decoder& decoder, const FuncBytes& func, const ValTypeVector& locals, - MIRGenerator& mirGen, - FuncCompileResults& compileResults) - : mg_(mg), + MIRGenerator& mirGen) + : env_(env), iter_(decoder, func.lineOrBytecode()), func_(func), locals_(locals), @@ -195,7 +194,7 @@ class FunctionCompiler tlsPointer_(nullptr) {} - const ModuleGeneratorData& mg() const { return mg_; } + const ModuleEnvironment& env() const { return env_; } IonOpIter& iter() { return iter_; } TempAllocator& alloc() const { return alloc_; } MacroAssembler& masm() const { return compileResults_.masm(); } @@ -205,7 +204,7 @@ class FunctionCompiler return iter_.trapOffset(); } Maybe trapIfNotAsmJS() const { - return mg_.isAsmJS() ? Nothing() : Some(iter_.trapOffset()); + return env_.isAsmJS() ? Nothing() : Some(iter_.trapOffset()); } bool init() @@ -372,7 +371,7 @@ class FunctionCompiler bool mustPreserveNaN(MIRType type) { - return IsFloatingPointType(type) && mg().kind == ModuleKind::Wasm; + return IsFloatingPointType(type) && !env().isAsmJS(); } MDefinition* sub(MDefinition* lhs, MDefinition* rhs, MIRType type) @@ -417,7 +416,7 @@ class FunctionCompiler { if (inDeadCode()) return nullptr; - bool trapOnError = !mg().isAsmJS(); + bool trapOnError = !env().isAsmJS(); auto* ins = MDiv::New(alloc(), lhs, rhs, type, unsignd, trapOnError, trapOffset(), mustPreserveNaN(type)); curBlock_->add(ins); @@ -428,7 +427,7 @@ class FunctionCompiler { if (inDeadCode()) return nullptr; - bool trapOnError = !mg().isAsmJS(); + bool trapOnError = !env().isAsmJS(); auto* ins = MMod::New(alloc(), lhs, rhs, type, unsignd, trapOnError, trapOffset()); curBlock_->add(ins); return ins; @@ -811,12 +810,12 @@ class FunctionCompiler return true; } - const SigWithId& sig = mg_.sigs[sigIndex]; + const SigWithId& sig = env_.sigs[sigIndex]; CalleeDesc callee; - if (mg_.isAsmJS()) { + if (env_.isAsmJS()) { MOZ_ASSERT(sig.id.kind() == SigIdDesc::Kind::None); - const TableDesc& table = mg_.tables[mg_.asmJSSigToTableIndex[sigIndex]]; + const TableDesc& table = env_.tables[env_.asmJSSigToTableIndex[sigIndex]]; MOZ_ASSERT(IsPowerOfTwo(table.limits.initial)); MOZ_ASSERT(!table.external); MOZ_ASSERT(call.tlsStackOffset_ == MWasmCall::DontSaveTls); @@ -830,8 +829,8 @@ class FunctionCompiler callee = CalleeDesc::asmJSTable(table); } else { MOZ_ASSERT(sig.id.kind() != SigIdDesc::Kind::None); - MOZ_ASSERT(mg_.tables.length() == 1); - const TableDesc& table = mg_.tables[0]; + MOZ_ASSERT(env_.tables.length() == 1); + const TableDesc& table = env_.tables[0]; MOZ_ASSERT(table.external == (call.tlsStackOffset_ != MWasmCall::DontSaveTls)); callee = CalleeDesc::wasmTable(table, sig.id); @@ -1683,8 +1682,8 @@ EmitCall(FunctionCompiler& f) if (f.inDeadCode()) return true; - const Sig& sig = *f.mg().funcSigs[funcIndex]; - bool import = f.mg().funcIsImport(funcIndex); + const Sig& sig = *f.env().funcSigs[funcIndex]; + bool import = f.env().funcIsImport(funcIndex); CallCompileState call(f, lineOrBytecode); if (!EmitCallArgs(f, sig, import ? TlsUsage::CallerSaved : TlsUsage::Need, &call)) @@ -1695,7 +1694,7 @@ EmitCall(FunctionCompiler& f) MDefinition* def; if (import) { - uint32_t globalDataOffset = f.mg().funcImportGlobalDataOffsets[funcIndex]; + uint32_t globalDataOffset = f.env().funcImportGlobalDataOffsets[funcIndex]; if (!f.callImport(globalDataOffset, call, sig.ret(), &def)) return false; } else { @@ -1728,9 +1727,9 @@ EmitCallIndirect(FunctionCompiler& f, bool oldStyle) if (f.inDeadCode()) return true; - const Sig& sig = f.mg().sigs[sigIndex]; + const Sig& sig = f.env().sigs[sigIndex]; - TlsUsage tls = !f.mg().isAsmJS() && f.mg().tables[0].external + TlsUsage tls = !f.env().isAsmJS() && f.env().tables[0].external ? TlsUsage::CallerSaved : TlsUsage::Need; @@ -1796,10 +1795,10 @@ static bool EmitGetGlobal(FunctionCompiler& f) { uint32_t id; - if (!f.iter().readGetGlobal(f.mg().globals, &id)) + if (!f.iter().readGetGlobal(f.env().globals, &id)) return false; - const GlobalDesc& global = f.mg().globals[id]; + const GlobalDesc& global = f.env().globals[id]; if (!global.isConstant()) { f.iter().setResult(f.loadGlobalVar(global.offset(), !global.isMutable(), ToMIRType(global.type()))); @@ -1836,10 +1835,10 @@ EmitSetGlobal(FunctionCompiler& f) { uint32_t id; MDefinition* value; - if (!f.iter().readSetGlobal(f.mg().globals, &id, &value)) + if (!f.iter().readSetGlobal(f.env().globals, &id, &value)) return false; - const GlobalDesc& global = f.mg().globals[id]; + const GlobalDesc& global = f.env().globals[id]; MOZ_ASSERT(global.isMutable()); f.storeGlobalVar(global.offset(), value); @@ -1851,10 +1850,10 @@ EmitTeeGlobal(FunctionCompiler& f) { uint32_t id; MDefinition* value; - if (!f.iter().readTeeGlobal(f.mg().globals, &id, &value)) + if (!f.iter().readTeeGlobal(f.env().globals, &id, &value)) return false; - const GlobalDesc& global = f.mg().globals[id]; + const GlobalDesc& global = f.env().globals[id]; MOZ_ASSERT(global.isMutable()); f.storeGlobalVar(global.offset(), value); @@ -1919,13 +1918,13 @@ EmitTruncate(FunctionCompiler& f, ValType operandType, ValType resultType, return false; if (resultType == ValType::I32) { - if (f.mg().isAsmJS()) + if (f.env().isAsmJS()) f.iter().setResult(f.unary(input)); else f.iter().setResult(f.truncate(input, isUnsigned)); } else { MOZ_ASSERT(resultType == ValType::I64); - MOZ_ASSERT(!f.mg().isAsmJS()); + MOZ_ASSERT(!f.env().isAsmJS()); f.iter().setResult(f.truncate(input, isUnsigned)); } return true; @@ -2862,8 +2861,8 @@ wasm::IonCompileFunction(IonCompileTask* task) { MOZ_ASSERT(task->mode() == IonCompileTask::CompileMode::Ion); - const FuncBytes& func = task->func(); - FuncCompileResults& results = task->results(); + const FuncBytes& func = unit->func(); + const ModuleEnvironment& env = task->env(); Decoder d(func.bytes()); @@ -2872,7 +2871,7 @@ wasm::IonCompileFunction(IonCompileTask* task) ValTypeVector locals; if (!locals.appendAll(func.sig().args())) return false; - if (!DecodeLocalEntries(d, task->mg().kind, &locals)) + if (!DecodeLocalEntries(d, env.kind, &locals)) return false; // Set up for Ion compilation. @@ -2883,7 +2882,7 @@ wasm::IonCompileFunction(IonCompileTask* task) CompileInfo compileInfo(locals.length()); MIRGenerator mir(nullptr, options, &results.alloc(), &graph, &compileInfo, IonOptimizations.get(OptimizationLevel::Wasm)); - mir.initMinWasmHeapLength(task->mg().minMemoryLength); + mir.initMinWasmHeapLength(env.minMemoryLength); // Capture the prologue's trap site before decoding the function. @@ -2891,7 +2890,7 @@ wasm::IonCompileFunction(IonCompileTask* task) // Build MIR graph { - FunctionCompiler f(task->mg(), d, func, locals, mir, results); + FunctionCompiler f(env, d, func, locals, mir); if (!f.init()) return false; @@ -2931,7 +2930,7 @@ wasm::IonCompileFunction(IonCompileTask* task) if (!lir) return false; - SigIdDesc sigId = task->mg().funcSigs[func.index()]->id; + SigIdDesc sigId = env.funcSigs[func.index()]->id; CodeGenerator codegen(&mir, lir, &results.masm()); if (!codegen.generateWasm(sigId, prologueTrapOffset, &results.offsets())) diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index 09cd1c486b..9ea336396f 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -138,132 +138,6 @@ LinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const symbolicLinks.sizeOfExcludingThis(mallocSizeOf); } -size_t -Import::serializedSize() const -{ - return module.serializedSize() + - field.serializedSize() + - sizeof(kind); -} - -uint8_t* -Import::serialize(uint8_t* cursor) const -{ - cursor = module.serialize(cursor); - cursor = field.serialize(cursor); - cursor = WriteScalar(cursor, kind); - return cursor; -} - -const uint8_t* -Import::deserialize(const uint8_t* cursor) -{ - (cursor = module.deserialize(cursor)) && - (cursor = field.deserialize(cursor)) && - (cursor = ReadScalar(cursor, &kind)); - return cursor; -} - -size_t -Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const -{ - return module.sizeOfExcludingThis(mallocSizeOf) + - field.sizeOfExcludingThis(mallocSizeOf); -} - -Export::Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind) - : fieldName_(Move(fieldName)) -{ - pod.kind_ = kind; - pod.index_ = index; -} - -Export::Export(UniqueChars fieldName, DefinitionKind kind) - : fieldName_(Move(fieldName)) -{ - pod.kind_ = kind; - pod.index_ = 0; -} - -uint32_t -Export::funcIndex() const -{ - MOZ_ASSERT(pod.kind_ == DefinitionKind::Function); - return pod.index_; -} - -uint32_t -Export::globalIndex() const -{ - MOZ_ASSERT(pod.kind_ == DefinitionKind::Global); - return pod.index_; -} - -size_t -Export::serializedSize() const -{ - return fieldName_.serializedSize() + - sizeof(pod); -} - -uint8_t* -Export::serialize(uint8_t* cursor) const -{ - cursor = fieldName_.serialize(cursor); - cursor = WriteBytes(cursor, &pod, sizeof(pod)); - return cursor; -} - -const uint8_t* -Export::deserialize(const uint8_t* cursor) -{ - (cursor = fieldName_.deserialize(cursor)) && - (cursor = ReadBytes(cursor, &pod, sizeof(pod))); - return cursor; -} - -size_t -Export::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const -{ - return fieldName_.sizeOfExcludingThis(mallocSizeOf); -} - -size_t -ElemSegment::serializedSize() const -{ - return sizeof(tableIndex) + - sizeof(offset) + - SerializedPodVectorSize(elemFuncIndices) + - SerializedPodVectorSize(elemCodeRangeIndices); -} - -uint8_t* -ElemSegment::serialize(uint8_t* cursor) const -{ - cursor = WriteBytes(cursor, &tableIndex, sizeof(tableIndex)); - cursor = WriteBytes(cursor, &offset, sizeof(offset)); - cursor = SerializePodVector(cursor, elemFuncIndices); - cursor = SerializePodVector(cursor, elemCodeRangeIndices); - return cursor; -} - -const uint8_t* -ElemSegment::deserialize(const uint8_t* cursor) -{ - (cursor = ReadBytes(cursor, &tableIndex, sizeof(tableIndex))) && - (cursor = ReadBytes(cursor, &offset, sizeof(offset))) && - (cursor = DeserializePodVector(cursor, &elemFuncIndices)) && - (cursor = DeserializePodVector(cursor, &elemCodeRangeIndices)); - return cursor; -} - -size_t -ElemSegment::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const -{ - return elemFuncIndices.sizeOfExcludingThis(mallocSizeOf) + - elemCodeRangeIndices.sizeOfExcludingThis(mallocSizeOf); -} - /* virtual */ void Module::serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const { diff --git a/js/src/wasm/WasmModule.h b/js/src/wasm/WasmModule.h index 99ed5d68f7..f36d11d124 100644 --- a/js/src/wasm/WasmModule.h +++ b/js/src/wasm/WasmModule.h @@ -76,59 +76,6 @@ struct LinkData : LinkDataCacheablePod typedef UniquePtr UniqueLinkData; typedef UniquePtr UniqueConstLinkData; -// Export describes the export of a definition in a Module to a field in the -// export object. For functions, Export stores an index into the -// FuncExportVector in Metadata. For memory and table exports, there is -// at most one (default) memory/table so no index is needed. Note: a single -// definition can be exported by multiple Exports in the ExportVector. -// -// ExportVector is built incrementally by ModuleGenerator and then stored -// immutably by Module. - -class Export -{ - CacheableChars fieldName_; - struct CacheablePod { - DefinitionKind kind_; - uint32_t index_; - } pod; - - public: - Export() = default; - explicit Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind); - explicit Export(UniqueChars fieldName, DefinitionKind kind); - - const char* fieldName() const { return fieldName_.get(); } - - DefinitionKind kind() const { return pod.kind_; } - uint32_t funcIndex() const; - uint32_t globalIndex() const; - - WASM_DECLARE_SERIALIZABLE(Export) -}; - -typedef Vector ExportVector; - -// ElemSegment represents an element segment in the module where each element -// describes both its function index and its code range. - -struct ElemSegment -{ - uint32_t tableIndex; - InitExpr offset; - Uint32Vector elemFuncIndices; - Uint32Vector elemCodeRangeIndices; - - ElemSegment() = default; - ElemSegment(uint32_t tableIndex, InitExpr offset, Uint32Vector&& elemFuncIndices) - : tableIndex(tableIndex), offset(offset), elemFuncIndices(Move(elemFuncIndices)) - {} - - WASM_DECLARE_SERIALIZABLE(ElemSegment) -}; - -typedef Vector ElemSegmentVector; - // Module represents a compiled wasm module and primarily provides two // operations: instantiation and serialization. A Module can be instantiated any // number of times to produce new Instance objects. A Module can be serialized diff --git a/js/src/wasm/WasmTypes.cpp b/js/src/wasm/WasmTypes.cpp index a36e4fe17e..5e857454d2 100644 --- a/js/src/wasm/WasmTypes.cpp +++ b/js/src/wasm/WasmTypes.cpp @@ -568,6 +568,129 @@ SigWithId::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const return Sig::sizeOfExcludingThis(mallocSizeOf); } +size_t +Import::serializedSize() const +{ + return module.serializedSize() + + field.serializedSize(); +} + +uint8_t* +Import::serialize(uint8_t* cursor) const +{ + cursor = module.serialize(cursor); + cursor = field.serialize(cursor); + return cursor; +} + +const uint8_t* +Import::deserialize(const uint8_t* cursor) +{ + (cursor = module.deserialize(cursor)) && + (cursor = field.deserialize(cursor)); + return cursor; +} + +size_t +Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const +{ + return module.sizeOfExcludingThis(mallocSizeOf) + + field.sizeOfExcludingThis(mallocSizeOf); +} + +Export::Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind) + : fieldName_(Move(fieldName)) +{ + pod.kind_ = kind; + pod.index_ = index; +} + +Export::Export(UniqueChars fieldName, DefinitionKind kind) + : fieldName_(Move(fieldName)) +{ + pod.kind_ = kind; + pod.index_ = 0; +} + +uint32_t +Export::funcIndex() const +{ + MOZ_ASSERT(pod.kind_ == DefinitionKind::Function); + return pod.index_; +} + +uint32_t +Export::globalIndex() const +{ + MOZ_ASSERT(pod.kind_ == DefinitionKind::Global); + return pod.index_; +} + +size_t +Export::serializedSize() const +{ + return fieldName_.serializedSize() + + sizeof(pod); +} + +uint8_t* +Export::serialize(uint8_t* cursor) const +{ + cursor = fieldName_.serialize(cursor); + cursor = WriteBytes(cursor, &pod, sizeof(pod)); + return cursor; +} + +const uint8_t* +Export::deserialize(const uint8_t* cursor) +{ + (cursor = fieldName_.deserialize(cursor)) && + (cursor = ReadBytes(cursor, &pod, sizeof(pod))); + return cursor; +} + +size_t +Export::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const +{ + return fieldName_.sizeOfExcludingThis(mallocSizeOf); +} + +size_t +ElemSegment::serializedSize() const +{ + return sizeof(tableIndex) + + sizeof(offset) + + SerializedPodVectorSize(elemFuncIndices) + + SerializedPodVectorSize(elemCodeRangeIndices); +} + +uint8_t* +ElemSegment::serialize(uint8_t* cursor) const +{ + cursor = WriteBytes(cursor, &tableIndex, sizeof(tableIndex)); + cursor = WriteBytes(cursor, &offset, sizeof(offset)); + cursor = SerializePodVector(cursor, elemFuncIndices); + cursor = SerializePodVector(cursor, elemCodeRangeIndices); + return cursor; +} + +const uint8_t* +ElemSegment::deserialize(const uint8_t* cursor) +{ + (cursor = ReadBytes(cursor, &tableIndex, sizeof(tableIndex))) && + (cursor = ReadBytes(cursor, &offset, sizeof(offset))) && + (cursor = DeserializePodVector(cursor, &elemFuncIndices)) && + (cursor = DeserializePodVector(cursor, &elemCodeRangeIndices)); + return cursor; +} + +size_t +ElemSegment::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const +{ + return elemFuncIndices.sizeOfExcludingThis(mallocSizeOf) + + elemCodeRangeIndices.sizeOfExcludingThis(mallocSizeOf); +} + Assumptions::Assumptions(JS::BuildIdCharVector&& buildId) : cpuId(GetCPUID()), buildId(Move(buildId)) diff --git a/js/src/wasm/WasmTypes.h b/js/src/wasm/WasmTypes.h index 2ea527bdfb..38795edc02 100644 --- a/js/src/wasm/WasmTypes.h +++ b/js/src/wasm/WasmTypes.h @@ -431,6 +431,39 @@ struct Import typedef Vector ImportVector; +// Export describes the export of a definition in a Module to a field in the +// export object. For functions, Export stores an index into the +// FuncExportVector in Metadata. For memory and table exports, there is +// at most one (default) memory/table so no index is needed. Note: a single +// definition can be exported by multiple Exports in the ExportVector. +// +// ExportVector is built incrementally by ModuleGenerator and then stored +// immutably by Module. + +class Export +{ + CacheableChars fieldName_; + struct CacheablePod { + DefinitionKind kind_; + uint32_t index_; + } pod; + + public: + Export() = default; + explicit Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind); + explicit Export(UniqueChars fieldName, DefinitionKind kind); + + const char* fieldName() const { return fieldName_.get(); } + + DefinitionKind kind() const { return pod.kind_; } + uint32_t funcIndex() const; + uint32_t globalIndex() const; + + WASM_DECLARE_SERIALIZABLE(Export) +}; + +typedef Vector ExportVector; + // A GlobalDesc describes a single global variable. Currently, asm.js and wasm // exposes mutable and immutable private globals, but can't import nor export // mutable globals. @@ -519,6 +552,26 @@ class GlobalDesc typedef Vector GlobalDescVector; +// ElemSegment represents an element segment in the module where each element +// describes both its function index and its code range. + +struct ElemSegment +{ + uint32_t tableIndex; + InitExpr offset; + Uint32Vector elemFuncIndices; + Uint32Vector elemCodeRangeIndices; + + ElemSegment() = default; + ElemSegment(uint32_t tableIndex, InitExpr offset, Uint32Vector&& elemFuncIndices) + : tableIndex(tableIndex), offset(offset), elemFuncIndices(Move(elemFuncIndices)) + {} + + WASM_DECLARE_SERIALIZABLE(ElemSegment) +}; + +typedef Vector ElemSegmentVector; + // DataSegment describes the offset of a data segment in the bytecode that is // to be copied at a given offset into linear memory upon instantiation.