@@ -1283,7 +1283,7 @@ struct OptimizeInstructions
12831283 }
12841284
12851285 void visitCallRef (CallRef* curr) {
1286- skipNonNullCast (curr->target );
1286+ skipNonNullCast (curr->target , curr );
12871287 if (trapOnNull (curr, curr->target )) {
12881288 return ;
12891289 }
@@ -1473,10 +1473,51 @@ struct OptimizeInstructions
14731473 // See "notes on removing casts", above. However, in most cases removing a
14741474 // non-null cast is obviously safe to do, since we only remove one if another
14751475 // check will happen later.
1476- void skipNonNullCast (Expression*& input) {
1476+ //
1477+ // We also pass in the parent, because we need to be careful about ordering:
1478+ // if the parent has other children than |input| then we may not be able to
1479+ // remove the trap. For example,
1480+ //
1481+ // (struct.set
1482+ // (ref.as_non_null X)
1483+ // (call $foo)
1484+ // )
1485+ //
1486+ // If X is null we'd trap before the call to $foo. If we remove the
1487+ // ref.as_non_null then the struct.set will still trap, of course, but that
1488+ // will only happen *after* the call, which is wrong.
1489+ void skipNonNullCast (Expression*& input, Expression* parent) {
1490+ // Check the other children for the ordering problem only if we find a
1491+ // possible optimization, to avoid wasted work.
1492+ bool checkedSiblings = false ;
1493+ auto & options = getPassOptions ();
14771494 while (1 ) {
14781495 if (auto * as = input->dynCast <RefAs>()) {
14791496 if (as->op == RefAsNonNull) {
1497+ // The problem with effect ordering that is described above is not an
1498+ // issue if traps are assumed to never happen anyhow.
1499+ if (!checkedSiblings && !options.trapsNeverHappen ) {
1500+ // We need to see if a child with side effects exists after |input|.
1501+ // If there is such a child, it is a problem as mentioned above (it
1502+ // is fine for such a child to appear *before* |input|, as then we
1503+ // wouldn't be reordering effects).
1504+ bool seenInput = false ;
1505+ for (auto * child : ChildIterator (parent)) {
1506+ if (child == input) {
1507+ seenInput = true ;
1508+ } else if (seenInput) {
1509+ // TODO We could ignore trap effects here (since traps are ok to
1510+ // reorder) and also local effects (since a change to a var
1511+ // would not be noticeable, unlike say a global).
1512+ if (EffectAnalyzer (options, *getModule (), child)
1513+ .hasSideEffects ()) {
1514+ return ;
1515+ }
1516+ }
1517+ }
1518+ // If we got here, we've checked the siblings and found no problem.
1519+ checkedSiblings = true ;
1520+ }
14801521 input = as->value ;
14811522 continue ;
14821523 }
@@ -1717,12 +1758,12 @@ struct OptimizeInstructions
17171758 }
17181759
17191760 void visitStructGet (StructGet* curr) {
1720- skipNonNullCast (curr->ref );
1761+ skipNonNullCast (curr->ref , curr );
17211762 trapOnNull (curr, curr->ref );
17221763 }
17231764
17241765 void visitStructSet (StructSet* curr) {
1725- skipNonNullCast (curr->ref );
1766+ skipNonNullCast (curr->ref , curr );
17261767 if (trapOnNull (curr, curr->ref )) {
17271768 return ;
17281769 }
@@ -1878,12 +1919,12 @@ struct OptimizeInstructions
18781919 }
18791920
18801921 void visitArrayGet (ArrayGet* curr) {
1881- skipNonNullCast (curr->ref );
1922+ skipNonNullCast (curr->ref , curr );
18821923 trapOnNull (curr, curr->ref );
18831924 }
18841925
18851926 void visitArraySet (ArraySet* curr) {
1886- skipNonNullCast (curr->ref );
1927+ skipNonNullCast (curr->ref , curr );
18871928 if (trapOnNull (curr, curr->ref )) {
18881929 return ;
18891930 }
@@ -1895,13 +1936,13 @@ struct OptimizeInstructions
18951936 }
18961937
18971938 void visitArrayLen (ArrayLen* curr) {
1898- skipNonNullCast (curr->ref );
1939+ skipNonNullCast (curr->ref , curr );
18991940 trapOnNull (curr, curr->ref );
19001941 }
19011942
19021943 void visitArrayCopy (ArrayCopy* curr) {
1903- skipNonNullCast (curr->destRef );
1904- skipNonNullCast (curr->srcRef );
1944+ skipNonNullCast (curr->destRef , curr );
1945+ skipNonNullCast (curr->srcRef , curr );
19051946 trapOnNull (curr, curr->destRef ) || trapOnNull (curr, curr->srcRef );
19061947 }
19071948
@@ -2155,7 +2196,7 @@ struct OptimizeInstructions
21552196 }
21562197
21572198 assert (curr->op == RefAsNonNull);
2158- skipNonNullCast (curr->value );
2199+ skipNonNullCast (curr->value , curr );
21592200 if (!curr->value ->type .isNullable ()) {
21602201 replaceCurrent (curr->value );
21612202 return ;
0 commit comments