diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 273 | ||||
-rw-r--r-- | src/wasm/wasm-debug.cpp | 3 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 356 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 46 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 140 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 32 |
6 files changed, 576 insertions, 274 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index e4b942203..7008d185c 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -52,7 +52,7 @@ void WasmBinaryWriter::write() { writeImports(); writeFunctionSignatures(); writeTableDeclarations(); - writeMemory(); + writeMemories(); writeTags(); if (wasm->features.hasStrings()) { writeStrings(); @@ -203,18 +203,21 @@ void WasmBinaryWriter::writeStart() { finishSection(start); } -void WasmBinaryWriter::writeMemory() { - if (!wasm->memory.exists || wasm->memory.imported()) { +void WasmBinaryWriter::writeMemories() { + if (wasm->memories.empty()) { return; } - BYN_TRACE("== writeMemory\n"); + BYN_TRACE("== writeMemories\n"); auto start = startSection(BinaryConsts::Section::Memory); - o << U32LEB(1); // Define 1 memory - writeResizableLimits(wasm->memory.initial, - wasm->memory.max, - wasm->memory.hasMax(), - wasm->memory.shared, - wasm->memory.is64()); + auto num = importInfo->getNumDefinedMemories(); + o << U32LEB(num); + ModuleUtils::iterDefinedMemories(*wasm, [&](Memory* memory) { + writeResizableLimits(memory->initial, + memory->max, + memory->hasMax(), + memory->shared, + memory->is64()); + }); finishSection(start); } @@ -333,16 +336,16 @@ void WasmBinaryWriter::writeImports() { o << uint8_t(0); // Reserved 'attribute' field. Always 0. o << U32LEB(getTypeIndex(tag->sig)); }); - if (wasm->memory.imported()) { + ModuleUtils::iterImportedMemories(*wasm, [&](Memory* memory) { BYN_TRACE("write one memory\n"); - writeImportHeader(&wasm->memory); + writeImportHeader(memory); o << U32LEB(int32_t(ExternalKind::Memory)); - writeResizableLimits(wasm->memory.initial, - wasm->memory.max, - wasm->memory.hasMax(), - wasm->memory.shared, - wasm->memory.is64()); - } + writeResizableLimits(memory->initial, + memory->max, + memory->hasMax(), + memory->shared, + memory->is64()); + }); ModuleUtils::iterImportedTables(*wasm, [&](Table* table) { BYN_TRACE("write one table\n"); writeImportHeader(table); @@ -566,8 +569,7 @@ void WasmBinaryWriter::writeExports() { o << U32LEB(getTableIndex(curr->value)); break; case ExternalKind::Memory: - // TODO: fix with multi-memory - o << U32LEB(0); + o << U32LEB(getMemoryIndex(curr->value)); break; case ExternalKind::Global: o << U32LEB(getGlobalIndex(curr->value)); @@ -629,6 +631,12 @@ uint32_t WasmBinaryWriter::getTableIndex(Name name) const { return it->second; } +uint32_t WasmBinaryWriter::getMemoryIndex(Name name) const { + auto it = indexes.memoryIndexes.find(name); + assert(it != indexes.memoryIndexes.end()); + return it->second; +} + uint32_t WasmBinaryWriter::getGlobalIndex(Name name) const { auto it = indexes.globalIndexes.find(name); assert(it != indexes.globalIndexes.end()); @@ -930,12 +938,28 @@ void WasmBinaryWriter::writeNames() { } // memory names - if (wasm->memory.exists && wasm->memory.hasExplicitName) { - auto substart = - startSubsection(BinaryConsts::UserSections::Subsection::NameMemory); - o << U32LEB(1) << U32LEB(0); // currently exactly 1 memory at index 0 - writeEscapedName(wasm->memory.name.str); - finishSubsection(substart); + { + std::vector<std::pair<Index, Memory*>> memoriesWithNames; + Index checked = 0; + auto check = [&](Memory* curr) { + if (curr->hasExplicitName) { + memoriesWithNames.push_back({checked, curr}); + } + checked++; + }; + ModuleUtils::iterImportedMemories(*wasm, check); + ModuleUtils::iterDefinedMemories(*wasm, check); + assert(checked == indexes.memoryIndexes.size()); + if (memoriesWithNames.size() > 0) { + auto substart = + startSubsection(BinaryConsts::UserSections::Subsection::NameMemory); + o << U32LEB(memoriesWithNames.size()); + for (auto& [index, memory] : memoriesWithNames) { + o << U32LEB(index); + writeEscapedName(memory->name.str); + } + finishSubsection(substart); + } } // global names @@ -990,7 +1014,7 @@ void WasmBinaryWriter::writeNames() { } // data segment names - if (wasm->memory.exists) { + if (!wasm->memories.empty()) { Index count = 0; for (auto& seg : wasm->dataSegments) { if (seg->hasExplicitName) { @@ -1547,7 +1571,7 @@ void WasmBinaryBuilder::read() { readStart(); break; case BinaryConsts::Section::Memory: - readMemory(); + readMemories(); break; case BinaryConsts::Section::Type: readTypes(); @@ -1761,10 +1785,6 @@ int64_t WasmBinaryBuilder::getS64LEB() { return ret.value; } -uint64_t WasmBinaryBuilder::getUPtrLEB() { - return wasm.memory.is64() ? getU64LEB() : getU32LEB(); -} - bool WasmBinaryBuilder::getBasicType(int32_t code, Type& out) { switch (code) { case BinaryConsts::EncodedType::i32: @@ -1963,24 +1983,20 @@ void WasmBinaryBuilder::readStart() { startIndex = getU32LEB(); } -void WasmBinaryBuilder::readMemory() { - BYN_TRACE("== readMemory\n"); - auto numMemories = getU32LEB(); - if (!numMemories) { - return; - } - if (numMemories != 1) { - throwError("Must be exactly 1 memory"); - } - if (wasm.memory.exists) { - throwError("Memory cannot be both imported and defined"); +void WasmBinaryBuilder::readMemories() { + BYN_TRACE("== readMemories\n"); + auto num = getU32LEB(); + BYN_TRACE("num: " << num << std::endl); + for (size_t i = 0; i < num; i++) { + BYN_TRACE("read one\n"); + auto memory = Builder::makeMemory(Name::fromInt(i)); + getResizableLimits(memory->initial, + memory->max, + memory->shared, + memory->indexType, + Memory::kUnlimitedSize); + memories.push_back(std::move(memory)); } - wasm.memory.exists = true; - getResizableLimits(wasm.memory.initial, - wasm.memory.max, - wasm.memory.shared, - wasm.memory.indexType, - Memory::kUnlimitedSize); } void WasmBinaryBuilder::readTypes() { @@ -2171,6 +2187,13 @@ Name WasmBinaryBuilder::getTableName(Index index) { return wasm.tables[index]->name; } +Name WasmBinaryBuilder::getMemoryName(Index index) { + if (index >= wasm.memories.size()) { + throwError("invalid memory index"); + } + return wasm.memories[index]->name; +} + Name WasmBinaryBuilder::getGlobalName(Index index) { if (index >= wasm.globals.size()) { throwError("invalid global index"); @@ -2270,15 +2293,16 @@ void WasmBinaryBuilder::readImports() { } case ExternalKind::Memory: { Name name(std::string("mimport$") + std::to_string(memoryCounter++)); - wasm.memory.module = module; - wasm.memory.base = base; - wasm.memory.name = name; - wasm.memory.exists = true; - getResizableLimits(wasm.memory.initial, - wasm.memory.max, - wasm.memory.shared, - wasm.memory.indexType, + auto memory = builder.makeMemory(name); + memory->module = module; + memory->base = base; + getResizableLimits(memory->initial, + memory->max, + memory->shared, + memory->indexType, Memory::kUnlimitedSize); + memoryImports.push_back(memory.get()); + wasm.addMemory(std::move(memory)); break; } case ExternalKind::Global: { @@ -2923,6 +2947,9 @@ void WasmBinaryBuilder::processNames() { for (auto& segment : elementSegments) { wasm.addElementSegment(std::move(segment)); } + for (auto& memory : memories) { + wasm.addMemory(std::move(memory)); + } for (auto& segment : dataSegments) { wasm.addDataSegment(std::move(segment)); } @@ -2943,7 +2970,7 @@ void WasmBinaryBuilder::processNames() { curr->value = getTableName(index); break; case ExternalKind::Memory: - curr->value = wasm.memory.name; + curr->value = getMemoryName(index); break; case ExternalKind::Global: curr->value = getGlobalName(index); @@ -2969,6 +2996,12 @@ void WasmBinaryBuilder::processNames() { } } + for (auto& [index, refs] : memoryRefs) { + for (auto ref : refs) { + *ref = getMemoryName(index); + } + } + for (auto& [index, refs] : globalRefs) { for (auto* ref : refs) { *ref = getGlobalName(index); @@ -2998,12 +3031,21 @@ void WasmBinaryBuilder::readDataSegments() { } curr->setName(Name::fromInt(i), false); curr->isPassive = flags & BinaryConsts::IsPassive; + Index memIdx = 0; if (flags & BinaryConsts::HasIndex) { - auto memIndex = getU32LEB(); - if (memIndex != 0) { - throwError("nonzero memory index"); - } + memIdx = getU32LEB(); + } + Memory* memory = nullptr; + Index numMemoryImports = memoryImports.size(); + if (memIdx < numMemoryImports) { + memory = memoryImports[memIdx]; + } else if (memIdx - numMemoryImports < memories.size()) { + memory = memories[memIdx - numMemoryImports].get(); } + if (!memory) { + throwError("Memory index out of range while reading data segments."); + } + curr->memory = memory->name; if (!curr->isPassive) { curr->offset = readExpression(); } @@ -3333,11 +3375,16 @@ void WasmBinaryBuilder::readNames(size_t payloadLen) { } } else if (nameType == BinaryConsts::UserSections::Subsection::NameMemory) { auto num = getU32LEB(); + NameProcessor processor; for (size_t i = 0; i < num; i++) { auto index = getU32LEB(); auto rawName = getInlineString(); - if (index == 0) { - wasm.memory.setExplicitName(escape(rawName)); + auto name = processor.process(rawName); + auto numMemoryImports = memoryImports.size(); + if (index < numMemoryImports) { + memoryImports[index]->setExplicitName(name); + } else if (index - numMemoryImports < memories.size()) { + memories[index - numMemoryImports]->setExplicitName(name); } else { std::cerr << "warning: memory index out of bounds in name section, " "memory subsection: " @@ -3713,18 +3760,12 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { break; case BinaryConsts::MemorySize: { auto size = allocator.alloc<MemorySize>(); - if (wasm.memory.is64()) { - size->make64(); - } curr = size; visitMemorySize(size); break; } case BinaryConsts::MemoryGrow: { auto grow = allocator.alloc<MemoryGrow>(); - if (wasm.memory.is64()) { - grow->make64(); - } curr = grow; visitMemoryGrow(grow); break; @@ -4304,13 +4345,39 @@ void WasmBinaryBuilder::visitGlobalSet(GlobalSet* curr) { curr->finalize(); } -void WasmBinaryBuilder::readMemoryAccess(Address& alignment, Address& offset) { +Index WasmBinaryBuilder::readMemoryAccess(Address& alignment, Address& offset) { auto rawAlignment = getU32LEB(); - if (rawAlignment > 4) { + bool hasMemIdx = false; + Index memIdx = 0; + // Check bit 6 in the alignment to know whether a memory index is present per: + // https://github.com/WebAssembly/multi-memory/blob/main/proposals/multi-memory/Overview.md + if (rawAlignment & (1 << (6))) { + hasMemIdx = true; + // Clear the bit before we parse alignment + rawAlignment = rawAlignment & ~(1 << 6); + } + + if (rawAlignment > 8) { throwError("Alignment must be of a reasonable size"); } + alignment = Bits::pow2(rawAlignment); - offset = getUPtrLEB(); + if (hasMemIdx) { + memIdx = getU32LEB(); + } + Memory* memory = nullptr; + auto numMemoryImports = memoryImports.size(); + if (memIdx < numMemoryImports) { + memory = memoryImports[memIdx]; + } else if (memIdx - numMemoryImports < memories.size()) { + memory = memories[memIdx - numMemoryImports].get(); + } + if (!memory) { + throwError("Memory index out of range while reading memory alignment."); + } + offset = memory->indexType == Type::i32 ? getU32LEB() : getU64LEB(); + + return memIdx; } bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, @@ -4445,7 +4512,8 @@ bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, } curr->isAtomic = isAtomic; - readMemoryAccess(curr->align, curr->offset); + Index memIdx = readMemoryAccess(curr->align, curr->offset); + memoryRefs[memIdx].push_back(&curr->memory); curr->ptr = popNonVoidExpression(); curr->finalize(); out = curr; @@ -4550,7 +4618,8 @@ bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, curr->isAtomic = isAtomic; BYN_TRACE("zz node: Store\n"); - readMemoryAccess(curr->align, curr->offset); + Index memIdx = readMemoryAccess(curr->align, curr->offset); + memoryRefs[memIdx].push_back(&curr->memory); curr->value = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); curr->finalize(); @@ -4610,7 +4679,8 @@ bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) { BYN_TRACE("zz node: AtomicRMW\n"); Address readAlign; - readMemoryAccess(readAlign, curr->offset); + Index memIdx = readMemoryAccess(readAlign, curr->offset); + memoryRefs[memIdx].push_back(&curr->memory); if (readAlign != curr->bytes) { throwError("Align of AtomicRMW must match size"); } @@ -4662,7 +4732,8 @@ bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out, BYN_TRACE("zz node: AtomicCmpxchg\n"); Address readAlign; - readMemoryAccess(readAlign, curr->offset); + Index memIdx = readMemoryAccess(readAlign, curr->offset); + memoryRefs[memIdx].push_back(&curr->memory); if (readAlign != curr->bytes) { throwError("Align of AtomicCpxchg must match size"); } @@ -4697,7 +4768,8 @@ bool WasmBinaryBuilder::maybeVisitAtomicWait(Expression*& out, uint8_t code) { curr->expected = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); Address readAlign; - readMemoryAccess(readAlign, curr->offset); + Index memIdx = readMemoryAccess(readAlign, curr->offset); + memoryRefs[memIdx].push_back(&curr->memory); if (readAlign != curr->expectedType.getByteSize()) { throwError("Align of AtomicWait must match size"); } @@ -4717,7 +4789,8 @@ bool WasmBinaryBuilder::maybeVisitAtomicNotify(Expression*& out, uint8_t code) { curr->notifyCount = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); Address readAlign; - readMemoryAccess(readAlign, curr->offset); + Index memIdx = readMemoryAccess(readAlign, curr->offset); + memoryRefs[memIdx].push_back(&curr->memory); if (readAlign != curr->type.getByteSize()) { throwError("Align of AtomicNotify must match size"); } @@ -5048,10 +5121,9 @@ bool WasmBinaryBuilder::maybeVisitMemoryInit(Expression*& out, uint32_t code) { curr->offset = popNonVoidExpression(); curr->dest = popNonVoidExpression(); curr->segment = getU32LEB(); - if (getInt8() != 0) { - throwError("Unexpected nonzero memory index"); - } + Index memIdx = getU32LEB(); curr->finalize(); + memoryRefs[memIdx].push_back(&curr->memory); out = curr; return true; } @@ -5075,10 +5147,11 @@ bool WasmBinaryBuilder::maybeVisitMemoryCopy(Expression*& out, uint32_t code) { curr->size = popNonVoidExpression(); curr->source = popNonVoidExpression(); curr->dest = popNonVoidExpression(); - if (getInt8() != 0 || getInt8() != 0) { - throwError("Unexpected nonzero memory index"); - } + Index destIdx = getU32LEB(); + Index sourceIdx = getU32LEB(); curr->finalize(); + memoryRefs[destIdx].push_back(&curr->destMemory); + memoryRefs[sourceIdx].push_back(&curr->sourceMemory); out = curr; return true; } @@ -5091,10 +5164,9 @@ bool WasmBinaryBuilder::maybeVisitMemoryFill(Expression*& out, uint32_t code) { curr->size = popNonVoidExpression(); curr->value = popNonVoidExpression(); curr->dest = popNonVoidExpression(); - if (getInt8() != 0) { - throwError("Unexpected nonzero memory index"); - } + Index memIdx = getU32LEB(); curr->finalize(); + memoryRefs[memIdx].push_back(&curr->memory); out = curr; return true; } @@ -6038,7 +6110,8 @@ bool WasmBinaryBuilder::maybeVisitSIMDStore(Expression*& out, uint32_t code) { auto* curr = allocator.alloc<Store>(); curr->bytes = 16; curr->valueType = Type::v128; - readMemoryAccess(curr->align, curr->offset); + Index memIdx = readMemoryAccess(curr->align, curr->offset); + memoryRefs[memIdx].push_back(&curr->memory); curr->isAtomic = false; curr->value = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); @@ -6277,7 +6350,8 @@ bool WasmBinaryBuilder::maybeVisitSIMDLoad(Expression*& out, uint32_t code) { auto* curr = allocator.alloc<Load>(); curr->type = Type::v128; curr->bytes = 16; - readMemoryAccess(curr->align, curr->offset); + Index memIdx = readMemoryAccess(curr->align, curr->offset); + memoryRefs[memIdx].push_back(&curr->memory); curr->isAtomic = false; curr->ptr = popNonVoidExpression(); curr->finalize(); @@ -6337,7 +6411,8 @@ bool WasmBinaryBuilder::maybeVisitSIMDLoad(Expression*& out, uint32_t code) { default: return false; } - readMemoryAccess(curr->align, curr->offset); + Index memIdx = readMemoryAccess(curr->align, curr->offset); + memoryRefs[memIdx].push_back(&curr->memory); curr->ptr = popNonVoidExpression(); curr->finalize(); out = curr; @@ -6386,7 +6461,8 @@ bool WasmBinaryBuilder::maybeVisitSIMDLoadStoreLane(Expression*& out, } auto* curr = allocator.alloc<SIMDLoadStoreLane>(); curr->op = op; - readMemoryAccess(curr->align, curr->offset); + Index memIdx = readMemoryAccess(curr->align, curr->offset); + memoryRefs[memIdx].push_back(&curr->memory); curr->index = getLaneIndex(lanes); curr->vec = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); @@ -6427,21 +6503,17 @@ void WasmBinaryBuilder::visitReturn(Return* curr) { void WasmBinaryBuilder::visitMemorySize(MemorySize* curr) { BYN_TRACE("zz node: MemorySize\n"); - auto reserved = getU32LEB(); - if (reserved != 0) { - throwError("Invalid reserved field on memory.size"); - } + Index memIdx = getU32LEB(); curr->finalize(); + memoryRefs[memIdx].push_back(&curr->memory); } void WasmBinaryBuilder::visitMemoryGrow(MemoryGrow* curr) { BYN_TRACE("zz node: MemoryGrow\n"); curr->delta = popNonVoidExpression(); - auto reserved = getU32LEB(); - if (reserved != 0) { - throwError("Invalid reserved field on memory.grow"); - } + Index memIdx = getU32LEB(); curr->finalize(); + memoryRefs[memIdx].push_back(&curr->memory); } void WasmBinaryBuilder::visitNop(Nop* curr) { BYN_TRACE("zz node: Nop\n"); } @@ -7270,7 +7342,6 @@ void WasmBinaryBuilder::visitRefAs(RefAs* curr, uint8_t code) { } curr->finalize(); } - void WasmBinaryBuilder::throwError(std::string text) { throw ParseException(text, 0, pos); } diff --git a/src/wasm/wasm-debug.cpp b/src/wasm/wasm-debug.cpp index 23c2dc938..8de691272 100644 --- a/src/wasm/wasm-debug.cpp +++ b/src/wasm/wasm-debug.cpp @@ -1065,7 +1065,8 @@ void writeDWARFSections(Module& wasm, const BinaryLocations& newLocations) { updateDebugLines(data, locationUpdater); - updateCompileUnits(info, data, locationUpdater, wasm.memory.is64()); + bool is64 = wasm.memories.size() > 0 ? wasm.memories[0]->is64() : false; + updateCompileUnits(info, data, locationUpdater, is64); updateRanges(data, locationUpdater); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 382ca741d..55551ef58 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -378,6 +378,7 @@ SExpressionWasmBuilder::SExpressionWasmBuilder(Module& wasm, auto& s = *module[j]; preParseFunctionType(s); preParseImports(s); + preParseMemory(s); if (elementStartsWith(s, FUNC) && !isImport(s)) { implementedFunctions++; } @@ -423,20 +424,27 @@ void SExpressionWasmBuilder::preParseImports(Element& curr) { } } +void SExpressionWasmBuilder::preParseMemory(Element& curr) { + IString id = curr[0]->str(); + if (id == MEMORY && !isImport(curr)) { + parseMemory(curr); + } +} + void SExpressionWasmBuilder::parseModuleElement(Element& curr) { if (isImport(curr)) { return; // already done } IString id = curr[0]->str(); + if (id == MEMORY) { + return; // already done + } if (id == START) { return parseStart(curr); } if (id == FUNC) { return parseFunction(curr); } - if (id == MEMORY) { - return parseMemory(curr); - } if (id == DATA) { return parseData(curr); } @@ -495,6 +503,31 @@ Name SExpressionWasmBuilder::getTableName(Element& s) { } } +bool SExpressionWasmBuilder::isMemory64(Name memoryName) { + auto* memory = wasm.getMemoryOrNull(memoryName); + if (!memory) { + throw ParseException("invalid memory name in isMemory64"); + } + return memory->is64(); +} + +Name SExpressionWasmBuilder::getMemoryNameAtIdx(Index i) { + if (i >= memoryNames.size()) { + throw ParseException("unknown memory in getMemoryName"); + } + return memoryNames[i]; +} + +Name SExpressionWasmBuilder::getMemoryName(Element& s) { + if (s.dollared()) { + return s.str(); + } else { + // index + size_t offset = atoi(s.str().c_str()); + return getMemoryNameAtIdx(offset); + } +} + Name SExpressionWasmBuilder::getGlobalName(Element& s) { if (s.dollared()) { return s.str(); @@ -1359,7 +1392,15 @@ Expression* SExpressionWasmBuilder::makeDrop(Element& s) { Expression* SExpressionWasmBuilder::makeMemorySize(Element& s) { auto ret = allocator.alloc<MemorySize>(); - if (wasm.memory.is64()) { + Index i = 1; + Name memory; + if (s.size() > 1) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; + if (isMemory64(memory)) { ret->make64(); } ret->finalize(); @@ -1368,10 +1409,18 @@ Expression* SExpressionWasmBuilder::makeMemorySize(Element& s) { Expression* SExpressionWasmBuilder::makeMemoryGrow(Element& s) { auto ret = allocator.alloc<MemoryGrow>(); - if (wasm.memory.is64()) { + Index i = 1; + Name memory; + if (s.size() > 2) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; + if (isMemory64(memory)) { ret->make64(); } - ret->delta = parseExpression(s[1]); + ret->delta = parseExpression(s[i]); ret->finalize(); return ret; } @@ -1820,11 +1869,11 @@ static uint8_t parseMemBytes(const char*& s, uint8_t fallback) { return ret; } -static size_t parseMemAttributes(Element& s, +static size_t parseMemAttributes(size_t i, + Element& s, Address& offset, Address& align, Address fallbackAlign) { - size_t i = 1; offset = 0; align = fallbackAlign; // Parse "align=X" and "offset=X" arguments, bailing out on anything else. @@ -1884,6 +1933,17 @@ static const char* findMemExtra(const Element& s, size_t skip, bool isAtomic) { return ret; } +bool SExpressionWasmBuilder::hasMemoryIdx(Element& s, + Index defaultSize, + Index i) { + if (s.size() > defaultSize && !s[i]->isList() && + strncmp(s[i]->c_str(), "align", 5) != 0 && + strncmp(s[i]->c_str(), "offset", 6) != 0) { + return true; + } + return false; +} + Expression* SExpressionWasmBuilder::makeLoad(Element& s, Type type, bool isAtomic) { const char* extra = findMemExtra(*s[0], 5 /* after "type.load" */, isAtomic); @@ -1892,7 +1952,17 @@ SExpressionWasmBuilder::makeLoad(Element& s, Type type, bool isAtomic) { ret->type = type; ret->bytes = parseMemBytes(extra, type.getByteSize()); ret->signed_ = extra[0] && extra[1] == 's'; - size_t i = parseMemAttributes(s, ret->offset, ret->align, ret->bytes); + Index i = 1; + Name memory; + // Check to make sure there are more than the default args & this str isn't + // the mem attributes + if (hasMemoryIdx(s, 2, i)) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; + i = parseMemAttributes(i, s, ret->offset, ret->align, ret->bytes); ret->ptr = parseExpression(s[i]); ret->finalize(); return ret; @@ -1905,7 +1975,17 @@ SExpressionWasmBuilder::makeStore(Element& s, Type type, bool isAtomic) { ret->isAtomic = isAtomic; ret->valueType = type; ret->bytes = parseMemBytes(extra, type.getByteSize()); - size_t i = parseMemAttributes(s, ret->offset, ret->align, ret->bytes); + Index i = 1; + Name memory; + // Check to make sure there are more than the default args & this str isn't + // the mem attributes + if (hasMemoryIdx(s, 3, i)) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; + i = parseMemAttributes(i, s, ret->offset, ret->align, ret->bytes); ret->ptr = parseExpression(s[i]); ret->value = parseExpression(s[i + 1]); ret->finalize(); @@ -1927,7 +2007,6 @@ Expression* SExpressionWasmBuilder::makeAtomicRMWOrCmpxchg(Element& s, } return makeAtomicRMW(s, type, bytes, extra); } - Expression* SExpressionWasmBuilder::makeAtomicRMW(Element& s, Type type, uint8_t bytes, @@ -1950,8 +2029,18 @@ Expression* SExpressionWasmBuilder::makeAtomicRMW(Element& s, } else { throw ParseException("bad atomic rmw operator", s.line, s.col); } + Index i = 1; + Name memory; + // Check to make sure there are more than the default args & this str isn't + // the mem attributes + if (hasMemoryIdx(s, 3, i)) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; Address align; - size_t i = parseMemAttributes(s, ret->offset, align, ret->bytes); + i = parseMemAttributes(i, s, ret->offset, align, ret->bytes); if (align != ret->bytes) { throw ParseException("Align of Atomic RMW must match size", s.line, s.col); } @@ -1968,8 +2057,18 @@ Expression* SExpressionWasmBuilder::makeAtomicCmpxchg(Element& s, auto ret = allocator.alloc<AtomicCmpxchg>(); ret->type = type; ret->bytes = bytes; + Index i = 1; Address align; - size_t i = parseMemAttributes(s, ret->offset, align, ret->bytes); + Name memory; + // Check to make sure there are more than the default args & this str isn't + // the mem attributes + if (hasMemoryIdx(s, 4, i)) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; + i = parseMemAttributes(i, s, ret->offset, align, ret->bytes); if (align != ret->bytes) { throw ParseException( "Align of Atomic Cmpxchg must match size", s.line, s.col); @@ -1994,7 +2093,17 @@ Expression* SExpressionWasmBuilder::makeAtomicWait(Element& s, Type type) { } else { WASM_UNREACHABLE("Invalid prefix for memory.atomic.wait"); } - size_t i = parseMemAttributes(s, ret->offset, align, expectedAlign); + Index i = 1; + Name memory; + // Check to make sure there are more than the default args & this str isn't + // the mem attributes + if (hasMemoryIdx(s, 4, i)) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; + i = parseMemAttributes(i, s, ret->offset, align, expectedAlign); if (align != expectedAlign) { throw ParseException( "Align of memory.atomic.wait must match size", s.line, s.col); @@ -2009,8 +2118,18 @@ Expression* SExpressionWasmBuilder::makeAtomicWait(Element& s, Type type) { Expression* SExpressionWasmBuilder::makeAtomicNotify(Element& s) { auto ret = allocator.alloc<AtomicNotify>(); ret->type = Type::i32; + Index i = 1; + Name memory; + // Check to make sure there are more than the default args & this str isn't + // the mem attributes + if (hasMemoryIdx(s, 3, i)) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; Address align; - size_t i = parseMemAttributes(s, ret->offset, align, 4); + i = parseMemAttributes(i, s, ret->offset, align, 4); if (align != 4) { throw ParseException( "Align of memory.atomic.notify must be 4", s.line, s.col); @@ -2119,7 +2238,17 @@ Expression* SExpressionWasmBuilder::makeSIMDLoad(Element& s, SIMDLoadOp op) { defaultAlign = 8; break; } - size_t i = parseMemAttributes(s, ret->offset, ret->align, defaultAlign); + Index i = 1; + Name memory; + // Check to make sure there are more than the default args & this str isn't + // the mem attributes + if (hasMemoryIdx(s, 2, i)) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; + i = parseMemAttributes(i, s, ret->offset, ret->align, defaultAlign); ret->ptr = parseExpression(s[i]); ret->finalize(); return ret; @@ -2156,7 +2285,17 @@ SExpressionWasmBuilder::makeSIMDLoadStoreLane(Element& s, default: WASM_UNREACHABLE("Unexpected SIMDLoadStoreLane op"); } - size_t i = parseMemAttributes(s, ret->offset, ret->align, defaultAlign); + Index i = 1; + Name memory; + // Check to make sure there are more than the default args & this str isn't + // the mem attributes + if (hasMemoryIdx(s, 4, i)) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; + i = parseMemAttributes(i, s, ret->offset, ret->align, defaultAlign); ret->index = parseLaneIndex(s[i++], lanes); ret->ptr = parseExpression(s[i++]); ret->vec = parseExpression(s[i]); @@ -2166,10 +2305,18 @@ SExpressionWasmBuilder::makeSIMDLoadStoreLane(Element& s, Expression* SExpressionWasmBuilder::makeMemoryInit(Element& s) { auto ret = allocator.alloc<MemoryInit>(); - ret->segment = atoi(s[1]->str().c_str()); - ret->dest = parseExpression(s[2]); - ret->offset = parseExpression(s[3]); - ret->size = parseExpression(s[4]); + Index i = 1; + Name memory; + if (s.size() > 5) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; + ret->segment = atoi(s[i++]->str().c_str()); + ret->dest = parseExpression(s[i++]); + ret->offset = parseExpression(s[i++]); + ret->size = parseExpression(s[i]); ret->finalize(); return ret; } @@ -2183,18 +2330,38 @@ Expression* SExpressionWasmBuilder::makeDataDrop(Element& s) { Expression* SExpressionWasmBuilder::makeMemoryCopy(Element& s) { auto ret = allocator.alloc<MemoryCopy>(); - ret->dest = parseExpression(s[1]); - ret->source = parseExpression(s[2]); - ret->size = parseExpression(s[3]); + Index i = 1; + Name destMemory; + Name sourceMemory; + if (s.size() > 4) { + destMemory = getMemoryName(*s[i++]); + sourceMemory = getMemoryName(*s[i++]); + } else { + destMemory = getMemoryNameAtIdx(0); + sourceMemory = getMemoryNameAtIdx(0); + } + ret->destMemory = destMemory; + ret->sourceMemory = sourceMemory; + ret->dest = parseExpression(s[i++]); + ret->source = parseExpression(s[i++]); + ret->size = parseExpression(s[i]); ret->finalize(); return ret; } Expression* SExpressionWasmBuilder::makeMemoryFill(Element& s) { auto ret = allocator.alloc<MemoryFill>(); - ret->dest = parseExpression(s[1]); - ret->value = parseExpression(s[2]); - ret->size = parseExpression(s[3]); + Index i = 1; + Name memory; + if (s.size() > 4) { + memory = getMemoryName(*s[i++]); + } else { + memory = getMemoryNameAtIdx(0); + } + ret->memory = memory; + ret->dest = parseExpression(s[i++]); + ret->value = parseExpression(s[i++]); + ret->size = parseExpression(s[i]); ret->finalize(); return ret; } @@ -2995,35 +3162,37 @@ void SExpressionWasmBuilder::stringToBinary(const char* input, data.resize(actual); } -Index SExpressionWasmBuilder::parseMemoryIndex(Element& s, Index i) { +Index SExpressionWasmBuilder::parseMemoryIndex( + Element& s, Index i, std::unique_ptr<Memory>& memory) { if (i < s.size() && s[i]->isStr()) { if (s[i]->str() == "i64") { i++; - wasm.memory.indexType = Type::i64; + memory->indexType = Type::i64; } else if (s[i]->str() == "i32") { i++; - wasm.memory.indexType = Type::i32; + memory->indexType = Type::i32; } } return i; } -Index SExpressionWasmBuilder::parseMemoryLimits(Element& s, Index i) { - i = parseMemoryIndex(s, i); +Index SExpressionWasmBuilder::parseMemoryLimits( + Element& s, Index i, std::unique_ptr<Memory>& memory) { + i = parseMemoryIndex(s, i, memory); if (i == s.size()) { throw ParseException("missing memory limits", s.line, s.col); } auto initElem = s[i++]; - wasm.memory.initial = getAddress(initElem); - if (!wasm.memory.is64()) { - checkAddress(wasm.memory.initial, "excessive memory init", initElem); + memory->initial = getAddress(initElem); + if (!memory->is64()) { + checkAddress(memory->initial, "excessive memory init", initElem); } if (i == s.size()) { - wasm.memory.max = Memory::kUnlimitedSize; + memory->max = Memory::kUnlimitedSize; } else { auto maxElem = s[i++]; - wasm.memory.max = getAddress(maxElem); - if (!wasm.memory.is64() && wasm.memory.max > Memory::kMaxSize32) { + memory->max = getAddress(maxElem); + if (!memory->is64() && memory->max > Memory::kMaxSize32) { throw ParseException( "total memory must be <= 4GB", maxElem->line, maxElem->col); } @@ -3032,23 +3201,24 @@ Index SExpressionWasmBuilder::parseMemoryLimits(Element& s, Index i) { } void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { - if (wasm.memory.exists) { - throw ParseException("too many memories", s.line, s.col); - } - wasm.memory.exists = true; - wasm.memory.shared = false; + auto memory = make_unique<Memory>(); + memory->shared = false; Index i = 1; if (s[i]->dollared()) { - wasm.memory.setExplicitName(s[i++]->str()); + memory->setExplicitName(s[i++]->str()); + } else { + memory->name = Name::fromInt(memoryCounter++); } - i = parseMemoryIndex(s, i); + memoryNames.push_back(memory->name); + + i = parseMemoryIndex(s, i, memory); Name importModule, importBase; if (s[i]->isList()) { auto& inner = *s[i]; if (elementStartsWith(inner, EXPORT)) { auto ex = make_unique<Export>(); ex->name = inner[1]->str(); - ex->value = wasm.memory.name; + ex->value = memory->name; ex->kind = ExternalKind::Memory; if (wasm.getExportOrNull(ex->name)) { throw ParseException("duplicate export", inner.line, inner.col); @@ -3056,33 +3226,36 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { wasm.addExport(ex.release()); i++; } else if (elementStartsWith(inner, IMPORT)) { - wasm.memory.module = inner[1]->str(); - wasm.memory.base = inner[2]->str(); + memory->module = inner[1]->str(); + memory->base = inner[2]->str(); i++; } else if (elementStartsWith(inner, SHARED)) { - wasm.memory.shared = true; - parseMemoryLimits(inner, 1); + memory->shared = true; + parseMemoryLimits(inner, 1, memory); i++; } else { if (!(inner.size() > 0 ? inner[0]->str() != IMPORT : true)) { throw ParseException("bad import ending", inner.line, inner.col); } // (memory (data ..)) format - auto j = parseMemoryIndex(inner, 1); + auto j = parseMemoryIndex(inner, 1, memory); auto offset = allocator.alloc<Const>(); - if (wasm.memory.is64()) { + if (memory->is64()) { offset->set(Literal(int64_t(0))); } else { offset->set(Literal(int32_t(0))); } - parseInnerData( - inner, j, Name::fromInt(dataCounter++), false, offset, false); - wasm.memory.initial = wasm.dataSegments[0]->data.size(); + auto seg = Builder::makeDataSegment( + Name::fromInt(dataCounter++), memory->name, false, offset); + parseInnerData(inner, j, seg); + memory->initial = seg->data.size(); + wasm.addDataSegment(std::move(seg)); + wasm.addMemory(std::move(memory)); return; } } - if (!wasm.memory.shared) { - i = parseMemoryLimits(s, i); + if (!memory->shared) { + i = parseMemoryLimits(s, i, memory); } // Parse memory initializers. @@ -3095,13 +3268,13 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { } else { auto offsetElem = curr[j++]; offsetValue = getAddress(offsetElem); - if (!wasm.memory.is64()) { + if (!memory->is64()) { checkAddress(offsetValue, "excessive memory offset", offsetElem); } } const char* input = curr[j]->c_str(); auto* offset = allocator.alloc<Const>(); - if (wasm.memory.is64()) { + if (memory->is64()) { offset->type = Type::i64; offset->value = Literal(offsetValue); } else { @@ -3111,27 +3284,33 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { if (auto size = strlen(input)) { std::vector<char> data; stringToBinary(input, size, data); - auto segment = Builder::makeDataSegment( - Name::fromInt(dataCounter++), false, offset, data.data(), data.size()); + auto segment = Builder::makeDataSegment(Name::fromInt(dataCounter++), + memory->name, + false, + offset, + data.data(), + data.size()); segment->hasExplicitName = false; - wasm.dataSegments.push_back(std::move(segment)); + wasm.addDataSegment(std::move(segment)); } else { - auto segment = - Builder::makeDataSegment(Name::fromInt(dataCounter++), false, offset); + auto segment = Builder::makeDataSegment( + Name::fromInt(dataCounter++), memory->name, false, offset); segment->hasExplicitName = false; - wasm.dataSegments.push_back(std::move(segment)); + wasm.addDataSegment(std::move(segment)); } i++; } + wasm.addMemory(std::move(memory)); } void SExpressionWasmBuilder::parseData(Element& s) { - if (!wasm.memory.exists) { + if (wasm.memories.empty()) { throw ParseException("data but no memory", s.line, s.col); } Index i = 1; Name name = Name::fromInt(dataCounter++); bool hasExplicitName = false; + Name memory; bool isPassive = true; Expression* offset = nullptr; @@ -3143,11 +3322,11 @@ void SExpressionWasmBuilder::parseData(Element& s) { if (s[i]->isList()) { // Optional (memory <memoryidx>) if (elementStartsWith(s[i], MEMORY)) { - // TODO: we're just skipping memory since we have only one. Assign the - // memory name to the segment when we support multiple memories. - i += 1; + auto& inner = *s[i++]; + memory = getMemoryName(*inner[1]); + } else { + memory = getMemoryNameAtIdx(0); } - // Offset expression (offset (<expr>)) | (<expr>) auto& inner = *s[i++]; if (elementStartsWith(inner, OFFSET)) { @@ -3158,15 +3337,15 @@ void SExpressionWasmBuilder::parseData(Element& s) { isPassive = false; } - parseInnerData(s, i, name, hasExplicitName, offset, isPassive); + auto seg = Builder::makeDataSegment(name, memory, isPassive, offset); + seg->hasExplicitName = hasExplicitName; + parseInnerData(s, i, seg); + wasm.addDataSegment(std::move(seg)); } void SExpressionWasmBuilder::parseInnerData(Element& s, Index i, - Name name, - bool hasExplicitName, - Expression* offset, - bool isPassive) { + std::unique_ptr<DataSegment>& seg) { std::vector<char> data; while (i < s.size()) { const char* input = s[i++]->c_str(); @@ -3174,10 +3353,8 @@ void SExpressionWasmBuilder::parseInnerData(Element& s, stringToBinary(input, size, data); } } - auto curr = - Builder::makeDataSegment(name, isPassive, offset, data.data(), data.size()); - curr->hasExplicitName = hasExplicitName; - wasm.dataSegments.push_back(std::move(curr)); + seg->data.resize(data.size()); + std::copy_n(data.data(), data.size(), seg->data.begin()); } void SExpressionWasmBuilder::parseExport(Element& s) { @@ -3224,10 +3401,6 @@ void SExpressionWasmBuilder::parseImport(Element& s) { kind = ExternalKind::Function; } else if (elementStartsWith(*s[3], MEMORY)) { kind = ExternalKind::Memory; - if (wasm.memory.exists) { - throw ParseException("more than one memory", s[3]->line, s[3]->col); - } - wasm.memory.exists = true; } else if (elementStartsWith(*s[3], TABLE)) { kind = ExternalKind::Table; } else if (elementStartsWith(*s[3], GLOBAL)) { @@ -3320,20 +3493,25 @@ void SExpressionWasmBuilder::parseImport(Element& s) { j++; // funcref // ends with the table element type } else if (kind == ExternalKind::Memory) { - wasm.memory.setName(name, hasExplicitName); - wasm.memory.module = module; - wasm.memory.base = base; + auto memory = make_unique<Memory>(); + memory->setName(name, hasExplicitName); + memory->module = module; + memory->base = base; + memoryNames.push_back(name); + if (inner[j]->isList()) { auto& limits = *inner[j]; if (!elementStartsWith(limits, SHARED)) { throw ParseException( "bad memory limit declaration", inner[j]->line, inner[j]->col); } - wasm.memory.shared = true; - j = parseMemoryLimits(limits, 1); + memory->shared = true; + j = parseMemoryLimits(limits, 1, memory); } else { - j = parseMemoryLimits(inner, j); + j = parseMemoryLimits(inner, j, memory); } + + wasm.addMemory(std::move(memory)); } else if (kind == ExternalKind::Tag) { auto tag = make_unique<Tag>(); HeapType tagType; diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index b887f6ec5..13bd09927 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -234,7 +234,7 @@ void BinaryInstWriter::visitLoad(Load* curr) { WASM_UNREACHABLE("unexpected type"); } } - emitMemoryAccess(curr->align, curr->bytes, curr->offset); + emitMemoryAccess(curr->align, curr->bytes, curr->offset, curr->memory); } void BinaryInstWriter::visitStore(Store* curr) { @@ -331,7 +331,7 @@ void BinaryInstWriter::visitStore(Store* curr) { WASM_UNREACHABLE("unexpected type"); } } - emitMemoryAccess(curr->align, curr->bytes, curr->offset); + emitMemoryAccess(curr->align, curr->bytes, curr->offset, curr->memory); } void BinaryInstWriter::visitAtomicRMW(AtomicRMW* curr) { @@ -390,7 +390,7 @@ void BinaryInstWriter::visitAtomicRMW(AtomicRMW* curr) { } #undef CASE_FOR_OP - emitMemoryAccess(curr->bytes, curr->bytes, curr->offset); + emitMemoryAccess(curr->bytes, curr->bytes, curr->offset, curr->memory); } void BinaryInstWriter::visitAtomicCmpxchg(AtomicCmpxchg* curr) { @@ -432,7 +432,7 @@ void BinaryInstWriter::visitAtomicCmpxchg(AtomicCmpxchg* curr) { default: WASM_UNREACHABLE("unexpected type"); } - emitMemoryAccess(curr->bytes, curr->bytes, curr->offset); + emitMemoryAccess(curr->bytes, curr->bytes, curr->offset, curr->memory); } void BinaryInstWriter::visitAtomicWait(AtomicWait* curr) { @@ -440,12 +440,12 @@ void BinaryInstWriter::visitAtomicWait(AtomicWait* curr) { switch (curr->expectedType.getBasic()) { case Type::i32: { o << int8_t(BinaryConsts::I32AtomicWait); - emitMemoryAccess(4, 4, curr->offset); + emitMemoryAccess(4, 4, curr->offset, curr->memory); break; } case Type::i64: { o << int8_t(BinaryConsts::I64AtomicWait); - emitMemoryAccess(8, 8, curr->offset); + emitMemoryAccess(8, 8, curr->offset, curr->memory); break; } default: @@ -455,7 +455,7 @@ void BinaryInstWriter::visitAtomicWait(AtomicWait* curr) { void BinaryInstWriter::visitAtomicNotify(AtomicNotify* curr) { o << int8_t(BinaryConsts::AtomicPrefix) << int8_t(BinaryConsts::AtomicNotify); - emitMemoryAccess(4, 4, curr->offset); + emitMemoryAccess(4, 4, curr->offset, curr->memory); } void BinaryInstWriter::visitAtomicFence(AtomicFence* curr) { @@ -646,7 +646,8 @@ void BinaryInstWriter::visitSIMDLoad(SIMDLoad* curr) { break; } assert(curr->align); - emitMemoryAccess(curr->align, /*(unused) bytes=*/0, curr->offset); + emitMemoryAccess( + curr->align, /*(unused) bytes=*/0, curr->offset, curr->memory); } void BinaryInstWriter::visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) { @@ -678,14 +679,15 @@ void BinaryInstWriter::visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) { break; } assert(curr->align); - emitMemoryAccess(curr->align, /*(unused) bytes=*/0, curr->offset); + emitMemoryAccess( + curr->align, /*(unused) bytes=*/0, curr->offset, curr->memory); o << curr->index; } void BinaryInstWriter::visitMemoryInit(MemoryInit* curr) { o << int8_t(BinaryConsts::MiscPrefix); o << U32LEB(BinaryConsts::MemoryInit); - o << U32LEB(curr->segment) << int8_t(0); + o << U32LEB(curr->segment) << int8_t(parent.getMemoryIndex(curr->memory)); } void BinaryInstWriter::visitDataDrop(DataDrop* curr) { @@ -697,13 +699,14 @@ void BinaryInstWriter::visitDataDrop(DataDrop* curr) { void BinaryInstWriter::visitMemoryCopy(MemoryCopy* curr) { o << int8_t(BinaryConsts::MiscPrefix); o << U32LEB(BinaryConsts::MemoryCopy); - o << int8_t(0) << int8_t(0); + o << int8_t(parent.getMemoryIndex(curr->destMemory)) + << int8_t(parent.getMemoryIndex(curr->sourceMemory)); } void BinaryInstWriter::visitMemoryFill(MemoryFill* curr) { o << int8_t(BinaryConsts::MiscPrefix); o << U32LEB(BinaryConsts::MemoryFill); - o << int8_t(0); + o << int8_t(parent.getMemoryIndex(curr->memory)); } void BinaryInstWriter::visitConst(Const* curr) { @@ -1859,12 +1862,12 @@ void BinaryInstWriter::visitReturn(Return* curr) { void BinaryInstWriter::visitMemorySize(MemorySize* curr) { o << int8_t(BinaryConsts::MemorySize); - o << U32LEB(0); // Reserved flags field + o << U32LEB(parent.getMemoryIndex(curr->memory)); } void BinaryInstWriter::visitMemoryGrow(MemoryGrow* curr) { o << int8_t(BinaryConsts::MemoryGrow); - o << U32LEB(0); // Reserved flags field + o << U32LEB(parent.getMemoryIndex(curr->memory)); } void BinaryInstWriter::visitRefNull(RefNull* curr) { @@ -2476,8 +2479,19 @@ void BinaryInstWriter::setScratchLocals() { void BinaryInstWriter::emitMemoryAccess(size_t alignment, size_t bytes, - uint32_t offset) { - o << U32LEB(Bits::log2(alignment ? alignment : bytes)); + uint32_t offset, + Name memory) { + uint32_t alignmentBits = Bits::log2(alignment ? alignment : bytes); + uint32_t memoryIdx = parent.getMemoryIndex(memory); + if (memoryIdx > 0) { + // Set bit 6 in the alignment to indicate a memory index is present per: + // https://github.com/WebAssembly/multi-memory/blob/main/proposals/multi-memory/Overview.md + alignmentBits = alignmentBits | 1 << 6; + } + o << U32LEB(alignmentBits); + if (memoryIdx > 0) { + o << U32LEB(memoryIdx); + } o << U32LEB(offset); } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index b7354ff3f..92f6c76bf 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -499,7 +499,10 @@ private: validateCallParamsAndResult(curr, sigType, curr); } - Type indexType() { return getModule()->memory.indexType; } + Type indexType(Name memoryName) { + auto memory = getModule()->getMemory(memoryName); + return memory->indexType; + } }; void FunctionValidator::noteLabelName(Name name) { @@ -934,8 +937,8 @@ void FunctionValidator::visitGlobalSet(GlobalSet* curr) { } void FunctionValidator::visitLoad(Load* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.load memory must exist"); if (curr->isAtomic) { shouldBeTrue(getModule()->features.hasAtomics(), curr, @@ -954,7 +957,7 @@ void FunctionValidator::visitLoad(Load* curr) { validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr); shouldBeEqualOrFirstIsUnreachable( curr->ptr->type, - indexType(), + indexType(curr->memory), curr, "load pointer type must match memory index type"); if (curr->isAtomic) { @@ -965,8 +968,8 @@ void FunctionValidator::visitLoad(Load* curr) { } void FunctionValidator::visitStore(Store* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.store memory must exist"); if (curr->isAtomic) { shouldBeTrue(getModule()->features.hasAtomics(), curr, @@ -986,7 +989,7 @@ void FunctionValidator::visitStore(Store* curr) { curr->align, curr->valueType, curr->bytes, curr->isAtomic, curr); shouldBeEqualOrFirstIsUnreachable( curr->ptr->type, - indexType(), + indexType(curr->memory), curr, "store pointer must match memory index type"); shouldBeUnequal(curr->value->type, @@ -1002,15 +1005,15 @@ void FunctionValidator::visitStore(Store* curr) { } void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.atomicRMW memory must exist"); shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); validateMemBytes(curr->bytes, curr->type, curr); shouldBeEqualOrFirstIsUnreachable( curr->ptr->type, - indexType(), + indexType(curr->memory), curr, "AtomicRMW pointer type must match memory index type"); shouldBeEqualOrFirstIsUnreachable(curr->type, @@ -1022,15 +1025,15 @@ void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) { } void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.atomicCmpxchg memory must exist"); shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); validateMemBytes(curr->bytes, curr->type, curr); shouldBeEqualOrFirstIsUnreachable( curr->ptr->type, - indexType(), + indexType(curr->memory), curr, "cmpxchg pointer must match memory index type"); if (curr->expected->type != Type::unreachable && @@ -1055,8 +1058,8 @@ void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) { } void FunctionValidator::visitAtomicWait(AtomicWait* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.atomicWait memory must exist"); shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); @@ -1064,7 +1067,7 @@ void FunctionValidator::visitAtomicWait(AtomicWait* curr) { curr->type, Type(Type::i32), curr, "AtomicWait must have type i32"); shouldBeEqualOrFirstIsUnreachable( curr->ptr->type, - indexType(), + indexType(curr->memory), curr, "AtomicWait pointer must match memory index type"); shouldBeIntOrUnreachable( @@ -1081,8 +1084,8 @@ void FunctionValidator::visitAtomicWait(AtomicWait* curr) { } void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.atomicNotify memory must exist"); shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); @@ -1090,7 +1093,7 @@ void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) { curr->type, Type(Type::i32), curr, "AtomicNotify must have type i32"); shouldBeEqualOrFirstIsUnreachable( curr->ptr->type, - indexType(), + indexType(curr->memory), curr, "AtomicNotify pointer must match memory index type"); shouldBeEqualOrFirstIsUnreachable( @@ -1101,8 +1104,8 @@ void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) { } void FunctionValidator::visitAtomicFence(AtomicFence* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeFalse( + getModule()->memories.empty(), curr, "Memory operations require a memory"); shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); @@ -1240,15 +1243,15 @@ void FunctionValidator::visitSIMDShift(SIMDShift* curr) { } void FunctionValidator::visitSIMDLoad(SIMDLoad* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.SIMDLoad memory must exist"); shouldBeTrue( getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::v128), curr, "load_splat must have type v128"); shouldBeEqualOrFirstIsUnreachable( curr->ptr->type, - indexType(), + indexType(curr->memory), curr, "load_splat address must match memory index type"); Type memAlignType = Type::none; @@ -1275,8 +1278,8 @@ void FunctionValidator::visitSIMDLoad(SIMDLoad* curr) { } void FunctionValidator::visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.SIMDLoadStoreLane memory must exist"); shouldBeTrue( getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); if (curr->isLoad()) { @@ -1288,7 +1291,7 @@ void FunctionValidator::visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) { } shouldBeEqualOrFirstIsUnreachable( curr->ptr->type, - indexType(), + indexType(curr->memory), curr, "loadX_lane or storeX_lane address must match memory index type"); shouldBeEqualOrFirstIsUnreachable( @@ -1335,7 +1338,7 @@ void FunctionValidator::visitMemoryInit(MemoryInit* curr) { curr->type, Type(Type::none), curr, "memory.init must have type none"); shouldBeEqualOrFirstIsUnreachable( curr->dest->type, - indexType(), + indexType(curr->memory), curr, "memory.init dest must match memory index type"); shouldBeEqualOrFirstIsUnreachable(curr->offset->type, @@ -1344,9 +1347,8 @@ void FunctionValidator::visitMemoryInit(MemoryInit* curr) { "memory.init offset must be an i32"); shouldBeEqualOrFirstIsUnreachable( curr->size->type, Type(Type::i32), curr, "memory.init size must be an i32"); - if (!shouldBeTrue(getModule()->memory.exists, - curr, - "Memory operations require a memory")) { + auto* memory = getModule()->getMemoryOrNull(curr->memory); + if (!shouldBeTrue(!!memory, curr, "memory.init memory must exist")) { return; } shouldBeTrue(curr->segment < getModule()->dataSegments.size(), @@ -1360,9 +1362,9 @@ void FunctionValidator::visitDataDrop(DataDrop* curr) { "Bulk memory operation (bulk memory is disabled)"); shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::none), curr, "data.drop must have type none"); - if (!shouldBeTrue(getModule()->memory.exists, - curr, - "Memory operations require a memory")) { + if (!shouldBeFalse(getModule()->memories.empty(), + curr, + "Memory operations require a memory")) { return; } shouldBeTrue(curr->segment < getModule()->dataSegments.size(), @@ -1376,23 +1378,30 @@ void FunctionValidator::visitMemoryCopy(MemoryCopy* curr) { "Bulk memory operation (bulk memory is disabled)"); shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::none), curr, "memory.copy must have type none"); + auto* destMemory = getModule()->getMemoryOrNull(curr->destMemory); + shouldBeTrue(!!destMemory, curr, "memory.copy destMemory must exist"); + auto* sourceMemory = getModule()->getMemoryOrNull(curr->sourceMemory); + shouldBeTrue(!!sourceMemory, curr, "memory.copy sourceMemory must exist"); shouldBeEqualOrFirstIsUnreachable( curr->dest->type, - indexType(), + indexType(curr->destMemory), curr, - "memory.copy dest must match memory index type"); + "memory.copy dest must match destMemory index type"); shouldBeEqualOrFirstIsUnreachable( curr->source->type, - indexType(), + indexType(curr->sourceMemory), curr, - "memory.copy source must match memory index type"); + "memory.copy source must match sourceMemory index type"); shouldBeEqualOrFirstIsUnreachable( curr->size->type, - indexType(), + indexType(curr->destMemory), curr, - "memory.copy size must match memory index type"); - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + "memory.copy size must match destMemory index type"); + shouldBeEqualOrFirstIsUnreachable( + curr->size->type, + indexType(curr->sourceMemory), + curr, + "memory.copy size must match destMemory index type"); } void FunctionValidator::visitMemoryFill(MemoryFill* curr) { @@ -1403,7 +1412,7 @@ void FunctionValidator::visitMemoryFill(MemoryFill* curr) { curr->type, Type(Type::none), curr, "memory.fill must have type none"); shouldBeEqualOrFirstIsUnreachable( curr->dest->type, - indexType(), + indexType(curr->memory), curr, "memory.fill dest must match memory index type"); shouldBeEqualOrFirstIsUnreachable(curr->value->type, @@ -1412,11 +1421,11 @@ void FunctionValidator::visitMemoryFill(MemoryFill* curr) { "memory.fill value must be an i32"); shouldBeEqualOrFirstIsUnreachable( curr->size->type, - indexType(), + indexType(curr->memory), curr, "memory.fill size must match memory index type"); - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.fill memory must exist"); } void FunctionValidator::validateMemBytes(uint8_t bytes, @@ -2020,15 +2029,15 @@ void FunctionValidator::visitReturn(Return* curr) { } void FunctionValidator::visitMemorySize(MemorySize* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.size memory must exist"); } void FunctionValidator::visitMemoryGrow(MemoryGrow* curr) { - shouldBeTrue( - getModule()->memory.exists, curr, "Memory operations require a memory"); + auto* memory = getModule()->getMemoryOrNull(curr->memory); + shouldBeTrue(!!memory, curr, "memory.grow memory must exist"); shouldBeEqualOrFirstIsUnreachable(curr->delta->type, - indexType(), + indexType(curr->memory), curr, "memory.grow must match memory index type"); } @@ -2940,7 +2949,7 @@ static void validateExports(Module& module, ValidationInfo& info) { name, "module table exports must be found"); } else if (exp->kind == ExternalKind::Memory) { - info.shouldBeTrue(name == Name("0") || name == module.memory.name, + info.shouldBeTrue(module.getMemoryOrNull(name), name, "module memory exports must be found"); } else if (exp->kind == ExternalKind::Tag) { @@ -2982,25 +2991,28 @@ static void validateGlobals(Module& module, ValidationInfo& info) { } static void validateMemory(Module& module, ValidationInfo& info) { - auto& curr = module.memory; + if (module.memories.empty()) { + return; + } + auto& curr = module.memories[0]; info.shouldBeFalse( - curr.initial > curr.max, "memory", "memory max >= initial"); - if (curr.is64()) { + curr->initial > curr->max, "memory", "memory max >= initial"); + if (curr->is64()) { info.shouldBeTrue(module.features.hasMemory64(), "memory", "memory is 64-bit, but memory64 is disabled"); } else { - info.shouldBeTrue(curr.initial <= Memory::kMaxSize32, + info.shouldBeTrue(curr->initial <= Memory::kMaxSize32, "memory", "initial memory must be <= 4GB"); - info.shouldBeTrue(!curr.hasMax() || curr.max <= Memory::kMaxSize32, + info.shouldBeTrue(!curr->hasMax() || curr->max <= Memory::kMaxSize32, "memory", "max memory must be <= 4GB, or unlimited"); } - info.shouldBeTrue(!curr.shared || curr.hasMax(), + info.shouldBeTrue(!curr->shared || curr->hasMax(), "memory", "shared memory must have max size"); - if (curr.shared) { + if (curr->shared) { info.shouldBeTrue(module.features.hasAtomics(), "memory", "memory is shared, but atomics are disabled"); @@ -3016,7 +3028,7 @@ static void validateMemory(Module& module, ValidationInfo& info) { segment->offset, "passive segment should not have an offset"); } else { - if (curr.is64()) { + if (curr->is64()) { if (!info.shouldBeEqual(segment->offset->type, Type(Type::i64), segment->offset, @@ -3033,14 +3045,14 @@ static void validateMemory(Module& module, ValidationInfo& info) { } info.shouldBeTrue(checkSegmentOffset(segment->offset, segment->data.size(), - curr.initial * Memory::kPageSize, + curr->initial * Memory::kPageSize, module.features), segment->offset, "memory segment offset should be reasonable"); if (segment->offset->is<Const>()) { auto start = segment->offset->cast<Const>()->value.getUnsigned(); auto end = start + size; - info.shouldBeTrue(end <= curr.initial * Memory::kPageSize, + info.shouldBeTrue(end <= curr->initial * Memory::kPageSize, segment->data.size(), "segment size should fit in memory (end)"); } @@ -3049,8 +3061,8 @@ static void validateMemory(Module& module, ValidationInfo& info) { // If the memory is imported we don't actually know its initial size. // Specifically wasm dll's import a zero sized memory which is perfectly // valid. - if (!curr.imported()) { - info.shouldBeTrue(size <= curr.initial * Memory::kPageSize, + if (!curr->imported()) { + info.shouldBeTrue(size <= curr->initial * Memory::kPageSize, segment->data.size(), "segment size should fit in memory (initial)"); } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 1c9c1389d..574eb5c47 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1336,6 +1336,10 @@ ElementSegment* Module::getElementSegment(Name name) { return getModuleElement(elementSegmentsMap, name, "getElementSegment"); } +Memory* Module::getMemory(Name name) { + return getModuleElement(memoriesMap, name, "getMemory"); +} + DataSegment* Module::getDataSegment(Name name) { return getModuleElement(dataSegmentsMap, name, "getDataSegment"); } @@ -1373,6 +1377,10 @@ ElementSegment* Module::getElementSegmentOrNull(Name name) { return getModuleElementOrNull(elementSegmentsMap, name); } +Memory* Module::getMemoryOrNull(Name name) { + return getModuleElementOrNull(memoriesMap, name); +} + DataSegment* Module::getDataSegmentOrNull(Name name) { return getModuleElementOrNull(dataSegmentsMap, name); } @@ -1452,6 +1460,10 @@ Module::addElementSegment(std::unique_ptr<ElementSegment>&& curr) { elementSegments, elementSegmentsMap, std::move(curr), "addElementSegment"); } +Memory* Module::addMemory(std::unique_ptr<Memory>&& curr) { + return addModuleElement(memories, memoriesMap, std::move(curr), "addMemory"); +} + DataSegment* Module::addDataSegment(std::unique_ptr<DataSegment>&& curr) { return addModuleElement( dataSegments, dataSegmentsMap, std::move(curr), "addDataSegment"); @@ -1490,6 +1502,9 @@ void Module::removeTable(Name name) { void Module::removeElementSegment(Name name) { removeModuleElement(elementSegments, elementSegmentsMap, name); } +void Module::removeMemory(Name name) { + removeModuleElement(memories, memoriesMap, name); +} void Module::removeDataSegment(Name name) { removeModuleElement(dataSegments, dataSegmentsMap, name); } @@ -1526,6 +1541,9 @@ void Module::removeTables(std::function<bool(Table*)> pred) { void Module::removeElementSegments(std::function<bool(ElementSegment*)> pred) { removeModuleElements(elementSegments, elementSegmentsMap, pred); } +void Module::removeMemories(std::function<bool(Memory*)> pred) { + removeModuleElements(memories, memoriesMap, pred); +} void Module::removeDataSegments(std::function<bool(DataSegment*)> pred) { removeModuleElements(dataSegments, dataSegmentsMap, pred); } @@ -1536,6 +1554,13 @@ void Module::removeTags(std::function<bool(Tag*)> pred) { removeModuleElements(tags, tagsMap, pred); } +void Module::updateDataSegmentsMap() { + dataSegmentsMap.clear(); + for (auto& curr : dataSegments) { + dataSegmentsMap[curr->name] = curr.get(); + } +} + void Module::updateMaps() { functionsMap.clear(); for (auto& curr : functions) { @@ -1553,10 +1578,11 @@ void Module::updateMaps() { for (auto& curr : elementSegments) { elementSegmentsMap[curr->name] = curr.get(); } - dataSegmentsMap.clear(); - for (auto& curr : dataSegments) { - dataSegmentsMap[curr->name] = curr.get(); + memoriesMap.clear(); + for (auto& curr : memories) { + memoriesMap[curr->name] = curr.get(); } + updateDataSegmentsMap(); globalsMap.clear(); for (auto& curr : globals) { globalsMap[curr->name] = curr.get(); |