summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAshley Nelson <nashley@google.com>2022-06-21 20:57:43 -0700
committerGitHub <noreply@github.com>2022-06-21 20:57:43 -0700
commit3b9c2e85fa5d97ba08a95c0c7cce7d041e699cde (patch)
treec01eb86869401931006b6503e47d60b9a44511b0 /src
parent7fa4c0841c31930759fbad2efb8ada3ef0e6f3ef (diff)
downloadbinaryen-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.cpp34
-rw-r--r--src/ir/ReFinalize.cpp3
-rw-r--r--src/ir/memory-utils.cpp26
-rw-r--r--src/ir/memory-utils.h75
-rw-r--r--src/ir/module-utils.h30
-rw-r--r--src/ir/utils.h2
-rw-r--r--src/passes/Memory64Lowering.cpp15
-rw-r--r--src/passes/MemoryPacking.cpp120
-rw-r--r--src/passes/Metrics.cpp22
-rw-r--r--src/passes/Print.cpp97
-rw-r--r--src/passes/RemoveMemory.cpp2
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp10
-rw-r--r--src/tools/fuzzing/fuzzing.cpp40
-rw-r--r--src/tools/wasm-ctor-eval.cpp15
-rw-r--r--src/tools/wasm-metadce.cpp7
-rw-r--r--src/tools/wasm-reduce.cpp14
-rw-r--r--src/wasm-binary.h11
-rw-r--r--src/wasm-builder.h15
-rw-r--r--src/wasm-interpreter.h19
-rw-r--r--src/wasm-s-parser.h9
-rw-r--r--src/wasm-traversal.h17
-rw-r--r--src/wasm.h43
-rw-r--r--src/wasm/wasm-binary.cpp67
-rw-r--r--src/wasm/wasm-emscripten.cpp44
-rw-r--r--src/wasm/wasm-s-parser.cpp37
-rw-r--r--src/wasm/wasm-validator.cpp40
-rw-r--r--src/wasm/wasm.cpp23
-rw-r--r--src/wasm2js.h30
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";
}
}