Skip to content

Commit a592b76

Browse files
authored
GlobalStructInference: Run the safe parts in open world (#8020)
We cannot infer a struct is only created in a global, but we can see global.gets and optimize them, at least, in a non-type-based way, even in open world. Fixes #8016
1 parent f0660c0 commit a592b76

File tree

5 files changed

+40
-8
lines changed

5 files changed

+40
-8
lines changed

scripts/fuzz_opt.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2484,7 +2484,6 @@ def write_commands(commands, filename):
24842484
("--abstract-type-refining",),
24852485
("--cfp",),
24862486
("--cfp-reftest",),
2487-
("--gsi",),
24882487
("--type-ssa",),
24892488
("--type-merging",),
24902489
("--unsubtyping",)}

src/passes/GlobalStructInference.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
* limitations under the License.
1515
*/
1616

17+
//
18+
// GlobalStructInference: Analyze struct usage globally, in particular, structs
19+
// created (perhaps only) in globals.
1720
//
1821
// Finds types which are only created in assignments to immutable globals. For
1922
// such types we can replace a struct.get with a global.get when there is a
@@ -47,7 +50,7 @@
4750
//
4851
// This also optimizes some related things - reads from structs created in
4952
// globals - that benefit from the infrastructure here (see unnesting, below),
50-
// even without this type-based approach.
53+
// even without this type-based approach, and even in open world.
5154
//
5255
// TODO: Only do the case with a select when shrinkLevel == 0?
5356
//
@@ -81,17 +84,24 @@ struct GlobalStructInference : public Pass {
8184
//
8285
// We will remove unoptimizable types from here, so in practice, if a type is
8386
// optimizable it will have an entry here, and not if not.
87+
//
88+
// This is filled in when in closed world. In open world, we cannot do such
89+
// type-based inference, and this remains empty.
8490
std::unordered_map<HeapType, std::vector<Name>> typeGlobals;
8591

8692
void run(Module* module) override {
8793
if (!module->features.hasGC()) {
8894
return;
8995
}
9096

91-
if (!getPassOptions().closedWorld) {
92-
Fatal() << "GSI requires --closed-world";
97+
if (getPassOptions().closedWorld) {
98+
analyzeClosedWorld(module);
9399
}
94100

101+
optimize(module);
102+
}
103+
104+
void analyzeClosedWorld(Module* module) {
95105
// First, find all the information we need. We need to know which struct
96106
// types are created in functions, because we will not be able to optimize
97107
// those.
@@ -213,7 +223,9 @@ struct GlobalStructInference : public Pass {
213223
for (auto& [type, globals] : typeGlobals) {
214224
std::sort(globals.begin(), globals.end());
215225
}
226+
}
216227

228+
void optimize(Module* module) {
217229
// We are looking for the case where we can pick between two values using a
218230
// single comparison. More than two values, or more than a single
219231
// comparison, lead to tradeoffs that may not be worth it.

src/passes/pass.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,9 @@ void PassRunner::addDefaultGlobalOptimizationPrePasses() {
769769
} else {
770770
addIfNoDWARFIssues("cfp");
771771
}
772-
addIfNoDWARFIssues("gsi");
772+
}
773+
addIfNoDWARFIssues("gsi");
774+
if (options.closedWorld) {
773775
addIfNoDWARFIssues("abstract-type-refining");
774776
addIfNoDWARFIssues("unsubtyping");
775777
}

test/lit/passes/gsi-nontype.wast

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
2-
;; RUN: foreach %s %t wasm-opt --gsi -all --closed-world -S -o - | filecheck %s
32

4-
;; Non-type-based optimizations in --gsi
3+
;; RUN: foreach %s %t wasm-opt --gsi -all -S -o - | filecheck %s
4+
;; RUN: foreach %s %t wasm-opt --gsi --closed-world -all -S -o - | filecheck %s
5+
6+
;; Non-type-based optimizations in --gsi, which work in open world too.
57

68
;; Create an immutable vtable in an immutable global, which we can optimize
79
;; with.
810
(module
911
;; CHECK: (type $vtable (struct (field funcref)))
1012
(type $vtable (struct funcref))
1113

14+
1215
;; CHECK: (type $1 (func))
1316

1417
;; CHECK: (import "a" "b" (global $imported funcref))
@@ -43,6 +46,7 @@
4346
;; CHECK: (type $vtable (struct (field funcref)))
4447
(type $vtable (struct funcref))
4548

49+
4650
;; CHECK: (type $1 (func))
4751

4852
;; CHECK: (import "a" "b" (global $imported funcref))
@@ -79,6 +83,7 @@
7983
;; CHECK: (type $vtable (struct (field funcref)))
8084
(type $vtable (struct funcref))
8185

86+
8287
;; CHECK: (type $1 (func))
8388

8489
;; CHECK: (import "a" "b" (global $imported funcref))
@@ -109,11 +114,13 @@
109114
;; CHECK: (type $table (struct (field anyref)))
110115
(type $table (struct anyref))
111116

117+
112118
;; CHECK: (type $1 (func))
113119

114120
;; CHECK: (import "a" "b" (global $imported funcref))
115121
(import "a" "b" (global $imported funcref))
116122

123+
117124
;; CHECK: (global $table.unnested.0 (ref (exact $table)) (struct.new_default $table))
118125

119126
;; CHECK: (global $table (ref $table) (struct.new $table
@@ -149,6 +156,7 @@
149156
(type $desc (sub (describes $struct (struct))))
150157
)
151158

159+
152160
;; CHECK: (type $2 (func))
153161

154162
;; CHECK: (import "a" "b" (global $imported (ref (exact $desc))))
@@ -183,6 +191,7 @@
183191
;; CHECK: (type $vtable (struct (field funcref)))
184192
(type $vtable (struct funcref))
185193

194+
186195
;; CHECK: (type $1 (func))
187196

188197
;; CHECK: (import "a" "b" (global $imported (ref $vtable)))
@@ -209,6 +218,7 @@
209218
;; CHECK: (type $A (struct (field i8)))
210219
(type $A (struct (field i8)))
211220

221+
212222
;; CHECK: (type $1 (func))
213223

214224
;; CHECK: (global $global (ref $A) (struct.new $A
@@ -246,6 +256,7 @@
246256
(type $A.desc (sub (describes $A (struct))))
247257
)
248258

259+
249260
;; CHECK: (type $2 (func (result (ref $A.desc))))
250261

251262
;; CHECK: (global $global (ref $A) (struct.new_default $A
@@ -279,6 +290,8 @@
279290
(type $inner (sub (struct (field (ref $func)))))
280291
)
281292

293+
294+
282295
;; CHECK: (type $3 (func (result anyref)))
283296

284297
;; CHECK: (global $global.unnested.0 (ref (exact $inner)) (struct.new $inner

test/unit/test_passes.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,20 @@ def test_O2(self):
3636
'signature-refining',
3737
'gto',
3838
'cfp',
39-
'gsi',
4039
]
4140
for pass_ in CLOSED_WORLD_PASSES:
4241
if closed_world:
4342
self.assertIn(pass_, passes)
4443
else:
4544
self.assertNotIn(pass_, passes)
4645

46+
# some passes run in open world too
47+
OPEN_WORLD_PASSES = [
48+
'gsi',
49+
]
50+
for pass_ in OPEN_WORLD_PASSES:
51+
self.assertIn(pass_, passes)
52+
4753
def test_O3_O1(self):
4854
# When we run something like -O3 -O1 we should run -O3 followed by -O1
4955
# (and not -O1 -O1, which would be the case if the last commandline

0 commit comments

Comments
 (0)