diff options
author | Thomas Lively <7121787+tlively@users.noreply.github.com> | 2019-04-05 19:48:54 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-05 19:48:54 -0700 |
commit | 37443aef8c22f100dc59e81aff6af5a252f3217d (patch) | |
tree | ae68bcc8b6bbc4f2b7fdd1f50349b9980e59f10f /src | |
parent | 5b24f039ba1f55520a61f6dd6bb8ca46556c592b (diff) | |
download | binaryen-37443aef8c22f100dc59e81aff6af5a252f3217d.tar.gz binaryen-37443aef8c22f100dc59e81aff6af5a252f3217d.tar.bz2 binaryen-37443aef8c22f100dc59e81aff6af5a252f3217d.zip |
Passive segments (#1976)
Adds support for the bulk memory proposal's passive segments. Uses a
new (data passive ...) s-expression syntax to mark sections as
passive.
Diffstat (limited to 'src')
-rw-r--r-- | src/binaryen-c.cpp | 13 | ||||
-rw-r--r-- | src/binaryen-c.h | 2 | ||||
-rw-r--r-- | src/ir/memory-utils.h | 4 | ||||
-rw-r--r-- | src/js/binaryen.js-post.js | 9 | ||||
-rw-r--r-- | src/passes/MemoryPacking.cpp | 4 | ||||
-rw-r--r-- | src/passes/Print.cpp | 6 | ||||
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 4 | ||||
-rw-r--r-- | src/shared-constants.h | 2 | ||||
-rw-r--r-- | src/shell-interface.h | 3 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 86 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 10 | ||||
-rw-r--r-- | src/tools/wasm-metadce.cpp | 4 | ||||
-rw-r--r-- | src/wasm-binary.h | 5 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 2 | ||||
-rw-r--r-- | src/wasm-traversal.h | 4 | ||||
-rw-r--r-- | src/wasm.h | 9 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 67 | ||||
-rw-r--r-- | src/wasm/wasm-emscripten.cpp | 1 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 28 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 19 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 2 | ||||
-rw-r--r-- | src/wasm2js.h | 1 |
22 files changed, 196 insertions, 89 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 72544d5d7..927cae574 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -2360,7 +2360,7 @@ void BinaryenSetFunctionTable(BinaryenModuleRef module, BinaryenIndex initial, B // Memory. One per module -void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char* exportName, const char** segments, BinaryenExpressionRef* segmentOffsets, BinaryenIndex* segmentSizes, BinaryenIndex numSegments, uint8_t shared) { +void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char* exportName, const char** segments, int8_t* segmentPassive, BinaryenExpressionRef* segmentOffsets, BinaryenIndex* segmentSizes, BinaryenIndex numSegments, uint8_t shared) { if (tracing) { std::cout << " {\n"; for (BinaryenIndex i = 0; i < numSegments; i++) { @@ -2378,6 +2378,13 @@ void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, Binaryen } if (numSegments == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS std::cout << " };\n"; + std::cout << " int8_t segmentPassive[] = { "; + for (BinaryenIndex i = 0; i < numSegments; i++) { + if (i > 0) std::cout << ", "; + std::cout << int(segmentPassive[i]); + } + if (numSegments == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + std::cout << " };\n"; std::cout << " BinaryenExpressionRef segmentOffsets[] = { "; for (BinaryenIndex i = 0; i < numSegments; i++) { if (i > 0) std::cout << ", "; @@ -2394,7 +2401,7 @@ void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, Binaryen std::cout << " };\n"; std::cout << " BinaryenSetMemory(the_module, " << initial << ", " << maximum << ", "; traceNameOrNULL(exportName); - std::cout << ", segments, segmentOffsets, segmentSizes, " << numSegments << ", " << int(shared) << ");\n"; + std::cout << ", segments, segmentPassive, segmentOffsets, segmentSizes, " << numSegments << ", " << int(shared) << ");\n"; std::cout << " }\n"; } @@ -2411,7 +2418,7 @@ void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, Binaryen wasm->addExport(memoryExport.release()); } for (BinaryenIndex i = 0; i < numSegments; i++) { - wasm->memory.segments.emplace_back((Expression*)segmentOffsets[i], segments[i], segmentSizes[i]); + wasm->memory.segments.emplace_back(segmentPassive[i], (Expression*)segmentOffsets[i], segments[i], segmentSizes[i]); } } diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 5bc95e0f8..5c5213383 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -744,7 +744,7 @@ void BinaryenSetFunctionTable(BinaryenModuleRef module, BinaryenIndex initial, B // Each segment has data in segments, a start offset in segmentOffsets, and a size in segmentSizes. // exportName can be NULL -void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char* exportName, const char** segments, BinaryenExpressionRef* segmentOffsets, BinaryenIndex* segmentSizes, BinaryenIndex numSegments, uint8_t shared); +void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char* exportName, const char** segments, int8_t* segmentPassive, BinaryenExpressionRef* segmentOffsets, BinaryenIndex* segmentSizes, BinaryenIndex numSegments, uint8_t shared); // Start function. One per module diff --git a/src/ir/memory-utils.h b/src/ir/memory-utils.h index c8f39a2ec..ea19ca799 100644 --- a/src/ir/memory-utils.h +++ b/src/ir/memory-utils.h @@ -31,6 +31,9 @@ namespace MemoryUtils { if (memory.segments.size() == 0) return true; std::vector<char> data; for (auto& segment : memory.segments) { + if (segment.isPassive) { + return false; + } auto* offset = segment.offset->dynCast<Const>(); if (!offset) return false; } @@ -61,4 +64,3 @@ namespace MemoryUtils { } // namespace wasm #endif // wasm_ir_memory_h - diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 1796080fc..b9f6b426c 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -1831,8 +1831,8 @@ function wrapModule(module, self) { ); }); }; - self['setMemory'] = function(initial, maximum, exportName, segments, shared) { - // segments are assumed to be { offset: expression ref, data: array of 8-bit data } + self['setMemory'] = function(initial, maximum, exportName, segments, flags, shared) { + // segments are assumed to be { passive: bool, offset: expression ref, data: array of 8-bit data } if (!segments) segments = []; return preserveStack(function() { return Module['_BinaryenSetMemory']( @@ -1842,6 +1842,11 @@ function wrapModule(module, self) { return allocate(segment.data, 'i8', ALLOC_STACK); }) ), + i8sToStack( + segments.map(function(segment) { + return segment.passive; + }) + ), i32sToStack( segments.map(function(segment) { return segment.offset; diff --git a/src/passes/MemoryPacking.cpp b/src/passes/MemoryPacking.cpp index c7b20c582..2c0dac395 100644 --- a/src/passes/MemoryPacking.cpp +++ b/src/passes/MemoryPacking.cpp @@ -35,7 +35,8 @@ struct MemoryPacking : public Pass { segment.data.pop_back(); } // we can only handle a constant offset for splitting - if (auto* offset = segment.offset->dynCast<Const>()) { + Const* offset; + if (!segment.isPassive && (offset = segment.offset->dynCast<Const>())) { // Find runs of zeros, and split auto& data = segment.data; auto base = offset->value.geti32(); @@ -79,4 +80,3 @@ Pass *createMemoryPackingPass() { } } // namespace wasm - diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 6d7d512ff..ebfb6c311 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1289,7 +1289,11 @@ struct PrintSExpression : public Visitor<PrintSExpression> { doIndent(o, indent); o << '('; printMajor(o, "data "); - visit(segment.offset); + if (segment.isPassive) { + printMedium(o, "passive"); + } else { + visit(segment.offset); + } o << " \""; for (size_t i = 0; i < segment.data.size(); i++) { unsigned char c = segment.data[i]; diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index b9c8d5150..777d2dacc 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -51,7 +51,9 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { queue = roots; // Globals used in memory/table init expressions are also roots for (auto& segment : module->memory.segments) { - walk(segment.offset); + if (!segment.isPassive) { + walk(segment.offset); + } } for (auto& segment : module->table.segments) { walk(segment.offset); diff --git a/src/shared-constants.h b/src/shared-constants.h index 52124d891..dbbc25e02 100644 --- a/src/shared-constants.h +++ b/src/shared-constants.h @@ -35,7 +35,7 @@ extern Name GROW_WASM_MEMORY, RESULT, MEMORY, DATA, - SEGMENT, + PASSIVE, EXPORT, IMPORT, TABLE, diff --git a/src/shell-interface.h b/src/shell-interface.h index ad553441f..6c6961541 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -97,6 +97,9 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { memory.resize(wasm.memory.initial * wasm::Memory::kPageSize); // apply memory segments for (auto& segment : wasm.memory.segments) { + if (segment.isPassive) { + continue; + } Address offset = (uint32_t)ConstantExpressionRunner<TrivialGlobalManager>(instance.globals).visit(segment.offset).value.geti32(); if (offset + segment.data.size() > wasm.memory.initial * wasm::Memory::kPageSize) { trap("invalid offset when initializing memory"); diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index b17be8afe..b8434353a 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -256,12 +256,32 @@ private: void setupMemory() { MemoryUtils::ensureExists(wasm.memory); - // init some data - wasm.memory.segments.emplace_back(builder.makeConst(Literal(int32_t(0)))); - auto num = upTo(USABLE_MEMORY * 2); - for (size_t i = 0; i < num; i++) { - auto value = upTo(512); - wasm.memory.segments[0].data.push_back(value >= 256 ? 0 : (value & 0xff)); + if (features.hasBulkMemory()) { + size_t memCovered = 0; + // need at least one segment for memory.inits + size_t numSegments = upTo(8) + 1; + for (size_t i = 0; i < numSegments; i++) { + Memory::Segment segment; + segment.isPassive = bool(upTo(2)); + size_t segSize = upTo(USABLE_MEMORY * 2); + segment.data.resize(segSize); + for (size_t j = 0; j < segSize; j++) { + segment.data[j] = upTo(512); + } + if (!segment.isPassive) { + segment.offset = builder.makeConst(Literal(int32_t(memCovered))); + memCovered += segSize; + } + wasm.memory.segments.push_back(segment); + } + } else { + // init some data + wasm.memory.segments.emplace_back(builder.makeConst(Literal(int32_t(0)))); + auto num = upTo(USABLE_MEMORY * 2); + for (size_t i = 0; i < num; i++) { + auto value = upTo(512); + wasm.memory.segments[0].data.push_back(value >= 256 ? 0 : (value & 0xff)); + } } } @@ -742,9 +762,6 @@ private: &Self::makeSelect, &Self::makeGetGlobal) .add(FeatureSet::SIMD, &Self::makeSIMD); - if (type == none) { - options.add(FeatureSet::BulkMemory, &Self::makeBulkMemory); - } if (type == i32 || type == i64) { options.add(FeatureSet::Atomics, &Self::makeAtomic); } @@ -760,20 +777,22 @@ private: if (choice < 70) return makeIf(none); if (choice < 80) return makeLoop(none); if (choice < 90) return makeBreak(none); - switch (upTo(11)) { - case 0: return makeBlock(none); - case 1: return makeIf(none); - case 2: return makeLoop(none); - case 3: return makeBreak(none); - case 4: return makeCall(none); - case 5: return makeCallIndirect(none); - case 6: return makeSetLocal(none); - case 7: return makeStore(none); - case 8: return makeDrop(none); - case 9: return makeNop(none); - case 10: return makeSetGlobal(none); - } - WASM_UNREACHABLE(); + using Self = TranslateToFuzzReader; + auto options = FeatureOptions<Expression* (Self::*)(Type)>() + .add(FeatureSet::MVP, + &Self::makeBlock, + &Self::makeIf, + &Self::makeLoop, + &Self::makeBreak, + &Self::makeCall, + &Self::makeCallIndirect, + &Self::makeSetLocal, + &Self::makeStore, + &Self::makeDrop, + &Self::makeNop, + &Self::makeSetGlobal) + .add(FeatureSet::BulkMemory, &Self::makeBulkMemory); + return (this->*pick(options))(none); } Expression* _makeunreachable() { @@ -1757,30 +1776,33 @@ private: Expression* makeMemoryInit() { if (!allowMemory) return makeTrivial(none); - auto segment = uint32_t(get32()); - Expression* dest = make(i32); - Expression* offset = make(i32); - Expression* size = make(i32); + uint32_t segment = upTo(wasm.memory.segments.size()); + size_t totalSize = wasm.memory.segments[segment].data.size(); + size_t offsetVal = upTo(totalSize); + size_t sizeVal = upTo(totalSize - offsetVal); + Expression* dest = makePointer(); + Expression* offset = builder.makeConst(Literal(int32_t(offsetVal))); + Expression* size = builder.makeConst(Literal(int32_t(sizeVal))); return builder.makeMemoryInit(segment, dest, offset, size); } Expression* makeDataDrop() { if (!allowMemory) return makeTrivial(none); - return builder.makeDataDrop(get32()); + return builder.makeDataDrop(upTo(wasm.memory.segments.size())); } Expression* makeMemoryCopy() { if (!allowMemory) return makeTrivial(none); - Expression* dest = make(i32); - Expression* source = make(i32); + Expression* dest = makePointer(); + Expression* source = makePointer(); Expression* size = make(i32); return builder.makeMemoryCopy(dest, source, size); } Expression* makeMemoryFill() { if (!allowMemory) return makeTrivial(none); - Expression* dest = make(i32); - Expression* value = make(i32); + Expression* dest = makePointer(); + Expression* value = makePointer(); Expression* size = make(i32); return builder.makeMemoryFill(dest, value, size); } diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index c0730877a..8b8eaa4ad 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -162,9 +162,9 @@ public: memorySize = total / Memory::kPageSize; } - // flatten memory into a single segment - void flattenMemory() { - MemoryUtils::flatten(wasm.memory); + // flatten memory into a single segment, return true if successful + bool flattenMemory() { + return MemoryUtils::flatten(wasm.memory); } }; @@ -321,7 +321,9 @@ void evalCtors(Module& wasm, std::vector<std::string> ctors) { // create an instance for evalling EvallingModuleInstance instance(wasm, &interface); // flatten memory, so we do not depend on the layout of data segments - instance.flattenMemory(); + if (!instance.flattenMemory()) { + Fatal() << " ...stopping since could not flatten memory\n"; + } // set up the stack area and other environment details instance.setupEnvironment(); // we should not add new globals from here on; as a result, using diff --git a/src/tools/wasm-metadce.cpp b/src/tools/wasm-metadce.cpp index f06c41882..adb623ea0 100644 --- a/src/tools/wasm-metadce.cpp +++ b/src/tools/wasm-metadce.cpp @@ -195,7 +195,9 @@ struct MetaDCEGraph { rooter.walk(segment.offset); } for (auto& segment : wasm.memory.segments) { - rooter.walk(segment.offset); + if (!segment.isPassive) { + rooter.walk(segment.offset); + } } // A parallel scanner for function bodies diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 7fe543cf6..f13b79654 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -325,6 +325,11 @@ enum Section { Data = 11 }; +enum SegmentFlag { + IsPassive = 0x01, + HasMemIndex = 0x02, +}; + enum EncodedType { // value_type i32 = -0x1, // 0x7f diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index c80fa8b83..295e6b6c9 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -224,7 +224,7 @@ private: void stringToBinary(const char* input, size_t size, std::vector<char>& data); void parseMemory(Element& s, bool preParseImport = false); void parseData(Element& s); - void parseInnerData(Element& s, Index i = 1, Expression* offset = nullptr); + void parseInnerData(Element& s, Index i, Expression* offset, bool isPassive); void parseExport(Element& s); void parseImport(Element& s); void parseGlobal(Element& s, bool preParseImport = false); diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 6259bf271..8aa203fa7 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -373,7 +373,9 @@ struct Walker : public VisitorType { void walkMemory(Memory* memory) { for (auto& segment : memory->segments) { - walk(segment.offset); + if (!segment.isPassive) { + walk(segment.offset); + } } static_cast<SubType*>(this)->visitMemory(memory); } diff --git a/src/wasm.h b/src/wasm.h index 12071fdb4..763a4e764 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -858,7 +858,9 @@ public: static const Address::address_t kPageMask = ~(kPageSize - 1); struct Segment { - Expression* offset; + bool isPassive = false; + Index index = 0; + Expression* offset = nullptr; std::vector<char> data; // TODO: optimize Segment() = default; Segment(Expression* offset) : offset(offset) {} @@ -869,6 +871,11 @@ public: Segment(Expression* offset, std::vector<char>& init) : offset(offset) { data.swap(init); } + Segment(bool isPassive, Expression* offset, const char* init, Address size) + : isPassive(isPassive), offset(offset) { + data.resize(size); + std::copy_n(init, size, data.begin()); + } }; bool exists = false; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 385ee0ccb..63adbb08f 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -316,13 +316,30 @@ static bool isEmpty(Memory::Segment& segment) { } static bool isConstantOffset(Memory::Segment& segment) { - return segment.offset->is<Const>(); + return segment.offset && segment.offset->is<Const>(); } void WasmBinaryWriter::writeDataSegments() { if (wasm->memory.segments.size() == 0) return; + + Index emitted = 0; + auto emit = [&](Memory::Segment& segment) { + uint32_t flags = 0; + if (segment.isPassive) { + flags |= BinaryConsts::IsPassive; + } + o << U32LEB(flags); + if (!segment.isPassive) { + writeExpression(segment.offset); + o << int8_t(BinaryConsts::End); + } + writeInlineBuffer(&segment.data[0], segment.data.size()); + emitted++; + }; + Index numConstant = 0, numDynamic = 0; + bool hasPassiveSegments = false; for (auto& segment : wasm->memory.segments) { if (!isEmpty(segment)) { if (isConstantOffset(segment)) { @@ -331,6 +348,21 @@ void WasmBinaryWriter::writeDataSegments() { numDynamic++; } } + hasPassiveSegments |= segment.isPassive; + } + + if (hasPassiveSegments) { + if (wasm->memory.segments.size() > WebLimitations::MaxDataSegments) { + std::cerr << "too many data segments, wasm VMs may not accept this binary" << std::endl; + } + // TODO: merge segments in a pass that can fix up memory.init instructions + auto section = startSection(BinaryConsts::Section::Data); + o << U32LEB(wasm->memory.segments.size()); + for (auto& segment : wasm->memory.segments) { + emit(segment); + } + finishSection(section); + return; } // check if we have too many dynamic data segments, which we can do nothing about auto num = numConstant + numDynamic; @@ -347,14 +379,6 @@ void WasmBinaryWriter::writeDataSegments() { o << U32LEB(num); // first, emit all non-constant-offset segments; then emit the constants, // which we may merge if forced to - Index emitted = 0; - auto emit = [&](Memory::Segment& segment) { - o << U32LEB(0); // Linear memory 0 in the MVP - writeExpression(segment.offset); - o << int8_t(BinaryConsts::End); - writeInlineBuffer(&segment.data[0], segment.data.size()); - emitted++; - }; auto& segments = wasm->memory.segments; for (auto& segment : segments) { if (isEmpty(segment)) continue; @@ -1536,20 +1560,25 @@ void WasmBinaryBuilder::readDataSegments() { if (debug) std::cerr << "== readDataSegments" << std::endl; auto num = getU32LEB(); for (size_t i = 0; i < num; i++) { - auto memoryIndex = getU32LEB(); - WASM_UNUSED(memoryIndex); - if (memoryIndex != 0) { - throwError("bad memory index, must be 0"); - } Memory::Segment curr; - auto offset = readExpression(); + uint32_t flags = getU32LEB(); + if (flags > 2) { + throwError("bad segment flags, must be 0, 1, or 2, not " + + std::to_string(flags)); + } + curr.isPassive = flags & BinaryConsts::IsPassive; + if (flags & BinaryConsts::HasMemIndex) { + curr.index = getU32LEB(); + } + if (!curr.isPassive) { + curr.offset = readExpression(); + } auto size = getU32LEB(); - std::vector<char> buffer; - buffer.resize(size); + curr.data.resize(size); for (size_t j = 0; j < size; j++) { - buffer[j] = char(getInt8()); + curr.data[j] = char(getInt8()); } - wasm.memory.segments.emplace_back(offset, (const char*)&buffer[0], size); + wasm.memory.segments.push_back(curr); } } diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp index 7ac7f4802..875212c44 100644 --- a/src/wasm/wasm-emscripten.cpp +++ b/src/wasm/wasm-emscripten.cpp @@ -944,6 +944,7 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata( void EmscriptenGlueGenerator::separateDataSegments(Output* outfile, Address base) { size_t lastEnd = 0; for (Memory::Segment& seg : wasm.memory.segments) { + assert(!seg.isPassive && "separating passive segments not implemented"); size_t offset = seg.offset->cast<Const>()->value.geti32(); offset -= base; size_t fill = offset - lastEnd; diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index ddda991cd..3ba8e0622 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1454,7 +1454,8 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { } else { if (!(inner.size() > 0 ? inner[0]->str() != IMPORT : true)) throw ParseException("bad import ending"); // (memory (data ..)) format - parseInnerData(*s[i]); + auto offset = allocator.alloc<Const>()->set(Literal(int32_t(0))); + parseInnerData(*s[i], 1, offset, false); wasm.memory.initial = wasm.memory.segments[0].data.size(); return; } @@ -1488,16 +1489,26 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { void SExpressionWasmBuilder::parseData(Element& s) { if (!wasm.memory.exists) throw ParseException("data but no memory"); + bool isPassive = false; + Expression* offset = nullptr; Index i = 1; - if (!s[i]->isList()) { - // the memory is named + if (s[i]->isStr()) { + // data is passive or named + if (s[i]->str() == PASSIVE) { + isPassive = true; + } i++; } - auto* offset = parseExpression(s[i++]); - parseInnerData(s, i, offset); + if (!isPassive) { + offset = parseExpression(s[i]); + } + if (s.size() != 3 && s.size() != 4) { + throw ParseException("Unexpected data items"); + } + parseInnerData(s, s.size() - 1, offset, isPassive); } -void SExpressionWasmBuilder::parseInnerData(Element& s, Index i, Expression* offset) { +void SExpressionWasmBuilder::parseInnerData(Element& s, Index i, Expression* offset, bool isPassive) { std::vector<char> data; while (i < s.size()) { const char *input = s[i++]->c_str(); @@ -1505,10 +1516,7 @@ void SExpressionWasmBuilder::parseInnerData(Element& s, Index i, Expression* off stringToBinary(input, size, data); } } - if (!offset) { - offset = allocator.alloc<Const>()->set(Literal(int32_t(0))); - } - wasm.memory.segments.emplace_back(offset, data.data(), data.size()); + wasm.memory.segments.emplace_back(isPassive, offset, data.data(), data.size()); } void SExpressionWasmBuilder::parseExport(Element& s) { diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 79c1d4f9b..a32dd5b0c 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -1304,20 +1304,25 @@ static void validateMemory(Module& module, ValidationInfo& info) { info.shouldBeTrue(!curr.shared || curr.hasMax(), "memory", "shared memory must have max size"); if (curr.shared) info.shouldBeTrue(info.features.hasAtomics(), "memory", "memory is shared, but atomics are disabled"); for (auto& segment : curr.segments) { - if (!info.shouldBeEqual(segment.offset->type, i32, segment.offset, "segment offset should be i32")) continue; - info.shouldBeTrue(checkOffset(segment.offset, segment.data.size(), curr.initial * Memory::kPageSize), segment.offset, "segment offset should be reasonable"); Index size = segment.data.size(); + if (segment.isPassive) { + info.shouldBeTrue(info.features.hasBulkMemory(), segment.offset, "nonzero segment flags (bulk memory is disabled)"); + info.shouldBeEqual(segment.offset, (Expression*)nullptr, segment.offset, "passive segment should not have an offset"); + } else { + if (!info.shouldBeEqual(segment.offset->type, i32, segment.offset, "segment offset should be i32")) continue; + info.shouldBeTrue(checkOffset(segment.offset, segment.data.size(), curr.initial * Memory::kPageSize), segment.offset, "segment offset should be reasonable"); + if (segment.offset->is<Const>()) { + Index start = segment.offset->cast<Const>()->value.geti32(); + Index end = start + size; + info.shouldBeTrue(end <= curr.initial * Memory::kPageSize, segment.data.size(), "segment size should fit in memory (end)"); + } + } // 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, segment.data.size(), "segment size should fit in memory (initial)"); } - if (segment.offset->is<Const>()) { - Index start = segment.offset->cast<Const>()->value.geti32(); - Index end = start + size; - info.shouldBeTrue(end <= curr.initial * Memory::kPageSize, segment.data.size(), "segment size should fit in memory (end)"); - } } } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index e14f6455f..ea1ff2a7c 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -56,7 +56,7 @@ Name GROW_WASM_MEMORY("__growWasmMemory"), RESULT("result"), MEMORY("memory"), DATA("data"), - SEGMENT("segment"), + PASSIVE("passive"), EXPORT("export"), IMPORT("import"), TABLE("table"), diff --git a/src/wasm2js.h b/src/wasm2js.h index 080315490..518a8037b 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -525,6 +525,7 @@ void Wasm2JSBuilder::addEsmExportsAndInstantiate(Ref ast, Module *wasm, Name fun flattenAppend(ast, ValueBuilder::makeName(name)); } for (auto& seg : wasm->memory.segments) { + assert(!seg.isPassive && "passive segments not implemented yet"); std::ostringstream out; out << "assign" << funcName.str << "(" << constOffset(seg) |