Skip to content

Commit 167acc7

Browse files
authored
SimplifyGlobals: Apply known constant values in linear traces (#2340)
This optimizes stuff like (global.set $x (i32.const 123)) (global.get $x) into (global.set $x (i32.const 123)) (i32.const 123) This doesn't help much with LLVM output as it's rare to use globals (except for the stack pointer, and that's already well optimized), but it may help on general wasm. It can also help with Asyncify that does use globals extensively.
1 parent 6a9acea commit 167acc7

12 files changed

Lines changed: 550 additions & 67 deletions

src/passes/SimplifyGlobals.cpp

Lines changed: 73 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,21 @@
2222
// * If an immutable global is a copy of another, use the earlier one,
2323
// to allow removal of the copies later.
2424
// * Apply the constant values of immutable globals.
25+
// * Apply the constant values of previous global.sets, in a linear
26+
// execution trace.
2527
//
2628
// Some globals may not have uses after these changes, which we leave
2729
// to other passes to optimize.
2830
//
31+
// This pass has a "optimize" variant (similar to inlining and DAE)
32+
// that also runs general function optimizations where we managed to replace
33+
// a constant value. That is helpful as such a replacement often opens up
34+
// further optimization opportunities.
35+
//
2936

3037
#include <atomic>
3138

39+
#include "ir/effects.h"
3240
#include "ir/utils.h"
3341
#include "pass.h"
3442
#include "wasm-builder.h"
@@ -84,26 +92,73 @@ struct GlobalUseModifier : public WalkerPass<PostWalker<GlobalUseModifier>> {
8492
};
8593

8694
struct ConstantGlobalApplier
87-
: public WalkerPass<PostWalker<ConstantGlobalApplier>> {
95+
: public WalkerPass<
96+
LinearExecutionWalker<ConstantGlobalApplier,
97+
UnifiedExpressionVisitor<ConstantGlobalApplier>>> {
8898
bool isFunctionParallel() override { return true; }
8999

90-
ConstantGlobalApplier(NameSet* constantGlobals)
91-
: constantGlobals(constantGlobals) {}
100+
ConstantGlobalApplier(NameSet* constantGlobals, bool optimize)
101+
: constantGlobals(constantGlobals), optimize(optimize) {}
92102

93103
ConstantGlobalApplier* create() override {
94-
return new ConstantGlobalApplier(constantGlobals);
104+
return new ConstantGlobalApplier(constantGlobals, optimize);
95105
}
96106

97-
void visitGlobalGet(GlobalGet* curr) {
98-
if (constantGlobals->count(curr->name)) {
99-
auto* global = getModule()->getGlobal(curr->name);
100-
assert(global->init->is<Const>());
101-
replaceCurrent(ExpressionManipulator::copy(global->init, *getModule()));
107+
void visitExpression(Expression* curr) {
108+
if (auto* set = curr->dynCast<GlobalSet>()) {
109+
if (auto* c = set->value->dynCast<Const>()) {
110+
currConstantGlobals[set->name] = c->value;
111+
} else {
112+
currConstantGlobals.erase(set->name);
113+
}
114+
return;
115+
} else if (auto* get = curr->dynCast<GlobalGet>()) {
116+
// Check if the global is known to be constant all the time.
117+
if (constantGlobals->count(get->name)) {
118+
auto* global = getModule()->getGlobal(get->name);
119+
assert(global->init->is<Const>());
120+
replaceCurrent(ExpressionManipulator::copy(global->init, *getModule()));
121+
replaced = true;
122+
return;
123+
}
124+
// Check if the global has a known value in this linear trace.
125+
auto iter = currConstantGlobals.find(get->name);
126+
if (iter != currConstantGlobals.end()) {
127+
Builder builder(*getModule());
128+
replaceCurrent(builder.makeConst(iter->second));
129+
replaced = true;
130+
}
131+
return;
132+
}
133+
// Otherwise, invalidate if we need to.
134+
EffectAnalyzer effects(getPassOptions());
135+
effects.visit(curr);
136+
assert(effects.globalsWritten.empty()); // handled above
137+
if (effects.calls) {
138+
currConstantGlobals.clear();
139+
}
140+
}
141+
142+
static void doNoteNonLinear(ConstantGlobalApplier* self, Expression** currp) {
143+
self->currConstantGlobals.clear();
144+
}
145+
146+
void visitFunction(Function* curr) {
147+
if (replaced && optimize) {
148+
PassRunner runner(getModule(), getPassRunner()->options);
149+
runner.setIsNested(true);
150+
runner.addDefaultFunctionOptimizationPasses();
151+
runner.runOnFunction(curr);
102152
}
103153
}
104154

105155
private:
106156
NameSet* constantGlobals;
157+
bool optimize;
158+
bool replaced = false;
159+
160+
// The globals currently constant in the linear trace.
161+
std::map<Name, Literal> currConstantGlobals;
107162
};
108163

109164
} // anonymous namespace
@@ -113,6 +168,9 @@ struct SimplifyGlobals : public Pass {
113168
Module* module;
114169

115170
GlobalInfoMap map;
171+
bool optimize;
172+
173+
SimplifyGlobals(bool optimize = false) : optimize(optimize) {}
116174

117175
void run(PassRunner* runner_, Module* module_) override {
118176
runner = runner_;
@@ -214,12 +272,14 @@ struct SimplifyGlobals : public Pass {
214272
constantGlobals.insert(global->name);
215273
}
216274
}
217-
if (!constantGlobals.empty()) {
218-
ConstantGlobalApplier(&constantGlobals).run(runner, module);
219-
}
275+
ConstantGlobalApplier(&constantGlobals, optimize).run(runner, module);
220276
}
221277
};
222278

223-
Pass* createSimplifyGlobalsPass() { return new SimplifyGlobals(); }
279+
Pass* createSimplifyGlobalsPass() { return new SimplifyGlobals(false); }
280+
281+
Pass* createSimplifyGlobalsOptimizingPass() {
282+
return new SimplifyGlobals(true);
283+
}
224284

225285
} // namespace wasm

src/passes/pass.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ void PassRegistry::registerPasses() {
267267
registerPass("simplify-globals",
268268
"miscellaneous globals-related optimizations",
269269
createSimplifyGlobalsPass);
270+
registerPass("simplify-globals-optimizing",
271+
"miscellaneous globals-related optimizations, and optimizes "
272+
"where we replaced global.gets with constants",
273+
createSimplifyGlobalsOptimizingPass);
270274
registerPass("simplify-locals",
271275
"miscellaneous locals-related optimizations",
272276
createSimplifyLocalsPass);
@@ -416,7 +420,11 @@ void PassRunner::addDefaultGlobalOptimizationPostPasses() {
416420
// optimizations show more functions as duplicate
417421
add("duplicate-function-elimination");
418422
add("duplicate-import-elimination");
419-
add("simplify-globals");
423+
if (options.optimizeLevel >= 2 || options.shrinkLevel >= 2) {
424+
add("simplify-globals-optimizing");
425+
} else {
426+
add("simplify-globals");
427+
}
420428
add("remove-unused-module-elements");
421429
add("memory-packing");
422430
// 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
@@ -92,6 +92,7 @@ Pass* createRedundantSetEliminationPass();
9292
Pass* createSafeHeapPass();
9393
Pass* createSimplifyLocalsPass();
9494
Pass* createSimplifyGlobalsPass();
95+
Pass* createSimplifyGlobalsOptimizingPass();
9596
Pass* createSimplifyLocalsNoNestingPass();
9697
Pass* createSimplifyLocalsNoTeePass();
9798
Pass* createSimplifyLocalsNoStructurePass();

test/passes/O4_disable-bulk-memory.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,7 @@
12841284
(i32.const 104)
12851285
)
12861286
(global.set $global$1
1287-
(global.get $global$0)
1287+
(i32.const 104)
12881288
)
12891289
)
12901290
(func $null (; 12 ;) (; has Stack IR ;) (type $0)
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
(module
2+
(type $FUNCSIG$v (func))
3+
(import "env" "global-1" (global $g1 i32))
4+
(global $g2 i32 (global.get $g1))
5+
(func $foo (; 0 ;) (type $FUNCSIG$v)
6+
(drop
7+
(global.get $g1)
8+
)
9+
(drop
10+
(global.get $g1)
11+
)
12+
)
13+
)
14+
(module
15+
(type $FUNCSIG$v (func))
16+
(import "env" "global-1" (global $g1 i32))
17+
(global $g2 i32 (global.get $g1))
18+
(global $g3 i32 (global.get $g2))
19+
(global $g4 i32 (global.get $g3))
20+
(func $foo (; 0 ;) (type $FUNCSIG$v)
21+
(drop
22+
(global.get $g1)
23+
)
24+
(drop
25+
(global.get $g1)
26+
)
27+
(drop
28+
(global.get $g1)
29+
)
30+
(drop
31+
(global.get $g1)
32+
)
33+
)
34+
)
35+
(module
36+
(import "env" "global-1" (global $g1 (mut i32)))
37+
(global $g2 i32 (global.get $g1))
38+
)
39+
(module
40+
(type $FUNCSIG$v (func))
41+
(import "env" "global-1" (global $g1 i32))
42+
(global $g2 (mut i32) (global.get $g1))
43+
(func $foo (; 0 ;) (type $FUNCSIG$v)
44+
(global.set $g2
45+
(unreachable)
46+
)
47+
)
48+
)
49+
(module
50+
(import "env" "global-1" (global $g1 (mut i32)))
51+
(global $g2 (mut i32) (global.get $g1))
52+
(export "global-2" (global $g2))
53+
)
54+
(module
55+
(type $FUNCSIG$v (func))
56+
(global $g1 i32 (i32.const 1))
57+
(global $g2 i32 (i32.const 1))
58+
(global $g3 f64 (f64.const -3.4))
59+
(global $g4 f64 (f64.const -2.8))
60+
(global $g5 i32 (i32.const 2))
61+
(global $g6 i32 (i32.const 2))
62+
(global $g7 i32 (i32.const 3))
63+
(global $g8 i32 (i32.const 3))
64+
(global $g9 i32 (i32.const 4))
65+
(global $ga (mut i32) (i32.const 4))
66+
(global $gb (mut i32) (i32.const 5))
67+
(global $gc i32 (i32.const 5))
68+
(func $foo (; 0 ;) (type $FUNCSIG$v)
69+
(global.set $ga
70+
(i32.const 6)
71+
)
72+
(global.set $gb
73+
(i32.const 7)
74+
)
75+
)
76+
)
77+
(module
78+
(type $FUNCSIG$ii (func (param i32) (result i32)))
79+
(global $g1 (mut i32) (i32.const 1))
80+
(global $g2 (mut i32) (i32.const 1))
81+
(func $f (; 0 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
82+
(global.set $g1
83+
(i32.const 100)
84+
)
85+
(global.set $g2
86+
(local.get $0)
87+
)
88+
(if
89+
(local.get $0)
90+
(return
91+
(i32.const 0)
92+
)
93+
)
94+
(if
95+
(local.tee $0
96+
(i32.add
97+
(global.get $g1)
98+
(global.get $g2)
99+
)
100+
)
101+
(return
102+
(i32.const 1)
103+
)
104+
)
105+
(global.set $g1
106+
(i32.const 200)
107+
)
108+
(global.set $g2
109+
(local.get $0)
110+
)
111+
(i32.add
112+
(global.get $g2)
113+
(i32.const 200)
114+
)
115+
)
116+
)
117+
(module
118+
(type $FUNCSIG$ii (func (param i32) (result i32)))
119+
(global $g1 (mut i32) (i32.const 1))
120+
(global $g2 (mut i32) (i32.const 1))
121+
(func $f (; 0 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
122+
(global.set $g1
123+
(i32.const 100)
124+
)
125+
(global.set $g2
126+
(local.get $0)
127+
)
128+
(i32.add
129+
(global.get $g2)
130+
(i32.const 200)
131+
)
132+
)
133+
)
134+
(module
135+
(type $FUNCSIG$ii (func (param i32) (result i32)))
136+
(global $g1 (mut i32) (i32.const 1))
137+
(global $g2 (mut i32) (i32.const 1))
138+
(func $no (; 0 ;) (type $FUNCSIG$ii) (param $x i32) (result i32)
139+
(global.set $g1
140+
(i32.const 100)
141+
)
142+
(drop
143+
(call $no
144+
(i32.const 200)
145+
)
146+
)
147+
(global.get $g1)
148+
)
149+
(func $no2 (; 1 ;) (type $FUNCSIG$ii) (param $x i32) (result i32)
150+
(global.set $g1
151+
(i32.const 100)
152+
)
153+
(global.set $g1
154+
(local.get $x)
155+
)
156+
(global.get $g1)
157+
)
158+
(func $yes (; 2 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
159+
(global.set $g1
160+
(i32.const 100)
161+
)
162+
(global.set $g2
163+
(local.get $0)
164+
)
165+
(i32.const 100)
166+
)
167+
)

0 commit comments

Comments
 (0)