summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Lively <7121787+tlively@users.noreply.github.com>2019-04-05 19:48:54 -0700
committerGitHub <noreply@github.com>2019-04-05 19:48:54 -0700
commit37443aef8c22f100dc59e81aff6af5a252f3217d (patch)
treeae68bcc8b6bbc4f2b7fdd1f50349b9980e59f10f /src
parent5b24f039ba1f55520a61f6dd6bb8ca46556c592b (diff)
downloadbinaryen-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.cpp13
-rw-r--r--src/binaryen-c.h2
-rw-r--r--src/ir/memory-utils.h4
-rw-r--r--src/js/binaryen.js-post.js9
-rw-r--r--src/passes/MemoryPacking.cpp4
-rw-r--r--src/passes/Print.cpp6
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp4
-rw-r--r--src/shared-constants.h2
-rw-r--r--src/shell-interface.h3
-rw-r--r--src/tools/fuzzing.h86
-rw-r--r--src/tools/wasm-ctor-eval.cpp10
-rw-r--r--src/tools/wasm-metadce.cpp4
-rw-r--r--src/wasm-binary.h5
-rw-r--r--src/wasm-s-parser.h2
-rw-r--r--src/wasm-traversal.h4
-rw-r--r--src/wasm.h9
-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
-rw-r--r--src/wasm2js.h1
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)