diff options
author | Ashley Nelson <nashley@google.com> | 2022-06-21 20:57:43 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-21 20:57:43 -0700 |
commit | 3b9c2e85fa5d97ba08a95c0c7cce7d041e699cde (patch) | |
tree | c01eb86869401931006b6503e47d60b9a44511b0 /src | |
parent | 7fa4c0841c31930759fbad2efb8ada3ef0e6f3ef (diff) | |
download | binaryen-3b9c2e85fa5d97ba08a95c0c7cce7d041e699cde.tar.gz binaryen-3b9c2e85fa5d97ba08a95c0c7cce7d041e699cde.tar.bz2 binaryen-3b9c2e85fa5d97ba08a95c0c7cce7d041e699cde.zip |
First class Data Segments (#4733)
* Updating wasm.h/cpp for DataSegments
* Updating wasm-binary.h/cpp for DataSegments
* Removed link from Memory to DataSegments and updated module-utils, Metrics and wasm-traversal
* checking isPassive when copying data segments to know whether to construct the data segment with an offset or not
* Removing memory member var from DataSegment class as there is only one memory rn. Updated wasm-validator.cpp
* Updated wasm-interpreter
* First look at updating Passes
* Updated wasm-s-parser
* Updated files in src/ir
* Updating tools files
* Last pass on src files before building
* added visitDataSegment
* Fixing build errors
* Data segments need a name
* fixing var name
* ran clang-format
* Ensuring a name on DataSegment
* Ensuring more datasegments have names
* Adding explicit name support
* Fix fuzzing name
* Outputting data name in wasm binary only if explicit
* Checking temp dataSegments vector to validateBinary because it's the one with the segments before we processNames
* Pass on when data segment names are explicitly set
* Ran auto_update_tests.py and check.py, success all around
* Removed an errant semi-colon and corrected a counter. Everything still passes
* Linting
* Fixing processing memory names after parsed from binary
* Updating the test from the last fix
* Correcting error comment
* Impl kripken@ comments
* Impl tlively@ comments
* Updated tests that remove data print when == 0
* Ran clang format
* Impl tlively@ comments
* Ran clang-format
Diffstat (limited to 'src')
-rw-r--r-- | src/binaryen-c.cpp | 34 | ||||
-rw-r--r-- | src/ir/ReFinalize.cpp | 3 | ||||
-rw-r--r-- | src/ir/memory-utils.cpp | 26 | ||||
-rw-r--r-- | src/ir/memory-utils.h | 75 | ||||
-rw-r--r-- | src/ir/module-utils.h | 30 | ||||
-rw-r--r-- | src/ir/utils.h | 2 | ||||
-rw-r--r-- | src/passes/Memory64Lowering.cpp | 15 | ||||
-rw-r--r-- | src/passes/MemoryPacking.cpp | 120 | ||||
-rw-r--r-- | src/passes/Metrics.cpp | 22 | ||||
-rw-r--r-- | src/passes/Print.cpp | 97 | ||||
-rw-r--r-- | src/passes/RemoveMemory.cpp | 2 | ||||
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 10 | ||||
-rw-r--r-- | src/tools/fuzzing/fuzzing.cpp | 40 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 15 | ||||
-rw-r--r-- | src/tools/wasm-metadce.cpp | 7 | ||||
-rw-r--r-- | src/tools/wasm-reduce.cpp | 14 | ||||
-rw-r--r-- | src/wasm-binary.h | 11 | ||||
-rw-r--r-- | src/wasm-builder.h | 15 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 19 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 9 | ||||
-rw-r--r-- | src/wasm-traversal.h | 17 | ||||
-rw-r--r-- | src/wasm.h | 43 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 67 | ||||
-rw-r--r-- | src/wasm/wasm-emscripten.cpp | 44 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 37 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 40 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 23 | ||||
-rw-r--r-- | src/wasm2js.h | 30 |
28 files changed, 486 insertions, 381 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index d70f31912..8a5756f6e 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -3768,23 +3768,25 @@ void BinaryenSetMemory(BinaryenModuleRef module, wasm->addExport(memoryExport.release()); } for (BinaryenIndex i = 0; i < numSegments; i++) { - wasm->memory.segments.emplace_back(Name(), - segmentPassive[i], - (Expression*)segmentOffsets[i], - segments[i], - segmentSizes[i]); + auto curr = Builder::makeDataSegment(Name::fromInt(i), + segmentPassive[i], + (Expression*)segmentOffsets[i], + segments[i], + segmentSizes[i]); + curr->hasExplicitName = false; + wasm->dataSegments.push_back(std::move(curr)); } } // Memory segments uint32_t BinaryenGetNumMemorySegments(BinaryenModuleRef module) { - return ((Module*)module)->memory.segments.size(); + return ((Module*)module)->dataSegments.size(); } uint32_t BinaryenGetMemorySegmentByteOffset(BinaryenModuleRef module, BinaryenIndex id) { auto* wasm = (Module*)module; - if (wasm->memory.segments.size() <= id) { + if (wasm->dataSegments.size() <= id) { Fatal() << "invalid segment id."; } @@ -3797,13 +3799,13 @@ uint32_t BinaryenGetMemorySegmentByteOffset(BinaryenModuleRef module, return false; }; - const auto& segment = wasm->memory.segments[id]; + const auto& segment = wasm->dataSegments[id]; int64_t ret; - if (globalOffset(segment.offset, ret)) { + if (globalOffset(segment->offset, ret)) { return ret; } - if (auto* get = segment.offset->dynCast<GlobalGet>()) { + if (auto* get = segment->offset->dynCast<GlobalGet>()) { Global* global = wasm->getGlobal(get->name); if (globalOffset(global->init, ret)) { return ret; @@ -3846,29 +3848,29 @@ bool BinaryenMemoryIsShared(BinaryenModuleRef module) { } size_t BinaryenGetMemorySegmentByteLength(BinaryenModuleRef module, BinaryenIndex id) { - const auto& segments = ((Module*)module)->memory.segments; + const auto& segments = ((Module*)module)->dataSegments; if (segments.size() <= id) { Fatal() << "invalid segment id."; } - return segments[id].data.size(); + return segments[id]->data.size(); } bool BinaryenGetMemorySegmentPassive(BinaryenModuleRef module, BinaryenIndex id) { - const auto& segments = ((Module*)module)->memory.segments; + const auto& segments = ((Module*)module)->dataSegments; if (segments.size() <= id) { Fatal() << "invalid segment id."; } - return segments[id].isPassive; + return segments[id]->isPassive; } void BinaryenCopyMemorySegmentData(BinaryenModuleRef module, BinaryenIndex id, char* buffer) { - const auto& segments = ((Module*)module)->memory.segments; + const auto& segments = ((Module*)module)->dataSegments; if (segments.size() <= id) { Fatal() << "invalid segment id."; } const auto& segment = segments[id]; - std::copy(segment.data.cbegin(), segment.data.cend(), buffer); + std::copy(segment->data.cbegin(), segment->data.cend(), buffer); } // Start function. One per module diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 79a4c9386..ef09ddbae 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -189,6 +189,9 @@ void ReFinalize::visitElementSegment(ElementSegment* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitMemory(Memory* curr) { WASM_UNREACHABLE("unimp"); } +void ReFinalize::visitDataSegment(DataSegment* curr) { + WASM_UNREACHABLE("unimp"); +} void ReFinalize::visitTag(Tag* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitModule(Module* curr) { WASM_UNREACHABLE("unimp"); } diff --git a/src/ir/memory-utils.cpp b/src/ir/memory-utils.cpp index b7c92fe9e..8dc3baeb9 100644 --- a/src/ir/memory-utils.cpp +++ b/src/ir/memory-utils.cpp @@ -37,34 +37,36 @@ bool flatten(Module& wasm) { } } - auto& memory = wasm.memory; + auto& dataSegments = wasm.dataSegments; - if (memory.segments.size() == 0) { + if (dataSegments.size() == 0) { return true; } std::vector<char> data; - for (auto& segment : memory.segments) { - if (segment.isPassive) { + for (auto& segment : dataSegments) { + if (segment->isPassive) { return false; } - auto* offset = segment.offset->dynCast<Const>(); + auto* offset = segment->offset->dynCast<Const>(); if (!offset) { return false; } } - for (auto& segment : memory.segments) { - auto* offset = segment.offset->dynCast<Const>(); + for (auto& segment : dataSegments) { + auto* offset = segment->offset->dynCast<Const>(); Index start = offset->value.getInteger(); - Index end = start + segment.data.size(); + Index end = start + segment->data.size(); if (end > data.size()) { data.resize(end); } - std::copy(segment.data.begin(), segment.data.end(), data.begin() + start); + std::copy(segment->data.begin(), segment->data.end(), data.begin() + start); } - memory.segments.resize(1); - memory.segments[0].offset->cast<Const>()->value = Literal(int32_t(0)); - memory.segments[0].data.swap(data); + dataSegments.resize(1); + dataSegments[0]->offset->cast<Const>()->value = Literal(int32_t(0)); + dataSegments[0]->data.swap(data); + wasm.removeDataSegments( + [&](DataSegment* curr) { return curr->name != dataSegments[0]->name; }); return true; } diff --git a/src/ir/memory-utils.h b/src/ir/memory-utils.h index 9b3325329..5e9086ca4 100644 --- a/src/ir/memory-utils.h +++ b/src/ir/memory-utils.h @@ -43,8 +43,8 @@ inline void ensureExists(Memory& memory) { // Try to merge segments until they fit into web limitations. // Return true if successful. inline bool ensureLimitedSegments(Module& module) { - Memory& memory = module.memory; - if (memory.segments.size() <= WebLimitations::MaxDataSegments) { + auto& dataSegments = module.dataSegments; + if (dataSegments.size() <= WebLimitations::MaxDataSegments) { return true; } @@ -54,25 +54,23 @@ inline bool ensureLimitedSegments(Module& module) { return false; } - auto isEmpty = [](Memory::Segment& segment) { - return segment.data.size() == 0; - }; + auto isEmpty = [](DataSegment& segment) { return segment.data.size() == 0; }; - auto isConstantOffset = [](Memory::Segment& segment) { + auto isConstantOffset = [](DataSegment& segment) { return segment.offset && segment.offset->is<Const>(); }; Index numConstant = 0, numDynamic = 0; bool hasPassiveSegments = false; - for (auto& segment : memory.segments) { - if (!isEmpty(segment)) { - if (isConstantOffset(segment)) { + for (auto& segment : dataSegments) { + if (!isEmpty(*segment)) { + if (isConstantOffset(*segment)) { numConstant++; } else { numDynamic++; } } - hasPassiveSegments |= segment.isPassive; + hasPassiveSegments |= segment->isPassive; } if (hasPassiveSegments) { @@ -93,43 +91,43 @@ inline bool ensureLimitedSegments(Module& module) { assert(num == WebLimitations::MaxDataSegments - 1); } - std::vector<Memory::Segment> mergedSegments; + std::vector<std::unique_ptr<wasm::DataSegment>> mergedSegments; mergedSegments.reserve(WebLimitations::MaxDataSegments); // drop empty segments and pass through dynamic-offset segments - for (auto& segment : memory.segments) { - if (isEmpty(segment)) { + for (auto& segment : dataSegments) { + if (isEmpty(*segment)) { continue; } - if (isConstantOffset(segment)) { + if (isConstantOffset(*segment)) { continue; } - mergedSegments.push_back(segment); + mergedSegments.push_back(std::move(segment)); } // from here on, we concern ourselves with non-empty constant-offset // segments, the ones which we may need to merge - auto isRelevant = [&](Memory::Segment& segment) { + auto isRelevant = [&](DataSegment& segment) { return !isEmpty(segment) && isConstantOffset(segment); }; - for (Index i = 0; i < memory.segments.size(); i++) { - auto& segment = memory.segments[i]; - if (!isRelevant(segment)) { + for (Index i = 0; i < dataSegments.size(); i++) { + auto& segment = dataSegments[i]; + if (!isRelevant(*segment)) { continue; } if (mergedSegments.size() + 2 < WebLimitations::MaxDataSegments) { - mergedSegments.push_back(segment); + mergedSegments.push_back(std::move(segment)); continue; } // we can emit only one more segment! merge everything into one // start the combined segment at the bottom of them all - auto start = segment.offset->cast<Const>()->value.getInteger(); - for (Index j = i + 1; j < memory.segments.size(); j++) { - auto& segment = memory.segments[j]; - if (!isRelevant(segment)) { + auto start = segment->offset->cast<Const>()->value.getInteger(); + for (Index j = i + 1; j < dataSegments.size(); j++) { + auto& segment = dataSegments[j]; + if (!isRelevant(*segment)) { continue; } - auto offset = segment.offset->cast<Const>()->value.getInteger(); + auto offset = segment->offset->cast<Const>()->value.getInteger(); start = std::min(start, offset); } // create the segment and add in all the data @@ -137,26 +135,27 @@ inline bool ensureLimitedSegments(Module& module) { c->value = Literal(int32_t(start)); c->type = Type::i32; - Memory::Segment combined(c); - for (Index j = i; j < memory.segments.size(); j++) { - auto& segment = memory.segments[j]; - if (!isRelevant(segment)) { + auto combined = Builder::makeDataSegment(); + combined->offset = c; + for (Index j = i; j < dataSegments.size(); j++) { + auto& segment = dataSegments[j]; + if (!isRelevant(*segment)) { continue; } - auto offset = segment.offset->cast<Const>()->value.getInteger(); - auto needed = offset + segment.data.size() - start; - if (combined.data.size() < needed) { - combined.data.resize(needed); + auto offset = segment->offset->cast<Const>()->value.getInteger(); + auto needed = offset + segment->data.size() - start; + if (combined->data.size() < needed) { + combined->data.resize(needed); } - std::copy(segment.data.begin(), - segment.data.end(), - combined.data.begin() + (offset - start)); + std::copy(segment->data.begin(), + segment->data.end(), + combined->data.begin() + (offset - start)); } - mergedSegments.push_back(combined); + mergedSegments.push_back(std::move(combined)); break; } - memory.segments.swap(mergedSegments); + dataSegments.swap(mergedSegments); return true; } } // namespace wasm::MemoryUtils diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index 5030bf84f..4f731748e 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -106,6 +106,20 @@ inline Table* copyTable(const Table* table, Module& out) { return out.addTable(std::move(ret)); } +inline DataSegment* copyDataSegment(const DataSegment* segment, Module& out) { + auto ret = Builder::makeDataSegment(); + ret->name = segment->name; + ret->hasExplicitName = segment->hasExplicitName; + ret->isPassive = segment->isPassive; + if (!segment->isPassive) { + auto offset = ExpressionManipulator::copy(segment->offset, out); + ret->offset = offset; + } + ret->data = segment->data; + + return out.addDataSegment(std::move(ret)); +} + inline void copyModule(const Module& in, Module& out) { // we use names throughout, not raw pointers, so simple copying is fine // for everything *but* expressions @@ -127,11 +141,10 @@ inline void copyModule(const Module& in, Module& out) { for (auto& curr : in.tables) { copyTable(curr.get(), out); } - - out.memory = in.memory; - for (auto& segment : out.memory.segments) { - segment.offset = ExpressionManipulator::copy(segment.offset, out); + for (auto& curr : in.dataSegments) { + copyDataSegment(curr.get(), out); } + out.memory = in.memory; out.start = in.start; out.userSections = in.userSections; out.debugInfoFileNames = in.debugInfoFileNames; @@ -205,6 +218,15 @@ template<typename T> inline void iterDefinedMemories(Module& wasm, T visitor) { } } +template<typename T> +inline void iterActiveDataSegments(Module& wasm, T visitor) { + for (auto& segment : wasm.dataSegments) { + if (!segment->isPassive) { + visitor(segment.get()); + } + } +} + template<typename T> inline void iterImportedTables(Module& wasm, T visitor) { for (auto& import : wasm.tables) { if (import->imported()) { diff --git a/src/ir/utils.h b/src/ir/utils.h index 7abc87b5e..cad4a78aa 100644 --- a/src/ir/utils.h +++ b/src/ir/utils.h @@ -139,6 +139,7 @@ struct ReFinalize void visitTable(Table* curr); void visitElementSegment(ElementSegment* curr); void visitMemory(Memory* curr); + void visitDataSegment(DataSegment* curr); void visitTag(Tag* curr); void visitModule(Module* curr); @@ -163,6 +164,7 @@ struct ReFinalizeNode : public OverriddenVisitor<ReFinalizeNode> { void visitTable(Table* curr) { WASM_UNREACHABLE("unimp"); } void visitElementSegment(ElementSegment* curr) { WASM_UNREACHABLE("unimp"); } void visitMemory(Memory* curr) { WASM_UNREACHABLE("unimp"); } + void visitDataSegment(DataSegment* curr) { WASM_UNREACHABLE("unimp"); } void visitTag(Tag* curr) { WASM_UNREACHABLE("unimp"); } void visitModule(Module* curr) { WASM_UNREACHABLE("unimp"); } diff --git a/src/passes/Memory64Lowering.cpp b/src/passes/Memory64Lowering.cpp index 95dbf4f17..4c2770e3a 100644 --- a/src/passes/Memory64Lowering.cpp +++ b/src/passes/Memory64Lowering.cpp @@ -99,19 +99,20 @@ struct Memory64Lowering : public WalkerPass<PostWalker<Memory64Lowering>> { void visitAtomicNotify(AtomicNotify* curr) { wrapAddress64(curr->ptr); } void visitMemory(Memory* memory) { - for (auto& segment : memory->segments) { - if (!segment.isPassive) { - auto* c = segment.offset->cast<Const>(); - c->value = Literal(static_cast<uint32_t>(c->value.geti64())); - c->type = Type::i32; - } - } // This is visited last. memory->indexType = Type::i32; if (memory->hasMax() && memory->max > Memory::kMaxSize32) { memory->max = Memory::kMaxSize32; } } + + void visitDataSegment(DataSegment* segment) { + if (!segment->isPassive) { + auto* c = segment->offset->cast<Const>(); + c->value = Literal(static_cast<uint32_t>(c->value.geti64())); + c->type = Type::i32; + } + } }; Pass* createMemory64LoweringPass() { return new Memory64Lowering(); } diff --git a/src/passes/MemoryPacking.cpp b/src/passes/MemoryPacking.cpp index 7b4caf11e..3be08e7b9 100644 --- a/src/passes/MemoryPacking.cpp +++ b/src/passes/MemoryPacking.cpp @@ -95,19 +95,22 @@ makeGtShiftedMemorySize(Builder& builder, Module& module, MemoryInit* curr) { struct MemoryPacking : public Pass { void run(PassRunner* runner, Module* module) override; - bool canOptimize(const Memory& memory, const PassOptions& passOptions); + bool canOptimize(const Memory& memory, + std::vector<std::unique_ptr<DataSegment>>& dataSegments, + const PassOptions& passOptions); void optimizeBulkMemoryOps(PassRunner* runner, Module* module); void getSegmentReferrers(Module* module, ReferrersMap& referrers); - void dropUnusedSegments(std::vector<Memory::Segment>& segments, + void dropUnusedSegments(std::vector<std::unique_ptr<DataSegment>>& segments, ReferrersMap& referrers); - bool canSplit(const Memory::Segment& segment, const Referrers& referrers); - void calculateRanges(const Memory::Segment& segment, + bool canSplit(const std::unique_ptr<DataSegment>& segment, + const Referrers& referrers); + void calculateRanges(const std::unique_ptr<DataSegment>& segment, const Referrers& referrers, std::vector<Range>& ranges); void createSplitSegments(Builder& builder, - const Memory::Segment& segment, + const DataSegment* segment, std::vector<Range>& ranges, - std::vector<Memory::Segment>& packed, + std::vector<std::unique_ptr<DataSegment>>& packed, size_t segmentsRemaining); void createReplacements(Module* module, const std::vector<Range>& ranges, @@ -120,11 +123,11 @@ struct MemoryPacking : public Pass { }; void MemoryPacking::run(PassRunner* runner, Module* module) { - if (!canOptimize(module->memory, runner->options)) { + if (!canOptimize(module->memory, module->dataSegments, runner->options)) { return; } - auto& segments = module->memory.segments; + auto& segments = module->dataSegments; // For each segment, a list of bulk memory instructions that refer to it ReferrersMap referrers; @@ -141,7 +144,7 @@ void MemoryPacking::run(PassRunner* runner, Module* module) { } // The new, split memory segments - std::vector<Memory::Segment> packed; + std::vector<std::unique_ptr<DataSegment>> packed; Replacements replacements; Builder builder(*module); @@ -156,12 +159,13 @@ void MemoryPacking::run(PassRunner* runner, Module* module) { } else { // A single range covers the entire segment. Set isZero to false so the // original memory.init will be used even if segment is all zeroes. - ranges.push_back({false, 0, segment.data.size()}); + ranges.push_back({false, 0, segment->data.size()}); } Index firstNewIndex = packed.size(); size_t segmentsRemaining = segments.size() - origIndex; - createSplitSegments(builder, segment, ranges, packed, segmentsRemaining); + createSplitSegments( + builder, segment.get(), ranges, packed, segmentsRemaining); createReplacements( module, ranges, currReferrers, replacements, firstNewIndex); } @@ -173,8 +177,10 @@ void MemoryPacking::run(PassRunner* runner, Module* module) { } } -bool MemoryPacking::canOptimize(const Memory& memory, - const PassOptions& passOptions) { +bool MemoryPacking::canOptimize( + const Memory& memory, + std::vector<std::unique_ptr<DataSegment>>& dataSegments, + const PassOptions& passOptions) { if (!memory.exists) { return false; } @@ -186,18 +192,16 @@ bool MemoryPacking::canOptimize(const Memory& memory, return false; } - auto& segments = memory.segments; - // One segment is always ok to optimize, as it does not have the potential // problems handled below. - if (segments.size() <= 1) { + if (dataSegments.size() <= 1) { return true; } // Check if it is ok for us to optimize. Address maxAddress = 0; - for (auto& segment : segments) { - if (!segment.isPassive) { - auto* c = segment.offset->dynCast<Const>(); + for (auto& segment : dataSegments) { + if (!segment->isPassive) { + auto* c = segment->offset->dynCast<Const>(); // If an active segment has a non-constant offset, then what gets written // cannot be known until runtime. That is, the active segments are written // out at startup, in order, and one may trample the data of another, like @@ -222,7 +226,7 @@ bool MemoryPacking::canOptimize(const Memory& memory, } // Note the maximum address so far. maxAddress = std::max( - maxAddress, Address(c->value.getUnsigned() + segment.data.size())); + maxAddress, Address(c->value.getUnsigned() + segment->data.size())); } } // All active segments have constant offsets, known at this time, so we may be @@ -230,11 +234,11 @@ bool MemoryPacking::canOptimize(const Memory& memory, // earlier. // TODO: optimize in the trampling case DisjointSpans space; - for (auto& segment : segments) { - if (!segment.isPassive) { - auto* c = segment.offset->cast<Const>(); + for (auto& segment : dataSegments) { + if (!segment->isPassive) { + auto* c = segment->offset->cast<Const>(); Address start = c->value.getUnsigned(); - DisjointSpans::Span span{start, start + segment.data.size()}; + DisjointSpans::Span span{start, start + segment->data.size()}; if (space.addAndCheckOverlap(span)) { std::cerr << "warning: active memory segments have overlap, which " << "prevents some optimizations.\n"; @@ -245,17 +249,17 @@ bool MemoryPacking::canOptimize(const Memory& memory, return true; } -bool MemoryPacking::canSplit(const Memory::Segment& segment, +bool MemoryPacking::canSplit(const std::unique_ptr<DataSegment>& segment, const Referrers& referrers) { // Don't mess with segments related to llvm coverage tools such as // __llvm_covfun. There segments are expected/parsed by external downstream // tools (llvm-cov) so they need to be left intact. // See https://clang.llvm.org/docs/SourceBasedCodeCoverage.html - if (segment.name.is() && segment.name.startsWith("__llvm")) { + if (segment->name.is() && segment->name.startsWith("__llvm")) { return false; } - if (segment.isPassive) { + if (segment->isPassive) { for (auto* referrer : referrers) { if (auto* init = referrer->dynCast<MemoryInit>()) { // Do not try to split if there is a nonconstant offset or size @@ -268,13 +272,13 @@ bool MemoryPacking::canSplit(const Memory::Segment& segment, } // Active segments can only be split if they have constant offsets - return segment.offset->is<Const>(); + return segment->offset->is<Const>(); } -void MemoryPacking::calculateRanges(const Memory::Segment& segment, +void MemoryPacking::calculateRanges(const std::unique_ptr<DataSegment>& segment, const Referrers& referrers, std::vector<Range>& ranges) { - auto& data = segment.data; + auto& data = segment->data; if (data.size() == 0) { return; } @@ -304,7 +308,7 @@ void MemoryPacking::calculateRanges(const Memory::Segment& segment, // entire segment and that all its arguments are constants. These assumptions // are true of all memory.inits generated by the tools. size_t threshold = 0; - if (segment.isPassive) { + if (segment->isPassive) { // Passive segment metadata size threshold += 2; // Zeroes on the edge do not increase the number of segments or data.drops, @@ -375,8 +379,8 @@ void MemoryPacking::optimizeBulkMemoryOps(PassRunner* runner, Module* module) { void visitMemoryInit(MemoryInit* curr) { Builder builder(*getModule()); - Memory::Segment& segment = getModule()->memory.segments[curr->segment]; - size_t maxRuntimeSize = segment.isPassive ? segment.data.size() : 0; + auto& segment = getModule()->dataSegments[curr->segment]; + size_t maxRuntimeSize = segment->isPassive ? segment->data.size() : 0; bool mustNop = false; bool mustTrap = false; auto* offset = curr->offset->dynCast<Const>(); @@ -409,7 +413,7 @@ void MemoryPacking::optimizeBulkMemoryOps(PassRunner* runner, Module* module) { builder.makeDrop(curr->size), builder.makeUnreachable())); needsRefinalizing = true; - } else if (!segment.isPassive) { + } else if (!segment->isPassive) { // trap if (dest > memory.size | offset | size) != 0 replaceCurrent(builder.makeIf( builder.makeBinary( @@ -420,7 +424,7 @@ void MemoryPacking::optimizeBulkMemoryOps(PassRunner* runner, Module* module) { } } void visitDataDrop(DataDrop* curr) { - if (!getModule()->memory.segments[curr->segment].isPassive) { + if (!getModule()->dataSegments[curr->segment]->isPassive) { ExpressionManipulator::nop(curr); } } @@ -467,9 +471,10 @@ void MemoryPacking::getSegmentReferrers(Module* module, } } -void MemoryPacking::dropUnusedSegments(std::vector<Memory::Segment>& segments, - ReferrersMap& referrers) { - std::vector<Memory::Segment> usedSegments; +void MemoryPacking::dropUnusedSegments( + std::vector<std::unique_ptr<DataSegment>>& segments, + ReferrersMap& referrers) { + std::vector<std::unique_ptr<DataSegment>> usedSegments; ReferrersMap usedReferrers; // Remove segments that are never used // TODO: remove unused portions of partially used segments as well @@ -477,7 +482,7 @@ void MemoryPacking::dropUnusedSegments(std::vector<Memory::Segment>& segments, bool used = false; auto referrersIt = referrers.find(i); bool hasReferrers = referrersIt != referrers.end(); - if (segments[i].isPassive) { + if (segments[i]->isPassive) { if (hasReferrers) { for (auto* referrer : referrersIt->second) { if (referrer->is<MemoryInit>()) { @@ -506,20 +511,22 @@ void MemoryPacking::dropUnusedSegments(std::vector<Memory::Segment>& segments, std::swap(referrers, usedReferrers); } -void MemoryPacking::createSplitSegments(Builder& builder, - const Memory::Segment& segment, - std::vector<Range>& ranges, - std::vector<Memory::Segment>& packed, - size_t segmentsRemaining) { +void MemoryPacking::createSplitSegments( + Builder& builder, + const DataSegment* segment, + std::vector<Range>& ranges, + std::vector<std::unique_ptr<DataSegment>>& packed, + size_t segmentsRemaining) { size_t segmentCount = 0; + bool hasExplicitName = false; for (size_t i = 0; i < ranges.size(); ++i) { Range& range = ranges[i]; if (range.isZero) { continue; } Expression* offset = nullptr; - if (!segment.isPassive) { - if (auto* c = segment.offset->dynCast<Const>()) { + if (!segment->isPassive) { + if (auto* c = segment->offset->dynCast<Const>()) { if (c->value.type == Type::i32) { offset = builder.makeConst(int32_t(c->value.geti32() + range.start)); } else { @@ -528,7 +535,7 @@ void MemoryPacking::createSplitSegments(Builder& builder, } } else { assert(ranges.size() == 1); - offset = segment.offset; + offset = segment->offset; } } if (WebLimitations::MaxDataSegments <= packed.size() + segmentsRemaining) { @@ -541,24 +548,27 @@ void MemoryPacking::createSplitSegments(Builder& builder, ranges.erase(ranges.begin() + i + 1, lastNonzero + 1); } Name name; - if (segment.name.is()) { + if (segment->name.is()) { // Name the first range after the original segment and all following // ranges get numbered accordingly. This means that for segments that // canot be split (segments that contains a single range) the input and // output segment have the same name. if (!segmentCount) { - name = segment.name; + name = segment->name; + hasExplicitName = segment->hasExplicitName; } else { - name = std::string(segment.name.c_str()) + "." + + name = std::string(segment->name.c_str()) + "." + std::to_string(segmentCount); } segmentCount++; } - packed.emplace_back(name, - segment.isPassive, - offset, - &segment.data[range.start], - range.end - range.start); + auto curr = Builder::makeDataSegment(name, + segment->isPassive, + offset, + &segment->data[range.start], + range.end - range.start); + curr->hasExplicitName = hasExplicitName; + packed.push_back(std::move(curr)); } } diff --git a/src/passes/Metrics.cpp b/src/passes/Metrics.cpp index 566738161..70d79c12c 100644 --- a/src/passes/Metrics.cpp +++ b/src/passes/Metrics.cpp @@ -53,7 +53,6 @@ struct Metrics } ModuleUtils::iterDefinedGlobals(*module, [&](Global* curr) { walkGlobal(curr); }); - walkMemory(&module->memory); // add imports / funcs / globals / exports / tables counts["[imports]"] = imports.getNumImports(); @@ -62,23 +61,26 @@ struct Metrics counts["[tags]"] = imports.getNumDefinedTags(); counts["[exports]"] = module->exports.size(); counts["[tables]"] = imports.getNumDefinedTables(); - // add memory and table - if (module->memory.exists) { - Index size = 0; - for (auto& segment : module->memory.segments) { - size += segment.data.size(); - } + + // add memory + walkMemory(&module->memory); + Index size = 0; + for (auto& segment : module->dataSegments) { + walkDataSegment(segment.get()); + size += segment->data.size(); + } + if (!module->dataSegments.empty()) { counts["[memory-data]"] = size; } - Index size = 0; - ModuleUtils::iterActiveElementSegments( - *module, [&](ElementSegment* segment) { size += segment->data.size(); }); + // add table + size = 0; for (auto& table : module->tables) { walkTable(table.get()); } for (auto& segment : module->elementSegments) { walkElementSegment(segment.get()); + size += segment->data.size(); } if (!module->tables.empty()) { counts["[table-data]"] = size; diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index a87b2e643..7260d7161 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -3006,57 +3006,57 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { printMemoryHeader(curr); o << '\n'; } - for (auto segment : curr->segments) { - doIndent(o, indent); - o << '('; - printMajor(o, "data "); - if (segment.name.is()) { - printName(segment.name, o); - o << ' '; - } - if (!segment.isPassive) { - visit(segment.offset); - o << ' '; - } - o << "\""; - for (size_t i = 0; i < segment.data.size(); i++) { - unsigned char c = segment.data[i]; - switch (c) { - case '\n': - o << "\\n"; - break; - case '\r': - o << "\\0d"; - break; - case '\t': - o << "\\t"; - break; - case '\f': - o << "\\0c"; - break; - case '\b': - o << "\\08"; - break; - case '\\': - o << "\\\\"; - break; - case '"': - o << "\\\""; - break; - case '\'': - o << "\\'"; - break; - default: { - if (c >= 32 && c < 127) { - o << c; - } else { - o << std::hex << '\\' << (c / 16) << (c % 16) << std::dec; - } + } + void visitDataSegment(DataSegment* curr) { + doIndent(o, indent); + o << '('; + printMajor(o, "data "); + if (curr->hasExplicitName) { + printName(curr->name, o); + o << ' '; + } + if (!curr->isPassive) { + visit(curr->offset); + o << ' '; + } + o << "\""; + for (size_t i = 0; i < curr->data.size(); i++) { + unsigned char c = curr->data[i]; + switch (c) { + case '\n': + o << "\\n"; + break; + case '\r': + o << "\\0d"; + break; + case '\t': + o << "\\t"; + break; + case '\f': + o << "\\0c"; + break; + case '\b': + o << "\\08"; + break; + case '\\': + o << "\\\\"; + break; + case '"': + o << "\\\""; + break; + case '\'': + o << "\\'"; + break; + default: { + if (c >= 32 && c < 127) { + o << c; + } else { + o << std::hex << '\\' << (c / 16) << (c % 16) << std::dec; } } } - o << "\")" << maybeNewLine; } + o << "\")" << maybeNewLine; } void printDylinkSection(const std::unique_ptr<DylinkSection>& dylinkSection) { doIndent(o, indent) << ";; dylink section\n"; @@ -3134,6 +3134,9 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { *curr, [&](Global* global) { visitGlobal(global); }); ModuleUtils::iterDefinedMemories( *curr, [&](Memory* memory) { visitMemory(memory); }); + for (auto& segment : curr->dataSegments) { + visitDataSegment(segment.get()); + } ModuleUtils::iterDefinedTables(*curr, [&](Table* table) { visitTable(table); }); for (auto& segment : curr->elementSegments) { diff --git a/src/passes/RemoveMemory.cpp b/src/passes/RemoveMemory.cpp index 399a32933..6f2f9aee7 100644 --- a/src/passes/RemoveMemory.cpp +++ b/src/passes/RemoveMemory.cpp @@ -25,7 +25,7 @@ namespace wasm { struct RemoveMemory : public Pass { void run(PassRunner* runner, Module* module) override { - module->memory.segments.clear(); + module->removeDataSegments([&](DataSegment* curr) { return true; }); } }; diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 686a95032..9268232b4 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -70,9 +70,9 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { : module(module) { queue = roots; // Globals used in memory/table init expressions are also roots - for (auto& segment : module->memory.segments) { - if (!segment.isPassive) { - walk(segment.offset); + for (auto& segment : module->dataSegments) { + if (!segment->isPassive) { + walk(segment->offset); } } for (auto& segment : module->elementSegments) { @@ -365,9 +365,9 @@ struct RemoveUnusedModuleElements : public Pass { if (!importsMemory) { // The memory is unobservable to the outside, we can remove the // contents. - module->memory.segments.clear(); + module->dataSegments.clear(); } - if (module->memory.segments.empty()) { + if (module->dataSegments.empty()) { module->memory.exists = false; module->memory.module = module->memory.base = Name(); module->memory.initial = 0; diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index e5afbf559..c828c84d0 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -191,26 +191,30 @@ void TranslateToFuzzReader::setupMemory() { // 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)); + auto segment = builder.makeDataSegment(); + segment->setName(Name::fromInt(i), false); + segment->isPassive = bool(upTo(2)); size_t segSize = upTo(USABLE_MEMORY * 2); - segment.data.resize(segSize); + segment->data.resize(segSize); for (size_t j = 0; j < segSize; j++) { - segment.data[j] = upTo(512); + segment->data[j] = upTo(512); } - if (!segment.isPassive) { - segment.offset = builder.makeConst(int32_t(memCovered)); + if (!segment->isPassive) { + segment->offset = builder.makeConst(int32_t(memCovered)); memCovered += segSize; } - wasm.memory.segments.push_back(segment); + wasm.dataSegments.push_back(std::move(segment)); } } else { // init some data - wasm.memory.segments.emplace_back(builder.makeConst(int32_t(0))); + auto segment = builder.makeDataSegment(); + segment->offset = builder.makeConst(int32_t(0)); + segment->setName(Name::fromInt(0), false); + wasm.dataSegments.push_back(std::move(segment)); 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)); + wasm.dataSegments[0]->data.push_back(value >= 256 ? 0 : (value & 0xff)); } } // Add memory hasher helper (for the hash, see hash.h). The function looks @@ -326,10 +330,10 @@ void TranslateToFuzzReader::setupTags() { } void TranslateToFuzzReader::finalizeMemory() { - for (auto& segment : wasm.memory.segments) { - Address maxOffset = segment.data.size(); - if (!segment.isPassive) { - if (auto* offset = segment.offset->dynCast<GlobalGet>()) { + for (auto& segment : wasm.dataSegments) { + Address maxOffset = segment->data.size(); + if (!segment->isPassive) { + if (auto* offset = segment->offset->dynCast<GlobalGet>()) { // Using a non-imported global in a segment offset is not valid in // wasm. This can occur due to us making what used to be an imported // global, in initial contents, be not imported any more. To fix that, @@ -342,11 +346,11 @@ void TranslateToFuzzReader::finalizeMemory() { if (!wasm.getGlobal(offset->name)->imported()) { // TODO: It would be better to avoid segment overlap so that // MemoryPacking can run. - segment.offset = + segment->offset = builder.makeConst(Literal::makeFromInt32(0, Type::i32)); } } - if (auto* offset = segment.offset->dynCast<Const>()) { + if (auto* offset = segment->offset->dynCast<Const>()) { maxOffset = maxOffset + offset->value.getInteger(); } } @@ -2905,8 +2909,8 @@ Expression* TranslateToFuzzReader::makeMemoryInit() { if (!allowMemory) { return makeTrivial(Type::none); } - uint32_t segment = upTo(wasm.memory.segments.size()); - size_t totalSize = wasm.memory.segments[segment].data.size(); + uint32_t segment = upTo(wasm.dataSegments.size()); + size_t totalSize = wasm.dataSegments[segment]->data.size(); size_t offsetVal = upTo(totalSize); size_t sizeVal = upTo(totalSize - offsetVal); Expression* dest = makePointer(); @@ -2919,7 +2923,7 @@ Expression* TranslateToFuzzReader::makeDataDrop() { if (!allowMemory) { return makeTrivial(Type::none); } - return builder.makeDataDrop(upTo(wasm.memory.segments.size())); + return builder.makeDataDrop(upTo(wasm.dataSegments.size())); } Expression* TranslateToFuzzReader::makeMemoryCopy() { diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 59e54d4f7..22fa87633 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -426,18 +426,19 @@ private: void applyMemoryToModule() { // Memory must have already been flattened into the standard form: one // segment at offset 0, or none. - if (wasm->memory.segments.empty()) { + if (wasm->dataSegments.empty()) { Builder builder(*wasm); - std::vector<char> empty; - wasm->memory.segments.push_back( - Memory::Segment(builder.makeConst(int32_t(0)), empty)); + auto curr = builder.makeDataSegment(); + curr->offset = builder.makeConst(int32_t(0)); + curr->setName(Name::fromInt(0), false); + wasm->dataSegments.push_back(std::move(curr)); } - auto& segment = wasm->memory.segments[0]; - assert(segment.offset->cast<Const>()->value.getInteger() == 0); + auto& segment = wasm->dataSegments[0]; + assert(segment->offset->cast<Const>()->value.getInteger() == 0); // Copy the current memory contents after execution into the Module's // memory. - segment.data = memory; + segment->data = memory; } // Serializing GC data requires more work than linear memory, because diff --git a/src/tools/wasm-metadce.cpp b/src/tools/wasm-metadce.cpp index 4b12446f4..741afb22f 100644 --- a/src/tools/wasm-metadce.cpp +++ b/src/tools/wasm-metadce.cpp @@ -235,11 +235,8 @@ struct MetaDCEGraph { }); rooter.walk(segment->offset); }); - for (auto& segment : wasm.memory.segments) { - if (!segment.isPassive) { - rooter.walk(segment.offset); - } - } + ModuleUtils::iterActiveDataSegments( + wasm, [&](DataSegment* segment) { rooter.walk(segment->offset); }); // A parallel scanner for function bodies struct Scanner : public WalkerPass<PostWalker<Scanner>> { diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp index 3cfc0bb11..2276f66a4 100644 --- a/src/tools/wasm-reduce.cpp +++ b/src/tools/wasm-reduce.cpp @@ -752,20 +752,14 @@ struct Reducer // TODO: bisection on segment shrinking? - void visitMemory(Memory* curr) { - std::cerr << "| try to simplify memory\n"; - + void visitDataSegment(DataSegment* curr) { // try to reduce to first function. first, shrink segment elements. // while we are shrinking successfully, keep going exponentially. bool shrank = false; - for (auto& segment : curr->segments) { - shrank = shrinkByReduction(&segment, 2); - } + shrank = shrinkByReduction(curr, 2); // the "opposite" of shrinking: copy a 'zero' element - for (auto& segment : curr->segments) { - reduceByZeroing( - &segment, 0, [](char item) { return item == 0; }, 2, shrank); - } + reduceByZeroing( + curr, 0, [](char item) { return item == 0; }, 2, shrank); } template<typename T, typename U, typename C> diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 96927b10e..659e0f58b 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1158,6 +1158,7 @@ class WasmBinaryWriter { std::unordered_map<Name, Index> globalIndexes; std::unordered_map<Name, Index> tableIndexes; std::unordered_map<Name, Index> elemIndexes; + std::unordered_map<Name, Index> dataIndexes; BinaryIndexes(Module& wasm) { auto addIndexes = [&](auto& source, auto& indexes) { @@ -1185,6 +1186,11 @@ class WasmBinaryWriter { elemIndexes[curr->name] = index; } + for (auto& curr : wasm.dataSegments) { + auto index = dataIndexes.size(); + dataIndexes[curr->name] = index; + } + // Globals may have tuple types in the IR, in which case they lower to // multiple globals, one for each tuple element, in the binary. Tuple // globals therefore occupy multiple binary indices, and we have to take @@ -1486,6 +1492,9 @@ public: // names std::vector<std::unique_ptr<ElementSegment>> elementSegments; + // we store data here after being read from binary, before we know their names + std::vector<std::unique_ptr<DataSegment>> dataSegments; + // we store globals here before wasm.addGlobal after we know their names std::vector<std::unique_ptr<Global>> globals; // we store global imports here before wasm.addGlobalImport after we know @@ -1590,7 +1599,7 @@ public: bool hasDataCount = false; void readDataSegments(); - void readDataCount(); + void readDataSegmentCount(); void readTableDeclarations(); void readElementSegments(); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 86de1f7db..20babacf1 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -106,6 +106,21 @@ public: return seg; } + static std::unique_ptr<DataSegment> + makeDataSegment(Name name = "", + bool isPassive = false, + Expression* offset = nullptr, + const char* init = "", + Address size = 0) { + auto seg = std::make_unique<DataSegment>(); + seg->name = name; + seg->isPassive = isPassive; + seg->offset = offset; + seg->data.resize(size); + std::copy_n(init, size, seg->data.begin()); + return seg; + } + static std::unique_ptr<Export> makeExport(Name name, Name value, ExternalKind kind) { auto export_ = std::make_unique<Export>(); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 7b17e344b..f682e89a2 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2604,19 +2604,18 @@ private: offset.finalize(); // apply active memory segments - for (size_t i = 0, e = wasm.memory.segments.size(); i < e; ++i) { - Memory::Segment& segment = wasm.memory.segments[i]; - if (segment.isPassive) { + for (size_t i = 0, e = wasm.dataSegments.size(); i < e; ++i) { + auto& segment = wasm.dataSegments[i]; + if (segment->isPassive) { continue; } - Const size; - size.value = Literal(uint32_t(segment.data.size())); + size.value = Literal(uint32_t(segment->data.size())); size.finalize(); MemoryInit init; init.segment = i; - init.dest = segment.offset; + init.dest = segment->offset; init.offset = &offset; init.size = &size; init.finalize(); @@ -3303,8 +3302,8 @@ public: NOTE_EVAL1(offset); NOTE_EVAL1(size); - assert(curr->segment < wasm.memory.segments.size()); - Memory::Segment& segment = wasm.memory.segments[curr->segment]; + assert(curr->segment < wasm.dataSegments.size()); + auto& segment = wasm.dataSegments[curr->segment]; Address destVal(dest.getSingleValue().getUnsigned()); Address offsetVal(uint32_t(offset.getSingleValue().geti32())); @@ -3313,7 +3312,7 @@ public: if (offsetVal + sizeVal > 0 && droppedSegments.count(curr->segment)) { trap("out of bounds segment access in memory.init"); } - if ((uint64_t)offsetVal + sizeVal > segment.data.size()) { + if ((uint64_t)offsetVal + sizeVal > segment->data.size()) { trap("out of bounds segment access in memory.init"); } auto* inst = getMemoryInstance(); @@ -3324,7 +3323,7 @@ public: Literal addr(destVal + i); inst->externalInterface->store8( inst->getFinalAddressWithoutOffset(addr, 1), - segment.data[offsetVal + i]); + segment->data[offsetVal + i]); } return {}; } diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 61383f084..75c4f0dec 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -134,6 +134,7 @@ class SExpressionWasmBuilder { int tableCounter = 0; int elemCounter = 0; int memoryCounter = 0; + int dataCounter = 0; // we need to know function return types before we parse their contents std::map<Name, HeapType> functionTypes; std::unordered_map<cashew::IString, Index> debugInfoFileIndices; @@ -320,8 +321,12 @@ 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, Name name, Expression* offset, bool isPassive); + void parseInnerData(Element& s, + Index i, + Name name, + bool hasExplicitName, + 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 7a803e2b2..7c10be3a9 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -50,6 +50,7 @@ template<typename SubType, typename ReturnType = void> struct Visitor { ReturnType visitTable(Table* curr) { return ReturnType(); } ReturnType visitElementSegment(ElementSegment* curr) { return ReturnType(); } ReturnType visitMemory(Memory* curr) { return ReturnType(); } + ReturnType visitDataSegment(DataSegment* curr) { return ReturnType(); } ReturnType visitTag(Tag* curr) { return ReturnType(); } ReturnType visitModule(Module* curr) { return ReturnType(); } @@ -204,12 +205,15 @@ struct Walker : public VisitorType { static_cast<SubType*>(this)->visitTable(table); } - void walkMemory(Memory* memory) { - for (auto& segment : memory->segments) { - if (!segment.isPassive) { - walk(segment.offset); - } + void walkDataSegment(DataSegment* segment) { + if (!segment->isPassive) { + walk(segment->offset); } + static_cast<SubType*>(this)->visitDataSegment(segment); + } + + void walkMemory(Memory* memory) { + // TODO: This method and walkTable should walk children too, or be renamed. static_cast<SubType*>(this)->visitMemory(memory); } @@ -255,6 +259,9 @@ struct Walker : public VisitorType { self->walkElementSegment(curr.get()); } self->walkMemory(&module->memory); + for (auto& curr : module->dataSegments) { + self->walkDataSegment(curr.get()); + } } // Walks module-level code, that is, code that is not in functions. diff --git a/src/wasm.h b/src/wasm.h index 3f2a83460..a466acdbe 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1862,6 +1862,13 @@ public: } }; +class DataSegment : public Named { +public: + bool isPassive = false; + Expression* offset = nullptr; + std::vector<char> data; // TODO: optimize +}; + class Memory : public Importable { public: static const Address::address32_t kPageSize = 64 * 1024; @@ -1870,37 +1877,9 @@ public: static const Address::address32_t kMaxSize32 = (uint64_t(4) * 1024 * 1024 * 1024) / kPageSize; - struct Segment { - // For use in name section only - Name name; - bool isPassive = false; - Expression* offset = nullptr; - std::vector<char> data; // TODO: optimize - Segment() = default; - Segment(Expression* offset) : offset(offset) {} - Segment(Expression* offset, const char* init, Address size) - : offset(offset) { - data.resize(size); - std::copy_n(init, size, data.begin()); - } - Segment(Expression* offset, std::vector<char>& init) : offset(offset) { - data.swap(init); - } - Segment(Name name, - bool isPassive, - Expression* offset, - const char* init, - Address size) - : name(name), isPassive(isPassive), offset(offset) { - data.resize(size); - std::copy_n(init, size, data.begin()); - } - }; - bool exists = false; Address initial = 0; // sizes are in pages Address max = kMaxSize32; - std::vector<Segment> segments; bool shared = false; Type indexType = Type::i32; @@ -1913,7 +1892,6 @@ public: name = ""; initial = 0; max = kMaxSize32; - segments.clear(); shared = false; indexType = Type::i32; } @@ -1957,6 +1935,7 @@ public: std::vector<std::unique_ptr<Global>> globals; std::vector<std::unique_ptr<Tag>> tags; std::vector<std::unique_ptr<ElementSegment>> elementSegments; + std::vector<std::unique_ptr<DataSegment>> dataSegments; std::vector<std::unique_ptr<Table>> tables; Memory memory; @@ -1992,6 +1971,7 @@ private: std::unordered_map<Name, Function*> functionsMap; std::unordered_map<Name, Table*> tablesMap; std::unordered_map<Name, ElementSegment*> elementSegmentsMap; + std::unordered_map<Name, DataSegment*> dataSegmentsMap; std::unordered_map<Name, Global*> globalsMap; std::unordered_map<Name, Tag*> tagsMap; @@ -2002,12 +1982,14 @@ public: Function* getFunction(Name name); Table* getTable(Name name); ElementSegment* getElementSegment(Name name); + DataSegment* getDataSegment(Name name); Global* getGlobal(Name name); Tag* getTag(Name name); Export* getExportOrNull(Name name); Table* getTableOrNull(Name name); ElementSegment* getElementSegmentOrNull(Name name); + DataSegment* getDataSegmentOrNull(Name name); Function* getFunctionOrNull(Name name); Global* getGlobalOrNull(Name name); Tag* getTagOrNull(Name name); @@ -2021,6 +2003,7 @@ public: Function* addFunction(std::unique_ptr<Function>&& curr); Table* addTable(std::unique_ptr<Table>&& curr); ElementSegment* addElementSegment(std::unique_ptr<ElementSegment>&& curr); + DataSegment* addDataSegment(std::unique_ptr<DataSegment>&& curr); Global* addGlobal(std::unique_ptr<Global>&& curr); Tag* addTag(std::unique_ptr<Tag>&& curr); @@ -2030,6 +2013,7 @@ public: void removeFunction(Name name); void removeTable(Name name); void removeElementSegment(Name name); + void removeDataSegment(Name name); void removeGlobal(Name name); void removeTag(Name name); @@ -2037,6 +2021,7 @@ public: void removeFunctions(std::function<bool(Function*)> pred); void removeTables(std::function<bool(Table*)> pred); void removeElementSegments(std::function<bool(ElementSegment*)> pred); + void removeDataSegments(std::function<bool(DataSegment*)> pred); void removeGlobals(std::function<bool(Global*)> pred); void removeTags(std::function<bool(Tag*)> pred); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index c161a623b..b74331aed 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -517,36 +517,36 @@ void WasmBinaryWriter::writeExports() { } void WasmBinaryWriter::writeDataCount() { - if (!wasm->features.hasBulkMemory() || !wasm->memory.segments.size()) { + if (!wasm->features.hasBulkMemory() || !wasm->dataSegments.size()) { return; } auto start = startSection(BinaryConsts::Section::DataCount); - o << U32LEB(wasm->memory.segments.size()); + o << U32LEB(wasm->dataSegments.size()); finishSection(start); } void WasmBinaryWriter::writeDataSegments() { - if (wasm->memory.segments.size() == 0) { + if (wasm->dataSegments.size() == 0) { return; } - if (wasm->memory.segments.size() > WebLimitations::MaxDataSegments) { + if (wasm->dataSegments.size() > WebLimitations::MaxDataSegments) { std::cerr << "Some VMs may not accept this binary because it has a large " << "number of data segments. Run the limit-segments pass to " << "merge segments.\n"; } auto start = startSection(BinaryConsts::Section::Data); - o << U32LEB(wasm->memory.segments.size()); - for (auto& segment : wasm->memory.segments) { + o << U32LEB(wasm->dataSegments.size()); + for (auto& segment : wasm->dataSegments) { uint32_t flags = 0; - if (segment.isPassive) { + if (segment->isPassive) { flags |= BinaryConsts::IsPassive; } o << U32LEB(flags); - if (!segment.isPassive) { - writeExpression(segment.offset); + if (!segment->isPassive) { + writeExpression(segment->offset); o << int8_t(BinaryConsts::End); } - writeInlineBuffer(segment.data.data(), segment.data.size()); + writeInlineBuffer(segment->data.data(), segment->data.size()); } finishSection(start); } @@ -919,8 +919,8 @@ void WasmBinaryWriter::writeNames() { // data segment names if (wasm->memory.exists) { Index count = 0; - for (auto& seg : wasm->memory.segments) { - if (seg.name.is()) { + for (auto& seg : wasm->dataSegments) { + if (seg->hasExplicitName) { count++; } } @@ -929,11 +929,11 @@ void WasmBinaryWriter::writeNames() { auto substart = startSubsection(BinaryConsts::UserSections::Subsection::NameData); o << U32LEB(count); - for (Index i = 0; i < wasm->memory.segments.size(); i++) { - auto& seg = wasm->memory.segments[i]; - if (seg.name.is()) { + for (Index i = 0; i < wasm->dataSegments.size(); i++) { + auto& seg = wasm->dataSegments[i]; + if (seg->name.is()) { o << U32LEB(i); - writeEscapedName(seg.name.str); + writeEscapedName(seg->name.str); } } finishSubsection(substart); @@ -1482,7 +1482,7 @@ void WasmBinaryBuilder::read() { readDataSegments(); break; case BinaryConsts::Section::DataCount: - readDataCount(); + readDataSegmentCount(); break; case BinaryConsts::Section::Table: readTableDeclarations(); @@ -2791,7 +2791,7 @@ Expression* WasmBinaryBuilder::popTypedExpression(Type type) { } void WasmBinaryBuilder::validateBinary() { - if (hasDataCount && wasm.memory.segments.size() != dataCount) { + if (hasDataCount && dataSegments.size() != dataCount) { throwError("Number of segments does not agree with DataCount section"); } } @@ -2809,7 +2809,9 @@ void WasmBinaryBuilder::processNames() { for (auto& segment : elementSegments) { wasm.addElementSegment(std::move(segment)); } - + for (auto& segment : dataSegments) { + wasm.addDataSegment(std::move(segment)); + } // now that we have names, apply things if (startIndex != static_cast<Index>(-1)) { @@ -2888,8 +2890,8 @@ void WasmBinaryBuilder::processNames() { wasm.updateMaps(); } -void WasmBinaryBuilder::readDataCount() { - BYN_TRACE("== readDataCount\n"); +void WasmBinaryBuilder::readDataSegmentCount() { + BYN_TRACE("== readDataSegmentCount\n"); hasDataCount = true; dataCount = getU32LEB(); } @@ -2898,26 +2900,27 @@ void WasmBinaryBuilder::readDataSegments() { BYN_TRACE("== readDataSegments\n"); auto num = getU32LEB(); for (size_t i = 0; i < num; i++) { - Memory::Segment curr; + auto curr = Builder::makeDataSegment(); 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; + curr->setName(Name::fromInt(i), false); + curr->isPassive = flags & BinaryConsts::IsPassive; if (flags & BinaryConsts::HasIndex) { auto memIndex = getU32LEB(); if (memIndex != 0) { throwError("nonzero memory index"); } } - if (!curr.isPassive) { - curr.offset = readExpression(); + if (!curr->isPassive) { + curr->offset = readExpression(); } auto size = getU32LEB(); auto data = getByteView(size); - curr.data = {data.first, data.second}; - wasm.memory.segments.push_back(std::move(curr)); + curr->data = {data.first, data.second}; + dataSegments.push_back(std::move(curr)); } } @@ -3254,14 +3257,16 @@ void WasmBinaryBuilder::readNames(size_t payloadLen) { } } else if (nameType == BinaryConsts::UserSections::Subsection::NameData) { auto num = getU32LEB(); + NameProcessor processor; for (size_t i = 0; i < num; i++) { auto index = getU32LEB(); auto rawName = getInlineString(); - if (index < wasm.memory.segments.size()) { - wasm.memory.segments[i].name = rawName; + auto name = processor.process(rawName); + if (index < dataSegments.size()) { + dataSegments[i]->setExplicitName(name); } else { - std::cerr << "warning: memory index out of bounds in name section, " - "memory subsection: " + std::cerr << "warning: data index out of bounds in name section, " + "data subsection: " << std::string(rawName.str) << " at index " << std::to_string(index) << std::endl; } diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp index fea5048f6..3416c731d 100644 --- a/src/wasm/wasm-emscripten.cpp +++ b/src/wasm/wasm-emscripten.cpp @@ -111,12 +111,12 @@ public: StringConstantTracker(Module& wasm) : wasm(wasm) { calcSegmentOffsets(); } const char* stringAtAddr(Address address) { - for (unsigned i = 0; i < wasm.memory.segments.size(); ++i) { - Memory::Segment& segment = wasm.memory.segments[i]; + for (unsigned i = 0; i < wasm.dataSegments.size(); ++i) { + auto& segment = wasm.dataSegments[i]; Address offset = segmentOffsets[i]; if (offset != UNKNOWN_OFFSET && address >= offset && - address < offset + segment.data.size()) { - return &segment.data[address - offset]; + address < offset + segment->data.size()) { + return &segment->data[address - offset]; } } Fatal() << "unable to find data for ASM/EM_JS const at: " << address; @@ -159,9 +159,9 @@ private: } searcher(passiveOffsets); searcher.walkModule(&wasm); } - for (unsigned i = 0; i < wasm.memory.segments.size(); ++i) { - auto& segment = wasm.memory.segments[i]; - if (segment.isPassive) { + for (unsigned i = 0; i < wasm.dataSegments.size(); ++i) { + auto& segment = wasm.dataSegments[i]; + if (segment->isPassive) { auto it = passiveOffsets.find(i); if (it != passiveOffsets.end()) { segmentOffsets.push_back(it->second); @@ -169,7 +169,7 @@ private: // This was a non-constant offset (perhaps TLS) segmentOffsets.push_back(UNKNOWN_OFFSET); } - } else if (auto* addrConst = segment.offset->dynCast<Const>()) { + } else if (auto* addrConst = segment->offset->dynCast<Const>()) { auto address = addrConst->value.getUnsigned(); segmentOffsets.push_back(address); } else { @@ -220,7 +220,7 @@ static void removeSegment(Module& wasm, Index segment) { // Resize the segment to zero. In theory we should completely remove it // but that would mean re-numbering the segments that follow which is // non-trivial. - wasm.memory.segments[segment].data.resize(0); + wasm.dataSegments[segment]->data.resize(0); } static Address getExportedAddress(Module& wasm, Export* export_) { @@ -250,9 +250,9 @@ static std::vector<AsmConst> findEmAsmConsts(Module& wasm, StringConstantTracker stringTracker(wasm); Address startAddress = getExportedAddress(wasm, start); Address endAddress = getExportedAddress(wasm, end); - for (Index i = 0; i < wasm.memory.segments.size(); i++) { + for (Index i = 0; i < wasm.dataSegments.size(); i++) { Address segmentStart = stringTracker.segmentOffsets[i]; - size_t segmentSize = wasm.memory.segments[i].data.size(); + size_t segmentSize = wasm.dataSegments[i]->data.size(); if (segmentStart <= startAddress && segmentStart + segmentSize >= endAddress) { Address address = startAddress; @@ -269,7 +269,7 @@ static std::vector<AsmConst> findEmAsmConsts(Module& wasm, // If we can't remove the whole segment then just set the string // data to zero. size_t segmentOffset = startAddress - segmentStart; - char* startElem = &wasm.memory.segments[i].data[segmentOffset]; + char* startElem = &wasm.dataSegments[i]->data[segmentOffset]; memset(startElem, 0, endAddress - startAddress); } break; @@ -343,18 +343,18 @@ EmJsWalker findEmJsFuncsAndReturnWalker(Module& wasm) { // single segment. // We can detect this by checking for segments that contain only JS strings. // When we find such segements we remove them from the final binary. - for (Index i = 0; i < wasm.memory.segments.size(); i++) { + for (Index i = 0; i < wasm.dataSegments.size(); i++) { Address start = walker.stringTracker.segmentOffsets[i]; Address cur = start; - while (cur < start + wasm.memory.segments[i].data.size()) { + while (cur < start + wasm.dataSegments[i]->data.size()) { if (walker.codeAddresses.count(cur) == 0) { break; } cur.addr += walker.codeAddresses[cur]; } - if (cur == start + wasm.memory.segments[i].data.size()) { + if (cur == start + wasm.dataSegments[i]->data.size()) { // Entire segment is contains JS strings. Remove it. removeSegment(wasm, i); } @@ -509,24 +509,24 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata() { void EmscriptenGlueGenerator::separateDataSegments(Output* outfile, Address base) { size_t lastEnd = 0; - for (Memory::Segment& seg : wasm.memory.segments) { - if (seg.isPassive) { + for (auto& seg : wasm.dataSegments) { + if (seg->isPassive) { Fatal() << "separating passive segments not implemented"; } - if (!seg.offset->is<Const>()) { + if (!seg->offset->is<Const>()) { Fatal() << "separating relocatable segments not implemented"; } - size_t offset = seg.offset->cast<Const>()->value.getInteger(); + size_t offset = seg->offset->cast<Const>()->value.getInteger(); offset -= base; size_t fill = offset - lastEnd; if (fill > 0) { std::vector<char> buf(fill); outfile->write(buf.data(), fill); } - outfile->write(seg.data.data(), seg.data.size()); - lastEnd = offset + seg.data.size(); + outfile->write(seg->data.data(), seg->data.size()); + lastEnd = offset + seg->data.size(); } - wasm.memory.segments.clear(); + wasm.dataSegments.clear(); } } // namespace wasm diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index dd92278c6..b59319be2 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -3038,8 +3038,9 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { } else { offset->set(Literal(int32_t(0))); } - parseInnerData(inner, j, {}, offset, false); - wasm.memory.initial = wasm.memory.segments[0].data.size(); + parseInnerData( + inner, j, Name::fromInt(dataCounter++), false, offset, false); + wasm.memory.initial = wasm.dataSegments[0]->data.size(); return; } } @@ -3073,9 +3074,15 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { if (auto size = strlen(input)) { std::vector<char> data; stringToBinary(input, size, data); - wasm.memory.segments.emplace_back(offset, data.data(), data.size()); + auto segment = Builder::makeDataSegment( + Name::fromInt(dataCounter++), false, offset, data.data(), data.size()); + segment->hasExplicitName = false; + wasm.dataSegments.push_back(std::move(segment)); } else { - wasm.memory.segments.emplace_back(offset, "", 0); + auto segment = + Builder::makeDataSegment(Name::fromInt(dataCounter++), false, offset); + segment->hasExplicitName = false; + wasm.dataSegments.push_back(std::move(segment)); } i++; } @@ -3085,13 +3092,15 @@ void SExpressionWasmBuilder::parseData(Element& s) { if (!wasm.memory.exists) { throw ParseException("data but no memory", s.line, s.col); } + Index i = 1; + Name name = Name::fromInt(dataCounter++); + bool hasExplicitName = false; bool isPassive = true; Expression* offset = nullptr; - Index i = 1; - Name name; if (s[i]->isStr() && s[i]->dollared()) { name = s[i++]->str(); + hasExplicitName = true; } if (s[i]->isList()) { @@ -3112,11 +3121,15 @@ void SExpressionWasmBuilder::parseData(Element& s) { isPassive = false; } - parseInnerData(s, i, name, offset, isPassive); + parseInnerData(s, i, name, hasExplicitName, offset, isPassive); } -void SExpressionWasmBuilder::parseInnerData( - Element& s, Index i, Name name, Expression* offset, bool isPassive) { +void SExpressionWasmBuilder::parseInnerData(Element& s, + Index i, + Name name, + bool hasExplicitName, + Expression* offset, + bool isPassive) { std::vector<char> data; while (i < s.size()) { const char* input = s[i++]->c_str(); @@ -3124,8 +3137,10 @@ void SExpressionWasmBuilder::parseInnerData( stringToBinary(input, size, data); } } - wasm.memory.segments.emplace_back( - name, isPassive, offset, data.data(), data.size()); + auto curr = + Builder::makeDataSegment(name, isPassive, offset, data.data(), data.size()); + curr->hasExplicitName = hasExplicitName; + wasm.dataSegments.push_back(std::move(curr)); } void SExpressionWasmBuilder::parseExport(Element& s) { diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 54bc2f9d4..4c6e8e929 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -1301,7 +1301,7 @@ void FunctionValidator::visitMemoryInit(MemoryInit* curr) { "Memory operations require a memory")) { return; } - shouldBeTrue(curr->segment < getModule()->memory.segments.size(), + shouldBeTrue(curr->segment < getModule()->dataSegments.size(), curr, "memory.init segment index out of bounds"); } @@ -1317,7 +1317,7 @@ void FunctionValidator::visitDataDrop(DataDrop* curr) { "Memory operations require a memory")) { return; } - shouldBeTrue(curr->segment < getModule()->memory.segments.size(), + shouldBeTrue(curr->segment < getModule()->dataSegments.size(), curr, "data.drop segment index out of bounds"); } @@ -3068,53 +3068,53 @@ static void validateMemory(Module& module, ValidationInfo& info) { "memory", "memory is shared, but atomics are disabled"); } - for (auto& segment : curr.segments) { - auto size = segment.data.size(); - if (segment.isPassive) { + for (auto& segment : module.dataSegments) { + auto size = segment->data.size(); + if (segment->isPassive) { info.shouldBeTrue(module.features.hasBulkMemory(), - segment.offset, + segment->offset, "nonzero segment flags (bulk memory is disabled)"); - info.shouldBeEqual(segment.offset, + info.shouldBeEqual(segment->offset, (Expression*)nullptr, - segment.offset, + segment->offset, "passive segment should not have an offset"); } else { if (curr.is64()) { - if (!info.shouldBeEqual(segment.offset->type, + if (!info.shouldBeEqual(segment->offset->type, Type(Type::i64), - segment.offset, + segment->offset, "segment offset should be i64")) { continue; } } else { - if (!info.shouldBeEqual(segment.offset->type, + if (!info.shouldBeEqual(segment->offset->type, Type(Type::i32), - segment.offset, + segment->offset, "segment offset should be i32")) { continue; } } - info.shouldBeTrue(checkSegmentOffset(segment.offset, - segment.data.size(), + info.shouldBeTrue(checkSegmentOffset(segment->offset, + segment->data.size(), curr.initial * Memory::kPageSize, module.features), - segment.offset, + segment->offset, "memory segment offset should be reasonable"); - if (segment.offset->is<Const>()) { - auto start = segment.offset->cast<Const>()->value.getUnsigned(); + if (segment->offset->is<Const>()) { + auto start = segment->offset->cast<Const>()->value.getUnsigned(); auto end = start + size; info.shouldBeTrue(end <= curr.initial * Memory::kPageSize, - segment.data.size(), + segment->data.size(), "segment size should fit in memory (end)"); } - FunctionValidator(module, &info).validate(segment.offset); + FunctionValidator(module, &info).validate(segment->offset); } // 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->data.size(), "segment size should fit in memory (initial)"); } } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index ca9691346..8d4f1dcee 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1275,6 +1275,10 @@ ElementSegment* Module::getElementSegment(Name name) { return getModuleElement(elementSegmentsMap, name, "getElementSegment"); } +DataSegment* Module::getDataSegment(Name name) { + return getModuleElement(dataSegmentsMap, name, "getDataSegment"); +} + Global* Module::getGlobal(Name name) { return getModuleElement(globalsMap, name, "getGlobal"); } @@ -1308,6 +1312,10 @@ ElementSegment* Module::getElementSegmentOrNull(Name name) { return getModuleElementOrNull(elementSegmentsMap, name); } +DataSegment* Module::getDataSegmentOrNull(Name name) { + return getModuleElementOrNull(dataSegmentsMap, name); +} + Global* Module::getGlobalOrNull(Name name) { return getModuleElementOrNull(globalsMap, name); } @@ -1383,6 +1391,11 @@ Module::addElementSegment(std::unique_ptr<ElementSegment>&& curr) { elementSegments, elementSegmentsMap, std::move(curr), "addElementSegment"); } +DataSegment* Module::addDataSegment(std::unique_ptr<DataSegment>&& curr) { + return addModuleElement( + dataSegments, dataSegmentsMap, std::move(curr), "addDataSegment"); +} + Global* Module::addGlobal(std::unique_ptr<Global>&& curr) { return addModuleElement(globals, globalsMap, std::move(curr), "addGlobal"); } @@ -1416,6 +1429,9 @@ void Module::removeTable(Name name) { void Module::removeElementSegment(Name name) { removeModuleElement(elementSegments, elementSegmentsMap, name); } +void Module::removeDataSegment(Name name) { + removeModuleElement(dataSegments, dataSegmentsMap, name); +} void Module::removeGlobal(Name name) { removeModuleElement(globals, globalsMap, name); } @@ -1449,6 +1465,9 @@ void Module::removeTables(std::function<bool(Table*)> pred) { void Module::removeElementSegments(std::function<bool(ElementSegment*)> pred) { removeModuleElements(elementSegments, elementSegmentsMap, pred); } +void Module::removeDataSegments(std::function<bool(DataSegment*)> pred) { + removeModuleElements(dataSegments, dataSegmentsMap, pred); +} void Module::removeGlobals(std::function<bool(Global*)> pred) { removeModuleElements(globals, globalsMap, pred); } @@ -1473,6 +1492,10 @@ void Module::updateMaps() { for (auto& curr : elementSegments) { elementSegmentsMap[curr->name] = curr.get(); } + dataSegmentsMap.clear(); + for (auto& curr : dataSegments) { + dataSegmentsMap[curr->name] = curr.get(); + } globalsMap.clear(); for (auto& curr : globals) { globalsMap[curr->name] = curr.get(); diff --git a/src/wasm2js.h b/src/wasm2js.h index b74572cfe..5b98362ad 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -94,8 +94,8 @@ bool isTableExported(Module& wasm) { } bool hasActiveSegments(Module& wasm) { - for (Index i = 0; i < wasm.memory.segments.size(); i++) { - if (!wasm.memory.segments[i].isPassive) { + for (Index i = 0; i < wasm.dataSegments.size(); i++) { + if (!wasm.dataSegments[i]->isPassive) { return true; } } @@ -2665,13 +2665,13 @@ void Wasm2JSGlue::emitMemory() { // If there are no memory segments, we don't need to emit any support code for // segment creation. - if ((!wasm.memory.exists) || wasm.memory.segments.empty()) { + if ((!wasm.memory.exists) || wasm.dataSegments.empty()) { return; } // If we have passive memory segments, we need to store those. - for (auto& seg : wasm.memory.segments) { - if (seg.isPassive) { + for (auto& seg : wasm.dataSegments) { + if (seg->isPassive) { out << " var memorySegments = {};\n"; break; } @@ -2706,20 +2706,20 @@ void Wasm2JSGlue::emitMemory() { } )"; - for (Index i = 0; i < wasm.memory.segments.size(); i++) { - auto& seg = wasm.memory.segments[i]; - if (seg.isPassive) { + for (Index i = 0; i < wasm.dataSegments.size(); i++) { + auto& seg = wasm.dataSegments[i]; + if (seg->isPassive) { // Fancy passive segments are decoded into typed arrays on the side, for // later copying. out << "memorySegments[" << i << "] = base64DecodeToExistingUint8Array(new Uint8Array(" - << seg.data.size() << ")" - << ", 0, \"" << base64Encode(seg.data) << "\");\n"; + << seg->data.size() << ")" + << ", 0, \"" << base64Encode(seg->data) << "\");\n"; } } if (hasActiveSegments(wasm)) { - auto globalOffset = [&](const Memory::Segment& segment) { + auto globalOffset = [&](const DataSegment& segment) { if (auto* c = segment.offset->dynCast<Const>()) { return std::to_string(c->value.getInteger()); } @@ -2732,12 +2732,12 @@ void Wasm2JSGlue::emitMemory() { }; out << "function initActiveSegments(imports) {\n"; - for (Index i = 0; i < wasm.memory.segments.size(); i++) { - auto& seg = wasm.memory.segments[i]; - if (!seg.isPassive) { + for (Index i = 0; i < wasm.dataSegments.size(); i++) { + auto& seg = wasm.dataSegments[i]; + if (!seg->isPassive) { // Plain active segments are decoded directly into the main memory. out << " base64DecodeToExistingUint8Array(bufferView, " - << globalOffset(seg) << ", \"" << base64Encode(seg.data) + << globalOffset(*seg) << ", \"" << base64Encode(seg->data) << "\");\n"; } } |