@@ -102,8 +102,8 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
102
102
: propagatedInfos(propagatedInfos), subTypes(subTypes),
103
103
rawNewInfos (rawNewInfos), refTest(refTest) {}
104
104
105
- template <typename T> std::optional<HeapType> getRelevantHeapType (T* curr ) {
106
- auto type = curr-> ref ->type ;
105
+ template <typename T> std::optional<HeapType> getRelevantHeapType (T* ref ) {
106
+ auto type = ref->type ;
107
107
if (type == Type::unreachable) {
108
108
return std::nullopt ;
109
109
}
@@ -124,38 +124,49 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
124
124
125
125
// Given information about a constant value, and the struct type and
126
126
// StructGet/RMW/Cmpxchg that reads it, create an expression for that value.
127
- template < typename T>
128
- Expression*
129
- makeExpression ( const PossibleConstantValues& info, HeapType type, T * curr) {
127
+ Expression* makeExpression ( const PossibleConstantValues& info,
128
+ HeapType type,
129
+ Expression * curr) {
130
130
auto * value = info.makeExpression (*getModule ());
131
- auto field = GCTypeUtils::getField (type, curr->index );
132
- assert ( field);
133
- // Apply packing, if needed.
134
- if constexpr (std::is_same_v<T, StructGet>) {
135
- value =
136
- Bits::makePackedFieldGet ( value, *field, curr ->signed_ , *getModule ());
137
- }
138
- // Check if the value makes sense. The analysis below flows values around
139
- // without considering where they are placed, that is, when we see a parent
140
- // type can contain a value in a field then we assume a child may as well
141
- // (which in general it can, e.g., using a reference to the parent, we can
142
- // write that value to it, but the reference might actually point to a
143
- // child instance). If we tracked the types of fields then we might avoid
144
- // flowing values into places they cannot reside, like when a child field is
145
- // a subtype, and so we could ignore things not refined enough for it (GUFA
146
- // does a better job at this). For here, just check we do not break
147
- // validation, and if we do, then we've inferred the only possible value is
148
- // an impossible one, making the code unreachable.
149
- if (! Type::isSubType (value-> type , field-> type )) {
150
- Builder builder (* getModule ());
151
- value = builder.makeSequence (builder. makeDrop (value),
152
- builder. makeUnreachable ());
131
+ if ( auto * structGet = curr->dynCast <StructGet>()) {
132
+ auto field = GCTypeUtils::getField (type, structGet-> index );
133
+ assert (field);
134
+ // Apply packing, if needed.
135
+ value = Bits::makePackedFieldGet (
136
+ value, *field, structGet ->signed_ , *getModule ());
137
+ // Check if the value makes sense. The analysis below flows values around
138
+ // without considering where they are placed, that is, when we see a
139
+ // parent type can contain a value in a field then we assume a child may
140
+ // as well (which in general it can, e.g., using a reference to the
141
+ // parent, we can write that value to it, but the reference might actually
142
+ // point to a child instance). If we tracked the types of fields then we
143
+ // might avoid flowing values into places they cannot reside, like when a
144
+ // child field is a subtype, and so we could ignore things not refined
145
+ // enough for it (GUFA does a better job at this). For here, just check we
146
+ // do not break validation, and if we do, then we've inferred the only
147
+ // possible value is an impossible one, making the code unreachable.
148
+ if (! Type::isSubType (value-> type , field-> type )) {
149
+ Builder builder (* getModule ());
150
+ value = builder. makeSequence (builder. makeDrop (value),
151
+ builder.makeUnreachable ());
152
+ }
153
153
}
154
154
return value;
155
155
}
156
156
157
157
void visitStructGet (StructGet* curr) {
158
- auto type = getRelevantHeapType (curr);
158
+ optimizeRead (curr, curr->ref , curr->index , curr->order );
159
+ }
160
+
161
+ void visitRefGetDesc (RefGetDesc* curr) {
162
+ optimizeRead (curr, curr->ref , StructUtils::DescriptorIndex);
163
+ }
164
+
165
+ void optimizeRead (Expression* curr,
166
+ Expression* ref,
167
+ Index index,
168
+ std::optional<MemoryOrder> order = std::nullopt ) {
169
+ auto type = getRelevantHeapType (ref);
159
170
if (!type) {
160
171
return ;
161
172
}
@@ -166,7 +177,7 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
166
177
// Find the info for this field, and see if we can optimize. First, see if
167
178
// there is any information for this heap type at all. If there isn't, it is
168
179
// as if nothing was ever noted for that field.
169
- PossibleConstantValues info = getInfo (heapType, curr-> index );
180
+ PossibleConstantValues info = getInfo (heapType, index);
170
181
if (!info.hasNoted ()) {
171
182
// This field is never written at all. That means that we do not even
172
183
// construct any data of this type, and so it is a logic error to reach
@@ -178,13 +189,13 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
178
189
// reference, so keep it around). We also do not need to care about
179
190
// synchronization since trapping accesses do not synchronize with other
180
191
// accesses.
181
- replaceCurrent (builder. makeSequence (builder. makeDrop (curr-> ref ),
182
- builder.makeUnreachable ()));
192
+ replaceCurrent (
193
+ builder. makeSequence (builder. makeDrop (ref), builder.makeUnreachable ()));
183
194
changed = true ;
184
195
return ;
185
196
}
186
197
187
- if (curr-> order != MemoryOrder::Unordered) {
198
+ if (order && * order != MemoryOrder::Unordered) {
188
199
// The analysis we're basing the optimization on is not precise enough to
189
200
// rule out the field being used to synchronize between a write of the
190
201
// constant value and a subsequent read on another thread. This
@@ -199,7 +210,7 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
199
210
// that is allowed.
200
211
if (!info.isConstant ()) {
201
212
if (refTest) {
202
- optimizeUsingRefTest (curr);
213
+ optimizeUsingRefTest (curr, ref, index );
203
214
}
204
215
return ;
205
216
}
@@ -209,16 +220,16 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
209
220
// constant value. (Leave it to further optimizations to get rid of the
210
221
// ref.)
211
222
auto * value = makeExpression (info, heapType, curr);
212
- auto * replacement = builder. blockify (
213
- builder.makeDrop (builder.makeRefAs (RefAsNonNull, curr-> ref )));
223
+ auto * replacement =
224
+ builder.blockify (builder. makeDrop (builder.makeRefAs (RefAsNonNull, ref)));
214
225
replacement->list .push_back (value);
215
226
replacement->type = value->type ;
216
227
replaceCurrent (replacement);
217
228
changed = true ;
218
229
}
219
230
220
- void optimizeUsingRefTest (StructGet * curr) {
221
- auto refType = curr-> ref ->type ;
231
+ void optimizeUsingRefTest (Expression * curr, Expression* ref, Index index ) {
232
+ auto refType = ref->type ;
222
233
auto refHeapType = refType.getHeapType ();
223
234
224
235
// We only handle immutable fields in this function, as we will be looking
@@ -232,7 +243,8 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
232
243
// reason the field must be immutable, so that it is valid to only look at
233
244
// the struct.news. (A more complex flow analysis could do better here, but
234
245
// would be far beyond the scope of this pass.)
235
- if (GCTypeUtils::getField (refType, curr->index )->mutable_ == Mutable) {
246
+ if (index != StructUtils::DescriptorIndex &&
247
+ GCTypeUtils::getField (refType, index)->mutable_ == Mutable) {
236
248
return ;
237
249
}
238
250
@@ -276,7 +288,7 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
276
288
return ;
277
289
}
278
290
279
- auto value = iter->second [curr-> index ];
291
+ auto value = iter->second [index];
280
292
if (!value.isConstant ()) {
281
293
// The value here is not constant, so give up entirely.
282
294
fail = true ;
@@ -374,7 +386,7 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
374
386
assert (testIndexTypes.size () == 1 );
375
387
auto testType = testIndexTypes[0 ];
376
388
377
- auto * nnRef = builder.makeRefAs (RefAsNonNull, curr-> ref );
389
+ auto * nnRef = builder.makeRefAs (RefAsNonNull, ref);
378
390
379
391
replaceCurrent (builder.makeSelect (
380
392
builder.makeRefTest (nnRef, Type (testType, NonNullable)),
@@ -432,11 +444,6 @@ struct PCVScanner
432
444
}
433
445
434
446
void noteCopy (HeapType type, Index index, PossibleConstantValues& info) {
435
- if (index == DescriptorIndex) {
436
- // We cannot continue on below, where we index into the vector of values.
437
- return ;
438
- }
439
-
440
447
// Note copies, as they must be considered later. See the comment on the
441
448
// propagation of values below.
442
449
functionCopyInfos[getFunction ()][type][index] = true ;
0 commit comments