summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/wasm-binary.cpp67
-rw-r--r--src/wasm/wasm-emscripten.cpp1
-rw-r--r--src/wasm/wasm-s-parser.cpp28
-rw-r--r--src/wasm/wasm-validator.cpp19
-rw-r--r--src/wasm/wasm.cpp2
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"),