@@ -50,32 +50,49 @@ struct LocalSubtyping : public WalkerPass<PostWalker<LocalSubtyping>> {
50
50
return ;
51
51
}
52
52
53
- auto numLocals = func->getNumLocals ();
53
+ // Compute the list of gets and sets for each local.
54
+ struct Scanner : public PostWalker <Scanner> {
55
+ // Which locals are relevant for us (we can ignore non-references).
56
+ std::vector<bool > relevant;
57
+
58
+ // The lists of gets and sets.
59
+ std::vector<std::vector<LocalSet*>> setsForLocal;
60
+ std::vector<std::vector<LocalGet*>> getsForLocal;
61
+
62
+ Scanner (Function* func) {
63
+ auto numLocals = func->getNumLocals ();
64
+ relevant.resize (numLocals);
65
+ setsForLocal.resize (numLocals);
66
+ getsForLocal.resize (numLocals);
67
+
68
+ for (Index i = 0 ; i < numLocals; i++) {
69
+ // TODO: Ignore params here? That may require changes below.
70
+ if (func->getLocalType (i).isRef ()) {
71
+ relevant[i] = true ;
72
+ }
73
+ }
54
74
55
- // Compute the local graph. We need to get the list of gets and sets for
56
- // each local, so that we can do the analysis. For non-nullable locals, we
57
- // also need to know when the default value of a local is used: if so then
58
- // we cannot change that type, as if we change the local type to
59
- // non-nullable then we'd be accessing the default, which is not allowed.
60
- //
61
- // TODO: Optimize this, as LocalGraph computes more than we need, and on
62
- // more locals than we need.
63
- LocalGraph localGraph (func, getModule ());
64
-
65
- // For each local index, compute all the the sets and gets.
66
- std::vector<std::vector<LocalSet*>> setsForLocal (numLocals);
67
- std::vector<std::vector<LocalGet*>> getsForLocal (numLocals);
68
-
69
- for (auto & [curr, _] : localGraph.locations ) {
70
- if (auto * set = curr->dynCast <LocalSet>()) {
71
- setsForLocal[set->index ].push_back (set);
72
- } else {
73
- auto * get = curr->cast <LocalGet>();
74
- getsForLocal[get->index ].push_back (get);
75
+ walk (func->body );
75
76
}
76
- }
77
77
78
- // Find which vars can be non-nullable.
78
+ void visitLocalGet (LocalGet* curr) {
79
+ if (relevant[curr->index ]) {
80
+ getsForLocal[curr->index ].push_back (curr);
81
+ }
82
+ }
83
+
84
+ void visitLocalSet (LocalSet* curr) {
85
+ if (relevant[curr->index ]) {
86
+ setsForLocal[curr->index ].push_back (curr);
87
+ }
88
+ }
89
+ } scanner (func);
90
+
91
+ auto & setsForLocal = scanner.setsForLocal ;
92
+ auto & getsForLocal = scanner.getsForLocal ;
93
+
94
+ // Find which vars can be non-nullable (if a null is written, or the default
95
+ // null is used, then a local cannot become non-nullable).
79
96
std::unordered_set<Index> cannotBeNonNullable;
80
97
81
98
// All gets must be dominated structurally by sets for the local to be non-
@@ -98,7 +115,8 @@ struct LocalSubtyping : public WalkerPass<PostWalker<LocalSubtyping>> {
98
115
// TODO: handle cycles of X -> Y -> X etc.
99
116
100
117
bool more;
101
- bool optimized = false ;
118
+
119
+ auto numLocals = func->getNumLocals ();
102
120
103
121
do {
104
122
more = false ;
@@ -148,7 +166,6 @@ struct LocalSubtyping : public WalkerPass<PostWalker<LocalSubtyping>> {
148
166
assert (Type::isSubType (newType, oldType));
149
167
func->vars [i - varBase] = newType;
150
168
more = true ;
151
- optimized = true ;
152
169
153
170
// Update gets and tees.
154
171
for (auto * get : getsForLocal[i]) {
@@ -166,50 +183,6 @@ struct LocalSubtyping : public WalkerPass<PostWalker<LocalSubtyping>> {
166
183
}
167
184
}
168
185
} while (more);
169
-
170
- // If we ever optimized, then we also need to do a final pass to update any
171
- // unreachable gets and tees. They are not seen or updated in the above
172
- // analysis, but must be fixed up for validation to work.
173
- if (optimized) {
174
- for (auto * get : FindAll<LocalGet>(func->body ).list ) {
175
- get->type = func->getLocalType (get->index );
176
- }
177
- for (auto * set : FindAll<LocalSet>(func->body ).list ) {
178
- auto newType = func->getLocalType (set->index );
179
- if (set->isTee ()) {
180
- set->type = newType;
181
- set->finalize ();
182
- }
183
-
184
- // If this set was not processed earlier - that is, if it is in
185
- // unreachable code - then it may have an incompatible type. That is,
186
- // If we saw a reachable set that writes type A, and this set writes
187
- // type B, we may have specialized the local type to A, but the value
188
- // of type B in this unreachable set is no longer valid to write to
189
- // that local. In such a case we must do additional work.
190
- if (!Type::isSubType (set->value ->type , newType)) {
191
- // The type is incompatible. To fix this, replace
192
- //
193
- // (set (bad-value))
194
- //
195
- // with
196
- //
197
- // (set (block
198
- // (drop (bad-value))
199
- // (unreachable)
200
- // ))
201
- //
202
- // (We cannot just ignore the bad value, as it may contain a break to
203
- // a target that is necessary for validation.)
204
- Builder builder (*getModule ());
205
- set->value = builder.makeSequence (builder.makeDrop (set->value ),
206
- builder.makeUnreachable ());
207
- }
208
- }
209
-
210
- // Also update their parents.
211
- ReFinalize ().walkFunctionInModule (func, getModule ());
212
- }
213
186
}
214
187
};
215
188
0 commit comments