diff options
Diffstat (limited to 'src/ir/memory-utils.h')
-rw-r--r-- | src/ir/memory-utils.h | 105 |
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 |