@@ -2550,13 +2550,41 @@ struct OptimizeInstructions
25502550 // traps are allowed, then we cannot remove the potentially-trapping
25512551 // child, though.
25522552 bool notWeaker = Type::isSubType (curr->type , child->type );
2553- bool safe = !child->desc || getPassOptions ().trapsNeverHappen ;
2554- if (notWeaker && safe) {
2553+ auto & options = getPassOptions ();
2554+ auto canTrap = !options.trapsNeverHappen ;
2555+ bool safe = !child->desc || !canTrap;
2556+ bool canOptimize = notWeaker && safe;
2557+ if (canOptimize && curr->desc && canTrap) {
2558+ // There is another child here, which might trap, and we need to
2559+ // consider that in this situation:
2560+ //
2561+ // (outer.cast
2562+ // (inner.cast (inner.ref))
2563+ // (descriptor with effects)
2564+ // )
2565+ //
2566+ // =>
2567+ //
2568+ // (outer.cast
2569+ // (inner.ref) ;; inner cast was removed
2570+ // (descriptor with effects)
2571+ // )
2572+ //
2573+ // It is safe to remove the inner cast, as if it trapped, the outer one
2574+ // would still trap. But if there is a descriptor, then we are moving
2575+ // the trap across the descriptor, and shouldn't cross effects there.
2576+ EffectAnalyzer descEffects (options, *getModule (), curr->desc );
2577+ ShallowEffectAnalyzer movingEffects (options, *getModule (), curr->ref );
2578+ if (movingEffects.orderedBefore (descEffects)) {
2579+ canOptimize = false ;
2580+ }
2581+ }
2582+ if (canOptimize) {
25552583 if (child->desc ) {
25562584 // Reorder the child's reference past its dropped descriptor if
25572585 // necessary.
25582586 auto * block =
2559- ChildLocalizer (child, getFunction (), *getModule (), getPassOptions () )
2587+ ChildLocalizer (child, getFunction (), *getModule (), options )
25602588 .getChildrenReplacement ();
25612589 block->list .push_back (child->ref );
25622590 block->type = child->ref ->type ;
0 commit comments