summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/wasm-binary.h9
-rw-r--r--src/wasm/wasm-binary.cpp82
2 files changed, 86 insertions, 5 deletions
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index a407c56e4..12aecfeaa 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -37,7 +37,14 @@ namespace wasm {
enum {
// the maximum amount of bytes we emit per LEB
- MaxLEB32Bytes = 5
+ MaxLEB32Bytes = 5,
+};
+
+// wasm VMs on the web have decided to impose some limits on what they
+// accept
+enum WebLimitations {
+ MaxDataSegments = 100 * 1000,
+ MaxFunctionBodySize = 128 * 1024
};
template<typename T, typename MiniT>
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 868fad1f6..1a7ab6267 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -328,20 +328,94 @@ void WasmBinaryWriter::writeExports() {
finishSection(start);
}
+static bool isEmpty(Memory::Segment& segment) {
+ return segment.data.size() == 0;
+}
+
+static bool isConstantOffset(Memory::Segment& segment) {
+ return segment.offset->is<Const>();
+}
+
void WasmBinaryWriter::writeDataSegments() {
if (wasm->memory.segments.size() == 0) return;
- uint32_t num = 0;
+ Index numConstant = 0,
+ numDynamic = 0;
for (auto& segment : wasm->memory.segments) {
- if (segment.data.size() > 0) num++;
+ if (!isEmpty(segment)) {
+ if (isConstantOffset(segment)) {
+ numConstant++;
+ } else {
+ numDynamic++;
+ }
+ }
+ }
+ // check if we have too many dynamic data segments, which we can do nothing about
+ auto num = numConstant + numDynamic;
+ if (numDynamic + 1 >= WebLimitations::MaxDataSegments) {
+ std::cerr << "too many non-constant-offset data segments, wasm VMs may not accept this binary" << std::endl;
+ }
+ // we'll merge constant segments if we must
+ if (numConstant + numDynamic >= WebLimitations::MaxDataSegments) {
+ numConstant = WebLimitations::MaxDataSegments - numDynamic - 1;
+ num = numConstant + numDynamic;
+ assert(num == WebLimitations::MaxDataSegments - 1);
}
auto start = startSection(BinaryConsts::Section::Data);
o << U32LEB(num);
- for (auto& segment : wasm->memory.segments) {
- if (segment.data.size() == 0) continue;
+ // first, emit all non-constant-offset segments; then emit the constants,
+ // which we may merge if forced to
+ Index emitted = 0;
+ auto emit = [&](Memory::Segment& segment) {
o << U32LEB(0); // Linear memory 0 in the MVP
writeExpression(segment.offset);
o << int8_t(BinaryConsts::End);
writeInlineBuffer(&segment.data[0], segment.data.size());
+ emitted++;
+ };
+ auto& segments = wasm->memory.segments;
+ for (auto& segment : segments) {
+ if (isEmpty(segment)) continue;
+ if (isConstantOffset(segment)) continue;
+ emit(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) {
+ return !isEmpty(segment) && isConstantOffset(segment);
+ };
+ for (Index i = 0; i < segments.size(); i++) {
+ auto& segment = segments[i];
+ if (!isRelevant(segment)) continue;
+ if (emitted + 2 < WebLimitations::MaxDataSegments) {
+ emit(segment);
+ } else {
+ // 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 < segments.size(); j++) {
+ auto& segment = segments[j];
+ if (!isRelevant(segment)) continue;
+ auto offset = segment.offset->cast<Const>()->value.getInteger();
+ start = std::min(start, offset);
+ }
+ // create the segment and add in all the data
+ Const c;
+ c.value = Literal(int32_t(start));
+ c.type = i32;
+ Memory::Segment combined(&c);
+ for (Index j = i; j < segments.size(); j++) {
+ auto& segment = segments[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);
+ }
+ std::copy(segment.data.begin(), segment.data.end(), combined.data.begin() + offset - start);
+ }
+ emit(combined);
+ break;
+ }
}
finishSection(start);
}