Skip to content

Commit 26cf323

Browse files
committed
add a pass to optimize memory segments, and pack memory in asm2wasm
1 parent c8eccc4 commit 26cf323

7 files changed

Lines changed: 131 additions & 1 deletion

File tree

src/passes/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ SET(passes_SOURCES
77
ExtractFunction.cpp
88
Inlining.cpp
99
LegalizeJSInterface.cpp
10+
MemoryPacking.cpp
1011
MergeBlocks.cpp
1112
Metrics.cpp
1213
NameManager.cpp

src/passes/MemoryPacking.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2016 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <wasm.h>
18+
#include <pass.h>
19+
#include <wasm-builder.h>
20+
21+
namespace wasm {
22+
23+
// Adding segments adds overhead, this is a rough estimate
24+
const Index OVERHEAD = 8;
25+
26+
struct MemoryPacking : public Pass {
27+
void run(PassRunner* runner, Module* module) override {
28+
if (!module->memory.exists) return;
29+
std::vector<Memory::Segment> packed;
30+
for (auto& segment : module->memory.segments) {
31+
// skip final zeros
32+
while (segment.data.size() > 0 && segment.data.back() == 0) {
33+
segment.data.pop_back();
34+
}
35+
// we can only handle a constant offset for splitting
36+
if (auto* offset = segment.offset->dynCast<Const>()) {
37+
// Find runs of zeros, and split
38+
auto& data = segment.data;
39+
auto base = offset->value.geti32();
40+
Index start = 0;
41+
// create new segments
42+
while (start < data.size()) {
43+
// skip initial zeros
44+
while (start < data.size() && data[start] == 0) {
45+
start++;
46+
}
47+
Index end = start; // end of data-containing part
48+
Index next = end; // after zeros we can skip. preserves next >= end
49+
while (next < data.size() && (next - end < OVERHEAD)) {
50+
if (data[end] != 0) {
51+
end++;
52+
next = end; // we can try to skip zeros from here
53+
} else {
54+
// end is on a zero, we are looking to skip
55+
if (data[next] != 0) {
56+
end = next; // we must extend the segment, including some zeros
57+
} else {
58+
next++;
59+
}
60+
}
61+
}
62+
if (end != start) {
63+
packed.emplace_back(Builder(*module).makeConst(Literal(int32_t(base + start))), &data[start], end - start);
64+
}
65+
start = next;
66+
}
67+
} else {
68+
packed.push_back(segment);
69+
}
70+
}
71+
module->memory.segments.swap(packed);
72+
}
73+
};
74+
75+
Pass *createMemoryPackingPass() {
76+
return new MemoryPacking();
77+
}
78+
79+
} // namespace wasm
80+

src/passes/pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ void PassRegistry::registerPasses() {
7171
registerPass("extract-function", "leaves just one function (useful for debugging)", createExtractFunctionPass);
7272
registerPass("inlining", "inlines functions (currently only ones with a single use)", createInliningPass);
7373
registerPass("legalize-js-interface", "legalizes i64 types on the import/export boundary", createLegalizeJSInterfacePass);
74+
registerPass("memory-packing", "packs memory into separate segments, skipping zeros", createMemoryPackingPass);
7475
registerPass("merge-blocks", "merges blocks to their parents", createMergeBlocksPass);
7576
registerPass("metrics", "reports metrics", createMetricsPass);
7677
registerPass("nm", "name list", createNameListPass);
@@ -100,6 +101,7 @@ void PassRunner::addDefaultOptimizationPasses() {
100101
addDefaultFunctionOptimizationPasses();
101102
add("duplicate-function-elimination"); // optimizations show more functions as duplicate
102103
add("remove-unused-functions");
104+
add("memory-packing");
103105
}
104106

105107
void PassRunner::addDefaultFunctionOptimizationPasses() {
@@ -127,6 +129,7 @@ void PassRunner::addDefaultFunctionOptimizationPasses() {
127129
void PassRunner::addDefaultGlobalOptimizationPasses() {
128130
add("duplicate-function-elimination");
129131
add("remove-unused-functions");
132+
add("memory-packing");
130133
}
131134

132135
void PassRunner::run() {

src/passes/passes.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ Pass *createFullPrinterPass();
3232
Pass *createInliningPass();
3333
Pass *createLegalizeJSInterfacePass();
3434
Pass *createLowerIfElsePass();
35-
Pass *createMinifiedPrinterPass();
35+
Pass *createMemoryPackingPass();
3636
Pass *createMergeBlocksPass();
37+
Pass *createMinifiedPrinterPass();
3738
Pass *createMetricsPass();
3839
Pass *createNameListPass();
3940
Pass *createNameManagerPass();

src/tools/asm2wasm.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ int main(int argc, const char *argv[]) {
116116
init = Builder(wasm).makeConst(Literal(int32_t(atoi(memBase->second.c_str()))));
117117
}
118118
wasm.memory.segments.emplace_back(init, data);
119+
if (runOptimizationPasses) {
120+
PassRunner runner(&wasm);
121+
runner.add("memory-packing");
122+
runner.run();
123+
}
119124
}
120125

121126
if (options.debug) std::cerr << "printing..." << std::endl;

test/passes/memory-packing.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
(module
2+
(import "env" "memory" (memory $0 2048 2048))
3+
(import "env" "memoryBase" (global $memoryBase i32))
4+
(data (get_global $memoryBase) "waka this cannot be optimized\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00we don\'t know where it will go")
5+
(data (i32.const 1024) "waka this CAN be optimized")
6+
(data (i32.const 1107) "we DO know where it will go")
7+
(data (i32.const 2057) "zeros before")
8+
(data (i32.const 3000) "zeros after")
9+
(data (i32.const 4000) "zeros\00in\00the\00middle")
10+
(data (i32.const 4035) "nice skip here")
11+
(data (i32.const 4066) "another\00but no")
12+
)
13+
(module
14+
(import "env" "memory" (memory $0 2048 2048))
15+
(import "env" "memoryBase" (global $memoryBase i32))
16+
)
17+
(module
18+
(import "env" "memory" (memory $0 2048 2048))
19+
(import "env" "memoryBase" (global $memoryBase i32))
20+
)

test/passes/memory-packing.wast

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
(module
2+
(import "env" "memory" (memory $0 2048 2048))
3+
(import "env" "memoryBase" (global $memoryBase i32))
4+
(data (get_global $memoryBase) "waka this cannot be optimized\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00we don't know where it will go")
5+
(data (i32.const 1024) "waka this CAN be optimized\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00we DO know where it will go")
6+
(data (i32.const 2000) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00zeros before")
7+
(data (i32.const 3000) "zeros after\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
8+
(data (i32.const 4000) "zeros\00in\00the\00middle\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00nice skip here\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00another\00but no")
9+
)
10+
(module
11+
(import "env" "memory" (memory $0 2048 2048))
12+
(import "env" "memoryBase" (global $memoryBase i32))
13+
;; nothing
14+
)
15+
(module
16+
(import "env" "memory" (memory $0 2048 2048))
17+
(import "env" "memoryBase" (global $memoryBase i32))
18+
(data (i32.const 4066) "") ;; empty
19+
)
20+

0 commit comments

Comments
 (0)