@@ -102,8 +102,8 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
102102 : propagatedInfos(propagatedInfos), subTypes(subTypes),
103103 rawNewInfos (rawNewInfos), refTest(refTest) {}
104104
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 ;
107107 if (type == Type::unreachable) {
108108 return std::nullopt ;
109109 }
@@ -124,38 +124,49 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
124124
125125 // Given information about a constant value, and the struct type and
126126 // 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) {
130130 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+ }
153153 }
154154 return value;
155155 }
156156
157157 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);
159170 if (!type) {
160171 return ;
161172 }
@@ -166,7 +177,7 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
166177 // Find the info for this field, and see if we can optimize. First, see if
167178 // there is any information for this heap type at all. If there isn't, it is
168179 // as if nothing was ever noted for that field.
169- PossibleConstantValues info = getInfo (heapType, curr-> index );
180+ PossibleConstantValues info = getInfo (heapType, index);
170181 if (!info.hasNoted ()) {
171182 // This field is never written at all. That means that we do not even
172183 // 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>> {
178189 // reference, so keep it around). We also do not need to care about
179190 // synchronization since trapping accesses do not synchronize with other
180191 // accesses.
181- replaceCurrent (builder. makeSequence (builder. makeDrop (curr-> ref ),
182- builder.makeUnreachable ()));
192+ replaceCurrent (
193+ builder. makeSequence (builder. makeDrop (ref), builder.makeUnreachable ()));
183194 changed = true ;
184195 return ;
185196 }
186197
187- if (curr-> order != MemoryOrder::Unordered) {
198+ if (order && * order != MemoryOrder::Unordered) {
188199 // The analysis we're basing the optimization on is not precise enough to
189200 // rule out the field being used to synchronize between a write of the
190201 // constant value and a subsequent read on another thread. This
@@ -199,7 +210,7 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
199210 // that is allowed.
200211 if (!info.isConstant ()) {
201212 if (refTest) {
202- optimizeUsingRefTest (curr);
213+ optimizeUsingRefTest (curr, ref, index );
203214 }
204215 return ;
205216 }
@@ -209,16 +220,16 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
209220 // constant value. (Leave it to further optimizations to get rid of the
210221 // ref.)
211222 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)));
214225 replacement->list .push_back (value);
215226 replacement->type = value->type ;
216227 replaceCurrent (replacement);
217228 changed = true ;
218229 }
219230
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 ;
222233 auto refHeapType = refType.getHeapType ();
223234
224235 // We only handle immutable fields in this function, as we will be looking
@@ -232,7 +243,8 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
232243 // reason the field must be immutable, so that it is valid to only look at
233244 // the struct.news. (A more complex flow analysis could do better here, but
234245 // 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) {
236248 return ;
237249 }
238250
@@ -276,7 +288,7 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
276288 return ;
277289 }
278290
279- auto value = iter->second [curr-> index ];
291+ auto value = iter->second [index];
280292 if (!value.isConstant ()) {
281293 // The value here is not constant, so give up entirely.
282294 fail = true ;
@@ -374,7 +386,7 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
374386 assert (testIndexTypes.size () == 1 );
375387 auto testType = testIndexTypes[0 ];
376388
377- auto * nnRef = builder.makeRefAs (RefAsNonNull, curr-> ref );
389+ auto * nnRef = builder.makeRefAs (RefAsNonNull, ref);
378390
379391 replaceCurrent (builder.makeSelect (
380392 builder.makeRefTest (nnRef, Type (testType, NonNullable)),
@@ -432,11 +444,6 @@ struct PCVScanner
432444 }
433445
434446 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-
440447 // Note copies, as they must be considered later. See the comment on the
441448 // propagation of values below.
442449 functionCopyInfos[getFunction ()][type][index] = true ;
0 commit comments