/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "pass.h" #include "wasm.h" #include "wasm-binary.h" #include "wasm-builder.h" namespace wasm { // Adding segments adds overhead, this is a rough estimate const Index OVERHEAD = 8; struct MemoryPacking : public Pass { bool modifiesBinaryenIR() override { return false; } void run(PassRunner* runner, Module* module) override { // Conservatively refuse to change segments if bulk memory is enabled to // avoid invalidating segment indices or segment contents referenced from // memory.init instructions. // TODO: optimize in the presence of memory.init instructions if (!module->memory.exists || module->features.hasBulkMemory()) { return; } std::vector packed; // we can only handle a constant offset for splitting auto isSplittable = [&](const Memory::Segment& segment) { return segment.offset->is(); }; for (auto& segment : module->memory.segments) { if (!isSplittable(segment)) { packed.push_back(segment); } } size_t numRemaining = module->memory.segments.size() - packed.size(); // Split only if we have room for more segments auto shouldSplit = [&]() { return WebLimitations::MaxDataSegments > packed.size() + numRemaining; }; for (auto& segment : module->memory.segments) { if (!isSplittable(segment)) continue; // skip final zeros while (segment.data.size() > 0 && segment.data.back() == 0) { segment.data.pop_back(); } if (!shouldSplit()) { packed.push_back(segment); continue; } auto* offset = segment.offset->cast(); // Find runs of zeros, and split auto& data = segment.data; auto base = offset->value.geti32(); Index start = 0; // create new segments while (start < data.size()) { // skip initial zeros while (start < data.size() && data[start] == 0) { start++; } Index end = start; // end of data-containing part Index next = end; // after zeros we can skip. preserves next >= end if (!shouldSplit()) { next = end = data.size(); } while (next < data.size() && (next - end < OVERHEAD)) { if (data[end] != 0) { end++; next = end; // we can try to skip zeros from here } else { // end is on a zero, we are looking to skip if (data[next] != 0) { end = next; // we must extend the segment, including some zeros } else { next++; } } } if (end != start) { packed.emplace_back(Builder(*module).makeConst(Literal(int32_t(base + start))), &data[start], end - start); } start = next; } numRemaining--; } module->memory.segments.swap(packed); } }; Pass *createMemoryPackingPass() { return new MemoryPacking(); } } // namespace wasm