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;
This commit is contained in:
win7-7 2025-12-28 16:48:39 +02:00 committed by wuggy
commit 562b53c8b4
13 changed files with 1436 additions and 1502 deletions

View file

@ -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<ModuleGeneratorData>(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<ModuleEnvironment>(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());
}
};

View file

@ -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<true>(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;

View file

@ -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, &sectionStart, &sectionSize, "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<Limits>* 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<uint32_t> 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<uint32_t> 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<Limits>* memory,
ImportVector* imports)
static bool
DecodeImportSection(Decoder& d, ModuleEnvironment* env)
{
uint32_t sectionStart, sectionSize;
if (!d.startSection(SectionId::Import, &sectionStart, &sectionSize, "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, &sectionStart, &sectionSize, "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<uint32_t> numFuncs = numImportedFunc;
CheckedInt<uint32_t> 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, &sectionStart, &sectionSize, "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, &sectionStart, &sectionSize, "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, &sectionStart, &sectionSize, "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<const char*, CStringHasher, SystemAllocPolicy> 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, &sectionStart, &sectionSize, "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, &sectionStart, &sectionSize, "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, &sectionStart, &sectionSize, "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, &sectionStart, &sectionSize, "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<uint32_t> 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<uint32_t> 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, &sectionStart, &sectionSize, "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;
}
}

View file

@ -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<uint32_t> minMemoryLength;
Maybe<uint32_t> 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<uint32_t> 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<ModuleEnvironment> 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<Limits>* 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

File diff suppressed because it is too large Load diff

View file

@ -419,246 +419,6 @@ DecodeFunctionBodyExprs(FunctionDecoder& f)
#undef CHECK
}
static bool
DecodeImportSection(Decoder& d, ModuleGeneratorData* init, ImportVector* imports)
{
Maybe<Limits> 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, &sectionStart, &sectionSize, "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, &sectionStart, &sectionSize, "global"))
return false;
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t numDefs;
if (!d.readVarU32(&numDefs))
return d.fail("expected number of globals");
CheckedInt<uint32_t> 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<const char*, CStringHasher, SystemAllocPolicy> 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, &sectionStart, &sectionSize, "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, &sectionStart, &sectionSize, "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, &sectionStart, &sectionSize, "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, &sectionStart, &sectionSize))
@ -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<ModuleGeneratorData>();
if (!init)
auto env = js::MakeUnique<ModuleEnvironment>();
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));
}

View file

@ -24,6 +24,7 @@
#include <algorithm>
#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<Metadata>();
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<Metadata>();
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<Module>(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));
}

View file

@ -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<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
typedef jit::ABIArgIter<ValTypeVector> 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<uint32_t> minMemoryLength;
Maybe<uint32_t> 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<FuncBytes> UniqueFuncBytes;
typedef Vector<UniqueFuncBytes, 8, SystemAllocPolicy> 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<bool> 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<ModuleGeneratorData> UniqueModuleGeneratorData;
typedef Vector<FuncCompileUnit, 8, SystemAllocPolicy> 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<jit::TempAllocator> alloc_;
Maybe<jit::MacroAssembler> 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.

View file

@ -147,7 +147,7 @@ class FunctionCompiler
typedef Vector<ControlFlowPatchVector, 0, SystemAllocPolicy> ControlFlowPatchsVector;
typedef Vector<CallCompileState*, 0, SystemAllocPolicy> 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<TrapOffset> 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<MTruncateToInt32>(input));
else
f.iter().setResult(f.truncate<MWasmTruncateToInt32>(input, isUnsigned));
} else {
MOZ_ASSERT(resultType == ValType::I64);
MOZ_ASSERT(!f.mg().isAsmJS());
MOZ_ASSERT(!f.env().isAsmJS());
f.iter().setResult(f.truncate<MWasmTruncateToInt64>(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()))

View file

@ -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<DefinitionKind>(cursor, kind);
return cursor;
}
const uint8_t*
Import::deserialize(const uint8_t* cursor)
{
(cursor = module.deserialize(cursor)) &&
(cursor = field.deserialize(cursor)) &&
(cursor = ReadScalar<DefinitionKind>(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
{

View file

@ -76,59 +76,6 @@ struct LinkData : LinkDataCacheablePod
typedef UniquePtr<LinkData> UniqueLinkData;
typedef UniquePtr<const LinkData> 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<Export, 0, SystemAllocPolicy> 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<ElemSegment, 0, SystemAllocPolicy> 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

View file

@ -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))

View file

@ -431,6 +431,39 @@ struct Import
typedef Vector<Import, 0, SystemAllocPolicy> 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<Export, 0, SystemAllocPolicy> 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<GlobalDesc, 0, SystemAllocPolicy> 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<ElemSegment, 0, SystemAllocPolicy> 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.