mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 08:48:39 +09:00
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:
parent
7a7950b212
commit
562b53c8b4
13 changed files with 1436 additions and 1502 deletions
|
|
@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<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, §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<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, §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<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, §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<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, §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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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, §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<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, §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<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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue