diff options
Diffstat (limited to 'src/wasm')
-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 |
5 files changed, 80 insertions, 37 deletions
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"), |