Skip to content

Commit b394fcc

Browse files
authored
Optimize mutable globals (#2066)
If a global is marked mutable but not assigned to, make it immutable. If an immutable global is a copy of another, use the original, so we can remove the duplicates. Fixes #2011
1 parent 01a4bfd commit b394fcc

21 files changed

Lines changed: 296 additions & 69 deletions

build-js.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ echo "building shared bitcode"
140140
$BINARYEN_SRC/passes/ReorderLocals.cpp \
141141
$BINARYEN_SRC/passes/ReReloop.cpp \
142142
$BINARYEN_SRC/passes/SafeHeap.cpp \
143+
$BINARYEN_SRC/passes/SimplifyGlobals.cpp \
143144
$BINARYEN_SRC/passes/SimplifyLocals.cpp \
144145
$BINARYEN_SRC/passes/Souperify.cpp \
145146
$BINARYEN_SRC/passes/SpillPointers.cpp \

src/passes/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ SET(passes_SOURCES
5858
ReorderFunctions.cpp
5959
TrapMode.cpp
6060
SafeHeap.cpp
61+
SimplifyGlobals.cpp
6162
SimplifyLocals.cpp
6263
Souperify.cpp
6364
SpillPointers.cpp

src/passes/SimplifyGlobals.cpp

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* Copyright 2019 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+
//
18+
// Simplify and optimize globals and their use.
19+
//
20+
// * Turns never-written and unwritable (not imported or exported)
21+
// globals immutable.
22+
// * If an immutable global is a copy of another, use the earlier one,
23+
// to allow removal of the copies later.
24+
//
25+
26+
#include <atomic>
27+
28+
#include "pass.h"
29+
#include "wasm.h"
30+
31+
namespace wasm {
32+
33+
namespace {
34+
35+
struct GlobalInfo {
36+
bool imported = false;
37+
bool exported = false;
38+
std::atomic<bool> written;
39+
};
40+
41+
using GlobalInfoMap = std::map<Name, GlobalInfo>;
42+
43+
struct GlobalUseScanner : public WalkerPass<PostWalker<GlobalUseScanner>> {
44+
bool isFunctionParallel() override { return true; }
45+
46+
GlobalUseScanner(GlobalInfoMap* infos) : infos(infos) {}
47+
48+
GlobalUseScanner* create() override { return new GlobalUseScanner(infos); }
49+
50+
void visitSetGlobal(SetGlobal* curr) { (*infos)[curr->name].written = true; }
51+
52+
private:
53+
GlobalInfoMap* infos;
54+
};
55+
56+
using NameNameMap = std::map<Name, Name>;
57+
58+
struct GlobalUseModifier : public WalkerPass<PostWalker<GlobalUseModifier>> {
59+
bool isFunctionParallel() override { return true; }
60+
61+
GlobalUseModifier(NameNameMap* copiedParentMap)
62+
: copiedParentMap(copiedParentMap) {}
63+
64+
GlobalUseModifier* create() override {
65+
return new GlobalUseModifier(copiedParentMap);
66+
}
67+
68+
void visitGetGlobal(GetGlobal* curr) {
69+
auto iter = copiedParentMap->find(curr->name);
70+
if (iter != copiedParentMap->end()) {
71+
curr->name = iter->second;
72+
}
73+
}
74+
75+
private:
76+
NameNameMap* copiedParentMap;
77+
};
78+
79+
} // anonymous namespace
80+
81+
struct SimplifyGlobals : public Pass {
82+
void run(PassRunner* runner, Module* module) override {
83+
// First, find out all the relevant info.
84+
GlobalInfoMap map;
85+
for (auto& global : module->globals) {
86+
auto& info = map[global->name];
87+
if (global->imported()) {
88+
info.imported = true;
89+
}
90+
}
91+
for (auto& ex : module->exports) {
92+
if (ex->kind == ExternalKind::Global) {
93+
map[ex->value].exported = true;
94+
}
95+
}
96+
{
97+
PassRunner subRunner(module, runner->options);
98+
subRunner.add<GlobalUseScanner>(&map);
99+
subRunner.run();
100+
}
101+
// We now know which are immutable in practice.
102+
for (auto& global : module->globals) {
103+
auto& info = map[global->name];
104+
if (global->mutable_ && !info.imported && !info.exported &&
105+
!info.written) {
106+
global->mutable_ = false;
107+
}
108+
}
109+
// Optimize uses of immutable globals, prefer the earlier import when
110+
// there is a copy.
111+
NameNameMap copiedParentMap;
112+
for (auto& global : module->globals) {
113+
auto child = global->name;
114+
if (!global->mutable_ && !global->imported()) {
115+
if (auto* get = global->init->dynCast<GetGlobal>()) {
116+
auto parent = get->name;
117+
if (!module->getGlobal(get->name)->mutable_) {
118+
copiedParentMap[child] = parent;
119+
}
120+
}
121+
}
122+
}
123+
if (!copiedParentMap.empty()) {
124+
// Go all the way back.
125+
for (auto& global : module->globals) {
126+
auto child = global->name;
127+
if (copiedParentMap.count(child)) {
128+
while (copiedParentMap.count(copiedParentMap[child])) {
129+
copiedParentMap[child] = copiedParentMap[copiedParentMap[child]];
130+
}
131+
}
132+
}
133+
// Apply to the gets.
134+
PassRunner subRunner(module, runner->options);
135+
subRunner.add<GlobalUseModifier>(&copiedParentMap);
136+
subRunner.run();
137+
}
138+
}
139+
};
140+
141+
Pass* createSimplifyGlobalsPass() { return new SimplifyGlobals(); }
142+
143+
} // namespace wasm

src/passes/pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ void PassRegistry::registerPasses() {
244244
registerPass("safe-heap",
245245
"instrument loads and stores to check for invalid behavior",
246246
createSafeHeapPass);
247+
registerPass("simplify-globals",
248+
"miscellaneous globals-related optimizations",
249+
createSimplifyGlobalsPass);
247250
registerPass("simplify-locals",
248251
"miscellaneous locals-related optimizations",
249252
createSimplifyLocalsPass);
@@ -392,6 +395,7 @@ void PassRunner::addDefaultGlobalOptimizationPostPasses() {
392395
}
393396
// optimizations show more functions as duplicate
394397
add("duplicate-function-elimination");
398+
add("simplify-globals");
395399
add("remove-unused-module-elements");
396400
add("memory-packing");
397401
// may allow more inlining/dae/etc., need --converge for that

src/passes/passes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ Pass* createReReloopPass();
8686
Pass* createRedundantSetEliminationPass();
8787
Pass* createSafeHeapPass();
8888
Pass* createSimplifyLocalsPass();
89+
Pass* createSimplifyGlobalsPass();
8990
Pass* createSimplifyLocalsNoNestingPass();
9091
Pass* createSimplifyLocalsNoTeePass();
9192
Pass* createSimplifyLocalsNoStructurePass();

src/wasm/wasm-s-parser.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,10 +1813,6 @@ void SExpressionWasmBuilder::parseExport(Element& s) {
18131813
ex->kind = ExternalKind::Table;
18141814
} else if (inner[0]->str() == GLOBAL) {
18151815
ex->kind = ExternalKind::Global;
1816-
if (wasm.getGlobalOrNull(ex->value) &&
1817-
wasm.getGlobal(ex->value)->mutable_) {
1818-
throw ParseException("cannot export a mutable global", s.line, s.col);
1819-
}
18201816
} else {
18211817
throw ParseException("invalid export");
18221818
}

test/dynamicLibrary.fromasm

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
(import "env" "memoryBase" (global $memoryBase$asm2wasm$import i32))
88
(import "env" "abortStackOverflow" (func $abortStackOverflow (param i32)))
99
(import "env" "_puts" (func $_puts (param i32) (result i32)))
10-
(global $memoryBase (mut i32) (global.get $memoryBase$asm2wasm$import))
1110
(global $STACKTOP (mut i32) (i32.const 0))
1211
(global $STACK_MAX (mut i32) (i32.const 0))
1312
(global $_global i32 (i32.const 5242912))
@@ -36,7 +35,7 @@
3635
)
3736
(drop
3837
(call $_puts
39-
(global.get $memoryBase)
38+
(global.get $memoryBase$asm2wasm$import)
4039
)
4140
)
4241
(global.set $STACKTOP
@@ -49,7 +48,7 @@
4948
(func $__post_instantiate (; 4 ;) (; has Stack IR ;)
5049
(global.set $STACKTOP
5150
(i32.add
52-
(global.get $memoryBase)
51+
(global.get $memoryBase$asm2wasm$import)
5352
(i32.const 32)
5453
)
5554
)
@@ -61,7 +60,7 @@
6160
)
6261
(call $__ZN3FooC2Ev
6362
(i32.add
64-
(global.get $memoryBase)
63+
(global.get $memoryBase$asm2wasm$import)
6564
(i32.const 5242912)
6665
)
6766
)

test/dynamicLibrary.fromasm.clamp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
(import "env" "memoryBase" (global $memoryBase$asm2wasm$import i32))
88
(import "env" "abortStackOverflow" (func $abortStackOverflow (param i32)))
99
(import "env" "_puts" (func $_puts (param i32) (result i32)))
10-
(global $memoryBase (mut i32) (global.get $memoryBase$asm2wasm$import))
1110
(global $STACKTOP (mut i32) (i32.const 0))
1211
(global $STACK_MAX (mut i32) (i32.const 0))
1312
(global $_global i32 (i32.const 5242912))
@@ -36,7 +35,7 @@
3635
)
3736
(drop
3837
(call $_puts
39-
(global.get $memoryBase)
38+
(global.get $memoryBase$asm2wasm$import)
4039
)
4140
)
4241
(global.set $STACKTOP
@@ -49,7 +48,7 @@
4948
(func $__post_instantiate (; 4 ;) (; has Stack IR ;)
5049
(global.set $STACKTOP
5150
(i32.add
52-
(global.get $memoryBase)
51+
(global.get $memoryBase$asm2wasm$import)
5352
(i32.const 32)
5453
)
5554
)
@@ -61,7 +60,7 @@
6160
)
6261
(call $__ZN3FooC2Ev
6362
(i32.add
64-
(global.get $memoryBase)
63+
(global.get $memoryBase$asm2wasm$import)
6564
(i32.const 5242912)
6665
)
6766
)

test/dynamicLibrary.fromasm.imprecise

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
(import "env" "memoryBase" (global $memoryBase$asm2wasm$import i32))
55
(import "env" "abortStackOverflow" (func $abortStackOverflow (param i32)))
66
(import "env" "_puts" (func $_puts (param i32) (result i32)))
7-
(global $memoryBase (mut i32) (global.get $memoryBase$asm2wasm$import))
87
(global $STACKTOP (mut i32) (i32.const 0))
98
(global $STACK_MAX (mut i32) (i32.const 0))
109
(global $_global i32 (i32.const 5242912))
@@ -33,7 +32,7 @@
3332
)
3433
(drop
3534
(call $_puts
36-
(global.get $memoryBase)
35+
(global.get $memoryBase$asm2wasm$import)
3736
)
3837
)
3938
(global.set $STACKTOP
@@ -46,7 +45,7 @@
4645
(func $__post_instantiate (; 4 ;) (; has Stack IR ;)
4746
(global.set $STACKTOP
4847
(i32.add
49-
(global.get $memoryBase)
48+
(global.get $memoryBase$asm2wasm$import)
5049
(i32.const 32)
5150
)
5251
)
@@ -58,7 +57,7 @@
5857
)
5958
(call $__ZN3FooC2Ev
6059
(i32.add
61-
(global.get $memoryBase)
60+
(global.get $memoryBase$asm2wasm$import)
6261
(i32.const 5242912)
6362
)
6463
)

0 commit comments

Comments
 (0)