diff --git a/js/src/asmjs/WasmAST.h b/js/src/asmjs/WasmAST.h index 0c66393dcb03..683ffe0d1507 100644 --- a/js/src/asmjs/WasmAST.h +++ b/js/src/asmjs/WasmAST.h @@ -649,15 +649,15 @@ class AstImport : public AstNode DefinitionKind kind_; AstRef funcSig_; - Limits resizable_; + Limits limits_; AstGlobal global_; public: AstImport(AstName name, AstName module, AstName field, AstRef funcSig) : name_(name), module_(module), field_(field), kind_(DefinitionKind::Function), funcSig_(funcSig) {} - AstImport(AstName name, AstName module, AstName field, DefinitionKind kind, Limits resizable) - : name_(name), module_(module), field_(field), kind_(kind), resizable_(resizable) + AstImport(AstName name, AstName module, AstName field, DefinitionKind kind, Limits limits) + : name_(name), module_(module), field_(field), kind_(kind), limits_(limits) {} AstImport(AstName name, AstName module, AstName field, AstGlobal global) : name_(name), module_(module), field_(field), kind_(DefinitionKind::Global), global_(global) @@ -672,9 +672,9 @@ class AstImport : public AstNode MOZ_ASSERT(kind_ == DefinitionKind::Function); return funcSig_; } - Limits resizable() const { + Limits limits() const { MOZ_ASSERT(kind_ == DefinitionKind::Memory || kind_ == DefinitionKind::Table); - return resizable_; + return limits_; } const AstGlobal& global() const { MOZ_ASSERT(kind_ == DefinitionKind::Global); @@ -750,6 +750,17 @@ class AstStartFunc : public AstNode } }; +struct AstResizable +{ + Limits limits; + bool imported; + + AstResizable(Limits limits, bool imported) + : limits(limits), + imported(imported) + {} +}; + class AstModule : public AstNode { public: @@ -758,6 +769,7 @@ class AstModule : public AstNode typedef AstVector ExportVector; typedef AstVector SigVector; typedef AstVector NameVector; + typedef AstVector AstResizableVector; private: typedef AstHashMap SigMap; @@ -767,8 +779,8 @@ class AstModule : public AstNode SigMap sigMap_; ImportVector imports_; NameVector funcImportNames_; - Maybe table_; - Maybe memory_; + AstResizableVector tables_; + AstResizableVector memories_; ExportVector exports_; Maybe startFunc_; FuncVector funcs_; @@ -783,6 +795,8 @@ class AstModule : public AstNode sigMap_(lifo), imports_(lifo), funcImportNames_(lifo), + tables_(lifo), + memories_(lifo), exports_(lifo), funcs_(lifo), dataSegments_(lifo), @@ -792,29 +806,23 @@ class AstModule : public AstNode bool init() { return sigMap_.init(); } - bool setMemory(Limits memory) { - if (memory_) - return false; - memory_.emplace(memory); - return true; + bool addMemory(Limits memory) { + return memories_.append(AstResizable(memory, false)); } bool hasMemory() const { - return !!memory_; + return !!memories_.length(); } - const Limits& memory() const { - return *memory_; + const AstResizableVector& memories() const { + return memories_; } - bool setTable(Limits table) { - if (table_) - return false; - table_.emplace(table); - return true; + bool addTable(Limits table) { + return tables_.append(AstResizable(table, false)); } bool hasTable() const { - return !!table_; + return !!tables_.length(); } - const Limits& table() const { - return *table_; + const AstResizableVector& tables() const { + return tables_; } bool append(AstDataSegment* seg) { return dataSegments_.append(seg); @@ -869,9 +877,21 @@ class AstModule : public AstNode return funcs_; } bool append(AstImport* imp) { - if (imp->kind() == DefinitionKind::Function) { + switch (imp->kind()) { + case DefinitionKind::Function: if (!funcImportNames_.append(imp->name())) return false; + break; + case DefinitionKind::Table: + if (!tables_.append(AstResizable(imp->limits(), true))) + return false; + break; + case DefinitionKind::Memory: + if (!memories_.append(AstResizable(imp->limits(), true))) + return false; + break; + case DefinitionKind::Global: + break; } return imports_.append(imp); diff --git a/js/src/asmjs/WasmBinaryFormat.cpp b/js/src/asmjs/WasmBinaryFormat.cpp index 550d1fe7a9cb..cebf479301d1 100644 --- a/js/src/asmjs/WasmBinaryFormat.cpp +++ b/js/src/asmjs/WasmBinaryFormat.cpp @@ -18,11 +18,15 @@ #include "asmjs/WasmBinaryFormat.h" +#include "mozilla/CheckedInt.h" + #include "jsprf.h" using namespace js; using namespace js::wasm; +using mozilla::CheckedInt; + bool wasm::DecodePreamble(Decoder& d) { @@ -268,6 +272,63 @@ wasm::DecodeDataSection(Decoder& d, bool usesMemory, uint32_t minMemoryByteLengt return true; } +bool +wasm::DecodeMemoryLimits(Decoder& d, bool hasMemory, Limits* memory) +{ + if (hasMemory) + return d.fail("already have default memory"); + + if (!DecodeLimits(d, memory)) + return false; + + CheckedInt initialBytes = memory->initial; + initialBytes *= PageSize; + if (!initialBytes.isValid() || initialBytes.value() > uint32_t(INT32_MAX)) + return d.fail("initial memory size too big"); + + memory->initial = initialBytes.value(); + + if (memory->maximum) { + CheckedInt maximumBytes = *memory->maximum; + maximumBytes *= PageSize; + if (!maximumBytes.isValid()) + return d.fail("maximum memory size too big"); + + memory->maximum = Some(maximumBytes.value()); + } + + return true; +} + +bool +wasm::DecodeMemorySection(Decoder& d, bool hasMemory, Limits* memory, bool *present) +{ + *present = false; + + uint32_t sectionStart, sectionSize; + if (!d.startSection(SectionId::Memory, §ionStart, §ionSize, "memory")) + return false; + if (sectionStart == Decoder::NotStarted) + return true; + + *present = true; + + uint32_t numMemories; + if (!d.readVarU32(&numMemories)) + return d.fail("failed to read number of memories"); + + if (numMemories != 1) + return d.fail("the number of memories must be exactly one"); + + if (!DecodeMemoryLimits(d, hasMemory, memory)) + return false; + + if (!d.finishSection(sectionStart, sectionSize, "memory")) + return false; + + return true; +} + bool wasm::DecodeUnknownSections(Decoder& d) { diff --git a/js/src/asmjs/WasmBinaryFormat.h b/js/src/asmjs/WasmBinaryFormat.h index 563554646741..1a8558ab0626 100644 --- a/js/src/asmjs/WasmBinaryFormat.h +++ b/js/src/asmjs/WasmBinaryFormat.h @@ -55,6 +55,12 @@ MOZ_MUST_USE bool DecodeDataSection(Decoder& d, bool usesMemory, uint32_t minMemoryByteLength, const GlobalDescVector& globals, DataSegmentVector* segments); +MOZ_MUST_USE bool +DecodeMemoryLimits(Decoder& d, bool hasMemory, Limits* memory); + +MOZ_MUST_USE bool +DecodeMemorySection(Decoder& d, bool hasMemory, Limits* memory, bool* present); + } // namespace wasm } // namespace js diff --git a/js/src/asmjs/WasmBinaryToAST.cpp b/js/src/asmjs/WasmBinaryToAST.cpp index c78c6c1f64b7..4669c666cb73 100644 --- a/js/src/asmjs/WasmBinaryToAST.cpp +++ b/js/src/asmjs/WasmBinaryToAST.cpp @@ -1573,9 +1573,12 @@ AstDecodeTableSection(AstDecodeContext& c) if (table.initial > MaxTableElems) return c.d.fail("too many table elements"); - if (!c.module().setTable(table)) + if (c.module().hasTable()) return c.d.fail("already have a table"); + if (!c.module().addTable(table)) + return false; + if (!c.d.finishSection(sectionStart, sectionSize, "table")) return false; @@ -1670,6 +1673,9 @@ AstDecodeImport(AstDecodeContext& c, uint32_t importIndex, AstImport** import) break; } case uint32_t(DefinitionKind::Table): { + if (c.module().hasTable()) + return c.d.fail("already have default table"); + AstName importName; if (!AstDecodeGenerateName(c, AstName(u"table"), importIndex, &importName)) return false; @@ -1688,7 +1694,7 @@ AstDecodeImport(AstDecodeContext& c, uint32_t importIndex, AstImport** import) return false; Limits memory; - if (!DecodeLimits(c.d, &memory)) + if (!DecodeMemoryLimits(c.d, c.module().hasMemory(), &memory)) return false; *import = new(c.lifo) AstImport(importName, moduleName, fieldName, @@ -1738,27 +1744,16 @@ AstDecodeImportSection(AstDecodeContext& c) static bool AstDecodeMemorySection(AstDecodeContext& c) { - uint32_t sectionStart, sectionSize; - if (!c.d.startSection(SectionId::Memory, §ionStart, §ionSize, "memory")) - return false; - if (sectionStart == Decoder::NotStarted) - return true; - - uint32_t numMemories; - if (!c.d.readVarU32(&numMemories)) - return c.d.fail("failed to read number of memories"); - - if (numMemories != 1) - return c.d.fail("the number of memories must be exactly one"); - + bool present; Limits memory; - if (!DecodeLimits(c.d, &memory)) + if (!DecodeMemorySection(c.d, c.module().hasMemory(), &memory, &present)) return false; - if (!c.d.finishSection(sectionStart, sectionSize, "memory")) - return false; + if (present) { + if (!c.module().addMemory(memory)) + return false; + } - c.module().setMemory(memory); return true; } @@ -2031,7 +2026,10 @@ AstDecodeDataSection(AstDecodeContext &c) { DataSegmentVector segments; bool hasMemory = c.module().hasMemory(); - uint32_t memByteLength = hasMemory ? c.module().memory().initial * PageSize : 0; + + MOZ_ASSERT(c.module().memories().length() <= 1, "at most one memory in MVP"); + uint32_t memByteLength = hasMemory ? c.module().memories()[0].limits.initial : 0; + if (!DecodeDataSection(c.d, hasMemory, memByteLength, c.globalDescs(), &segments)) return false; diff --git a/js/src/asmjs/WasmBinaryToExperimentalText.cpp b/js/src/asmjs/WasmBinaryToExperimentalText.cpp index dcf33c98d24d..f69e8690ec22 100644 --- a/js/src/asmjs/WasmBinaryToExperimentalText.cpp +++ b/js/src/asmjs/WasmBinaryToExperimentalText.cpp @@ -1757,23 +1757,29 @@ PrintCodeSection(WasmPrintContext& c, const AstModule::FuncVector& funcs, const return true; } - static bool PrintDataSection(WasmPrintContext& c, const AstModule& module) { if (!module.hasMemory()) return true; + MOZ_ASSERT(module.memories().length() == 1, "NYI: several memories"); + if (!PrintIndent(c)) return false; if (!c.buffer.append("memory ")) return false; - if (!PrintInt32(c, module.memory().initial)) + + const Limits& memory = module.memories()[0].limits; + MOZ_ASSERT(memory.initial % PageSize == 0); + if (!PrintInt32(c, memory.initial / PageSize)) return false; - if (module.memory().maximum) { + + if (memory.maximum) { + MOZ_ASSERT(*memory.maximum % PageSize == 0); if (!c.buffer.append(", ")) return false; - if (!PrintInt32(c, *module.memory().maximum)) + if (!PrintInt32(c, *memory.maximum / PageSize)) return false; } diff --git a/js/src/asmjs/WasmBinaryToText.cpp b/js/src/asmjs/WasmBinaryToText.cpp index ee720ef590af..5d3c5473cc72 100644 --- a/js/src/asmjs/WasmBinaryToText.cpp +++ b/js/src/asmjs/WasmBinaryToText.cpp @@ -1296,6 +1296,42 @@ RenderGlobalSection(WasmRenderContext& c, const AstModule& module) return true; } +static bool +RenderLimits(WasmRenderContext& c, const Limits& limits) +{ + if (!RenderInt32(c, limits.initial)) + return false; + + if (limits.maximum) { + if (!c.buffer.append(" ")) + return false; + if (!RenderInt32(c, *limits.maximum)) + return false; + } + + return true; +} + +static bool +RenderResizableMemory(WasmRenderContext& c, Limits memory) +{ + if (!c.buffer.append("(memory ")) + return false; + + MOZ_ASSERT(memory.initial % PageSize == 0); + memory.initial /= PageSize; + + if (memory.maximum) { + MOZ_ASSERT(*memory.maximum % PageSize == 0); + *memory.maximum /= PageSize; + } + + if (!RenderLimits(c, memory)) + return false; + + return c.buffer.append(")"); +} + static bool RenderImport(WasmRenderContext& c, AstImport& import, const AstModule& module) { @@ -1334,7 +1370,8 @@ RenderImport(WasmRenderContext& c, AstImport& import, const AstModule& module) break; } case DefinitionKind::Memory: { - // TODO next patch + if (!RenderResizableMemory(c, import.limits())) + return false; break; } case DefinitionKind::Global: { @@ -1371,14 +1408,13 @@ RenderExport(WasmRenderContext& c, AstExport& export_, return false; if (!c.buffer.append("\" ")) return false; - if (export_.kind() == DefinitionKind::Memory) { - if (!c.buffer.append("memory")) - return false; - } else { + + switch (export_.kind()) { + case DefinitionKind::Function: { uint32_t index = export_.ref().index(); AstName name = index < funcImportNames.length() - ? funcImportNames[index] - : funcs[index - funcImportNames.length()]->name(); + ? funcImportNames[index] + : funcs[index - funcImportNames.length()]->name(); if (name.empty()) { if (!RenderInt32(c, index)) return false; @@ -1386,7 +1422,27 @@ RenderExport(WasmRenderContext& c, AstExport& export_, if (!RenderName(c, name)) return false; } + break; + } + case DefinitionKind::Table: { + if (!c.buffer.append("table")) + return false; + break; + } + case DefinitionKind::Memory: { + if (!c.buffer.append("memory")) + return false; + break; + } + case DefinitionKind::Global: { + if (!c.buffer.append("global")) + return false; + if (!RenderRef(c, export_.ref())) + return false; + break; + } } + return c.buffer.append(")\n"); } @@ -1493,30 +1549,28 @@ RenderCodeSection(WasmRenderContext& c, const AstModule::FuncVector& funcs, cons } static bool -RenderDataSection(WasmRenderContext& c, const AstModule& module) +RenderMemorySection(WasmRenderContext& c, const AstModule& module) { if (!module.hasMemory()) return true; - if (!RenderIndent(c)) - return false; - if (!c.buffer.append("(memory ")) - return false; - - if (!RenderInt32(c, module.memory().initial)) - return false; - - Maybe memMax = module.memory().maximum; - if (memMax) { - if (!c.buffer.append(" ")) + for (const AstResizable& memory : module.memories()) { + if (memory.imported) + continue; + if (!RenderIndent(c)) return false; - if (!RenderInt32(c, *memMax)) + if (!RenderResizableMemory(c, memory.limits)) + return false; + if (!c.buffer.append("\n")) return false; } - if (!c.buffer.append(")\n")) - return false; + return true; +} +static bool +RenderDataSection(WasmRenderContext& c, const AstModule& module) +{ uint32_t numSegments = module.dataSegments().length(); if (!numSegments) return true; @@ -1565,6 +1619,9 @@ RenderModule(WasmRenderContext& c, AstModule& module) if (!RenderCodeSection(c, module.funcs(), module.sigs())) return false; + if (!RenderMemorySection(c, module)) + return false; + if (!RenderDataSection(c, module)) return false; diff --git a/js/src/asmjs/WasmCompile.cpp b/js/src/asmjs/WasmCompile.cpp index c626b952e8b9..72638126dbd2 100644 --- a/js/src/asmjs/WasmCompile.cpp +++ b/js/src/asmjs/WasmCompile.cpp @@ -18,8 +18,6 @@ #include "asmjs/WasmCompile.h" -#include "mozilla/CheckedInt.h" - #include "jsprf.h" #include "asmjs/WasmBinaryFormat.h" @@ -31,7 +29,6 @@ using namespace js; using namespace js::jit; using namespace js::wasm; -using mozilla::CheckedInt; using mozilla::IsNaN; namespace { @@ -569,33 +566,15 @@ DecodeName(Decoder& d) } static bool -DecodeResizableMemory(Decoder& d, ModuleGeneratorData* init) +DecodeMemoryLimits(Decoder& d, ModuleGeneratorData* init) { - if (UsesMemory(init->memoryUsage)) - return d.fail("already have default memory"); - - Limits limits; - if (!DecodeLimits(d, &limits)) + Limits memory; + if (!DecodeMemoryLimits(d, UsesMemory(init->memoryUsage), &memory)) return false; init->memoryUsage = MemoryUsage::Unshared; - - CheckedInt initialBytes = limits.initial; - initialBytes *= PageSize; - if (!initialBytes.isValid() || initialBytes.value() > uint32_t(INT32_MAX)) - return d.fail("initial memory size too big"); - - init->minMemoryLength = initialBytes.value(); - - if (limits.maximum) { - CheckedInt maximumBytes = *limits.maximum; - maximumBytes *= PageSize; - if (!maximumBytes.isValid()) - return d.fail("maximum memory size too big"); - - init->maxMemoryLength = Some(maximumBytes.value()); - } - + init->minMemoryLength = memory.initial; + init->maxMemoryLength = memory.maximum; return true; } @@ -671,7 +650,7 @@ DecodeImport(Decoder& d, ModuleGeneratorData* init, ImportVector* imports) break; } case DefinitionKind::Memory: { - if (!DecodeResizableMemory(d, init)) + if (!DecodeMemoryLimits(d, init)) return false; break; } @@ -746,26 +725,18 @@ DecodeTableSection(Decoder& d, ModuleGeneratorData* init, Uint32Vector* oldElems } static bool -DecodeMemorySection(Decoder& d, ModuleGeneratorData* init, bool* exported) +DecodeMemorySection(Decoder& d, ModuleGeneratorData* init) { - 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 (!DecodeResizableMemory(d, init)) + bool present; + Limits memory; + if (!DecodeMemorySection(d, UsesMemory(init->memoryUsage), &memory, &present)) return false; - if (!d.finishSection(sectionStart, sectionSize, "memory")) - return false; + if (present) { + init->memoryUsage = MemoryUsage::Unshared; + init->minMemoryLength = memory.initial; + init->maxMemoryLength = memory.maximum; + } return true; } @@ -893,7 +864,7 @@ DecodeExport(Decoder& d, ModuleGenerator& mg, CStringSet* dupSet) } static bool -DecodeExportSection(Decoder& d, bool memoryExported, ModuleGenerator& mg) +DecodeExportSection(Decoder& d, ModuleGenerator& mg) { uint32_t sectionStart, sectionSize; if (!d.startSection(SectionId::Export, §ionStart, §ionSize, "export")) @@ -1213,8 +1184,7 @@ wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueCha if (!DecodeTableSection(d, init.get(), &oldElems)) return nullptr; - bool memoryExported = false; - if (!DecodeMemorySection(d, init.get(), &memoryExported)) + if (!::DecodeMemorySection(d, init.get())) return nullptr; if (!DecodeGlobalSection(d, init.get())) @@ -1224,7 +1194,7 @@ wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueCha if (!mg.init(Move(init), args)) return nullptr; - if (!DecodeExportSection(d, memoryExported, mg)) + if (!DecodeExportSection(d, mg)) return nullptr; if (!DecodeStartSection(d, mg)) diff --git a/js/src/asmjs/WasmTextToBinary.cpp b/js/src/asmjs/WasmTextToBinary.cpp index bdace6dbf404..f5003d088df3 100644 --- a/js/src/asmjs/WasmTextToBinary.cpp +++ b/js/src/asmjs/WasmTextToBinary.cpp @@ -2735,7 +2735,7 @@ ParseDataSegment(WasmParseContext& c) } static bool -ParseLimits(WasmParseContext& c, Limits* resizable) +ParseLimits(WasmParseContext& c, Limits* limits) { WasmToken initial; if (!c.ts.match(WasmToken::Index, &initial, c.error)) @@ -2747,7 +2747,7 @@ ParseLimits(WasmParseContext& c, Limits* resizable) maximum.emplace(token.index()); Limits r = { initial.index(), maximum }; - *resizable = r; + *limits = r; return true; } @@ -2805,7 +2805,7 @@ ParseMemory(WasmParseContext& c, WasmToken token, AstModule* module) } Limits memory = { uint32_t(pages), Some(uint32_t(pages)) }; - if (!module->setMemory(memory)) + if (!module->addMemory(memory)) return false; if (!c.ts.match(WasmToken::CloseParen, c.error)) @@ -2818,12 +2818,7 @@ ParseMemory(WasmParseContext& c, WasmToken token, AstModule* module) if (!ParseLimits(c, &memory)) return false; - if (!module->setMemory(memory)) { - c.ts.generateError(token, c.error); - return false; - } - - return true; + return module->addMemory(memory); } static bool @@ -3084,11 +3079,7 @@ ParseTable(WasmParseContext& c, WasmToken token, AstModule* module) Limits table; if (!ParseTableSig(c, &table)) return false; - if (!module->setTable(table)) { - c.ts.generateError(token, c.error); - return false; - } - return true; + return module->addTable(table); } // Or: anyfunc (elem 1 2 ...) @@ -3116,10 +3107,8 @@ ParseTable(WasmParseContext& c, WasmToken token, AstModule* module) return false; Limits r = { numElements, Some(numElements) }; - if (!module->setTable(r)) { - c.ts.generateError(token, c.error); + if (!module->addTable(r)) return false; - } auto* zero = new(c.lifo) AstConst(Val(uint32_t(0))); if (!zero) @@ -4371,11 +4360,11 @@ EncodeImport(Encoder& e, AstImport& imp) return false; break; case DefinitionKind::Table: - if (!EncodeTableLimits(e, imp.resizable())) + if (!EncodeTableLimits(e, imp.limits())) return false; break; case DefinitionKind::Memory: - if (!EncodeLimits(e, imp.resizable())) + if (!EncodeLimits(e, imp.limits())) return false; break; } @@ -4408,21 +4397,28 @@ EncodeImportSection(Encoder& e, AstModule& module) static bool EncodeMemorySection(Encoder& e, AstModule& module) { - if (!module.hasMemory()) + size_t numOwnMemories = 0; + for (const AstResizable& memory : module.memories()) { + if (!memory.imported) + numOwnMemories++; + } + + if (!numOwnMemories) return true; size_t offset; if (!e.startSection(SectionId::Memory, &offset)) return false; - uint32_t numMemories = 1; - if (!e.writeVarU32(numMemories)) + if (!e.writeVarU32(numOwnMemories)) return false; - const Limits& memory = module.memory(); - - if (!EncodeLimits(e, memory)) - return false; + for (const AstResizable& memory : module.memories()) { + if (memory.imported) + continue; + if (!EncodeLimits(e, memory.limits)) + return false; + } e.finishSection(offset); return true; @@ -4505,20 +4501,28 @@ EncodeExportSection(Encoder& e, AstModule& module) static bool EncodeTableSection(Encoder& e, AstModule& module) { - if (!module.hasTable()) + size_t numOwnTables = 0; + for (const AstResizable& table : module.tables()) { + if (!table.imported) + numOwnTables++; + } + + if (!numOwnTables) return true; size_t offset; if (!e.startSection(SectionId::Table, &offset)) return false; - uint32_t numTables = 1; - if (!e.writeVarU32(numTables)) + if (!e.writeVarU32(numOwnTables)) return false; - const Limits& table = module.table(); - if (!EncodeTableLimits(e, table)) - return false; + for (const AstResizable& table : module.tables()) { + if (table.imported) + continue; + if (!EncodeTableLimits(e, table.limits)) + return false; + } e.finishSection(offset); return true; diff --git a/js/src/jit-test/tests/wasm/full-cycle.js b/js/src/jit-test/tests/wasm/full-cycle.js index abf29e012800..99297f4f5a01 100644 --- a/js/src/jit-test/tests/wasm/full-cycle.js +++ b/js/src/jit-test/tests/wasm/full-cycle.js @@ -42,3 +42,24 @@ wasmFullPass(`(module (export "run" $get) )`, 13 + 42 + 37 + 42, { globals: {x: 42} }); +// Memory. +wasmFullPass(`(module + (memory (export "memory") 1 2) + (data (i32.const 0) "\\00\\01\\02\\03\\04\\05") + (func (export "run") (result i32) + i32.const 1 + i32.load offset=2 + ) +)`, 0x050403); + +let memory = new WebAssembly.Memory({ initial: 1, maximum: 2 }); + +wasmFullPass(`(module + (memory (import "" "memory") 1 2) + (data (i32.const 0) "\\00\\01\\02\\03\\04\\05") + (func (export "run") (result i32) + i32.const 1 + i32.load offset=2 + ) + (export "mem" memory) +)`, 0x050403, {"": {memory}});