summaryrefslogtreecommitdiff
path: root/src/ir/memory-utils.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/ir/memory-utils.h')
-rw-r--r--src/ir/memory-utils.h105
1 files changed, 104 insertions, 1 deletions
diff --git a/src/ir/memory-utils.h b/src/ir/memory-utils.h
index ea19ca799..0bad50c5a 100644
--- a/src/ir/memory-utils.h
+++ b/src/ir/memory-utils.h
@@ -22,6 +22,7 @@
#include "literal.h"
#include "wasm.h"
+#include "wasm-binary.h"
namespace wasm {
@@ -59,7 +60,109 @@ namespace MemoryUtils {
memory.initial = memory.max = 1;
}
}
-};
+
+ // 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) {
+ return true;
+ }
+
+ auto isEmpty = [](Memory::Segment& segment) {
+ return segment.data.size() == 0;
+ };
+
+ auto isConstantOffset = [](Memory::Segment& 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)) {
+ numConstant++;
+ } else {
+ numDynamic++;
+ }
+ }
+ hasPassiveSegments |= segment.isPassive;
+ }
+
+ if (hasPassiveSegments) {
+ return false;
+ }
+
+ // check if we have too many dynamic data segments, which we can do nothing about
+ auto num = numConstant + numDynamic;
+ if (numDynamic + 1 >= WebLimitations::MaxDataSegments) {
+ return false;
+ }
+
+ // 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);
+ }
+
+ std::vector<Memory::Segment> mergedSegments;
+ mergedSegments.reserve(WebLimitations::MaxDataSegments);
+
+ // drop empty segments and pass through dynamic-offset segments
+ for (auto& segment : memory.segments) {
+ if (isEmpty(segment)) continue;
+ if (isConstantOffset(segment)) continue;
+ mergedSegments.push_back(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 < memory.segments.size(); i++) {
+ auto& segment = memory.segments[i];
+ if (!isRelevant(segment)) continue;
+ if (mergedSegments.size() + 2 < WebLimitations::MaxDataSegments) {
+ mergedSegments.push_back(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)) continue;
+ auto offset = segment.offset->cast<Const>()->value.getInteger();
+ start = std::min(start, offset);
+ }
+ // create the segment and add in all the data
+ auto* c = module.allocator.alloc<Const>();
+ c->value = Literal(int32_t(start));
+ c->type = i32;
+
+ Memory::Segment combined(c);
+ for (Index j = i; j < memory.segments.size(); j++) {
+ auto& segment = memory.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));
+ }
+ mergedSegments.push_back(combined);
+ break;
+ }
+
+ memory.segments.swap(mergedSegments);
+ return true;
+ }
+} // namespace MemoryUtils
} // namespace wasm