From 0d4e55d401903bd5695e446bad6f5459b6b603c7 Mon Sep 17 00:00:00 2001 From: Goktug Gokdogan Date: Thu, 24 Sep 2026 23:15:23 -0700 Subject: [PATCH] Sink drop into arms of concrete if expressions in Vacuum pass In Binaryen's Vacuum pass, a drop of an if expression was previously only sunk into an arm if the other arm ended in an unreachable. When both arms were concrete, the drop was kept wrapping the if expression, forcing the if to retain a concrete block type (requiring multi-byte type index headers for GC types) and preventing dead-code elimination, vacuuming, and PruneDroppedReturns from optimizing dropped expressions within the branches. Furthermore, visitIf contained legacy logic that hoisted drops back outside of the if if both arms were dropped with identical types, which was detrimental for Wasm GC reference types (where block type indexes exceed 1 byte) and broke optimization cascades across passes. This change: 1. Sinks drops into both arms of concrete if-else expressions in visitDrop. 2. Removes the conflicting drop-hoisting logic in visitIf. 3. Adds unit tests and updates lit tests accordingly. --- src/passes/Vacuum.cpp | 26 +- test/lit/passes/dae-optimizing.wast | 24 +- .../inlining-optimizing_optimize-level=3.wast | 272 +++++++++--------- test/lit/passes/vacuum-intrinsics.wast | 32 ++- test/lit/passes/vacuum_all-features.wast | 176 +++++++++--- 5 files changed, 319 insertions(+), 211 deletions(-) diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp index ac645e77549..961f7eeefe0 100644 --- a/src/passes/Vacuum.cpp +++ b/src/passes/Vacuum.cpp @@ -331,17 +331,6 @@ struct Vacuum : public WalkerPass> { curr->condition = Builder(*getModule()).makeUnary(EqZInt32, curr->condition); BranchHints::flip(curr, getFunction()); - } else if (curr->ifTrue->is() && curr->ifFalse->is()) { - // instead of dropping both sides, drop the if, if they are the same - // type - auto* left = curr->ifTrue->cast()->value; - auto* right = curr->ifFalse->cast()->value; - if (left->type == right->type) { - curr->ifTrue = left; - curr->ifFalse = right; - curr->finalize(); - replaceCurrent(Builder(*getModule()).makeDrop(curr)); - } } } else { // This is an if without an else. If the body is empty, we do not need it. @@ -427,9 +416,11 @@ struct Vacuum : public WalkerPass> { } } } - // sink a drop into an arm of an if-else if the other arm ends in an - // unreachable, as it if is a branch, this can make that branch optimizable - // and more vacuuming possible + // Sink a drop into an arm of an if-else if the other arm ends in an + // unreachable, or sink drops into both arms if both arms are concrete. + // This allows the if expression to become void, eliminates block type + // overhead, and enables further vacuuming/dead-code elimination in each + // arm. auto* iff = curr->value->dynCast(); if (iff && iff->ifFalse && iff->type.isConcrete()) { // reuse the drop in both cases @@ -445,6 +436,13 @@ struct Vacuum : public WalkerPass> { iff->ifTrue = curr; iff->type = Type::none; replaceCurrent(iff); + } else if (iff->ifTrue->type.isConcrete() && + iff->ifFalse->type.isConcrete()) { + Builder builder(*getModule()); + iff->ifTrue = builder.dropIfConcretelyTyped(iff->ifTrue); + iff->ifFalse = builder.dropIfConcretelyTyped(iff->ifFalse); + iff->type = Type::none; + replaceCurrent(iff); } } } diff --git a/test/lit/passes/dae-optimizing.wast b/test/lit/passes/dae-optimizing.wast index 316c377586f..e7177a07138 100644 --- a/test/lit/passes/dae-optimizing.wast +++ b/test/lit/passes/dae-optimizing.wast @@ -8,7 +8,7 @@ (type $1 (func (param f64 f32 f32 f64 f32 i64 f64) (result i32))) ;; CHECK: (type $3 (func (result i32))) - ;; CHECK: (type $4 (func (result f32))) + ;; CHECK: (type $4 (func)) ;; CHECK: (type $2 (func (param f64 f32 f32 f64 f32 i32 i32 f64) (result i32))) (type $2 (func (param f64 f32 f32 f64 f32 i32 i32 f64) (result i32))) @@ -17,12 +17,12 @@ ;; CHECK: (func $0 (type $3) (result i32) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (if (result f32) - ;; CHECK-NEXT: (local.tee $0 - ;; CHECK-NEXT: (i32.const 33554432) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.tee $0 + ;; CHECK-NEXT: (i32.const 33554432) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (loop $label$2 (result f32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (global.get $global$0) @@ -44,9 +44,9 @@ ;; CHECK-NEXT: (f32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (call $1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (call $1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const -11) @@ -96,8 +96,8 @@ ) (i32.const -11) ) - ;; CHECK: (func $1 (type $4) (result f32) - ;; CHECK-NEXT: (f32.const 0) + ;; CHECK: (func $1 (type $4) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $1 (; 1 ;) (type $0) (param $0 f32) (result f32) (f32.const 0) diff --git a/test/lit/passes/inlining-optimizing_optimize-level=3.wast b/test/lit/passes/inlining-optimizing_optimize-level=3.wast index bf931609666..5707cf6dfba 100644 --- a/test/lit/passes/inlining-optimizing_optimize-level=3.wast +++ b/test/lit/passes/inlining-optimizing_optimize-level=3.wast @@ -418,188 +418,182 @@ ;; CHECK-NEXT: (local.get $6) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (if (result i32) - ;; CHECK-NEXT: (i32.lt_s - ;; CHECK-NEXT: (call $_printf_core - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 672) - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (local.get $7) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ge_s + ;; CHECK-NEXT: (call $_printf_core ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 672) + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (local.get $7) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.load offset=76 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.load offset=76 + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.le_s + ;; CHECK-NEXT: (i32.load8_s offset=74 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $3 - ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.store ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: (i32.const -33) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.le_s - ;; CHECK-NEXT: (i32.load8_s offset=74 + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (local.tee $5 + ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 48) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $_printf_core ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (local.get $3) - ;; CHECK-NEXT: (i32.const -33) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 672) + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (local.get $7) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (local.tee $5 - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 48) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (local.set $10 + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (local.tee $9 + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 44) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $_printf_core + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.get $9) + ;; CHECK-NEXT: (local.get $4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.tee $12 + ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 672) - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (local.get $7) + ;; CHECK-NEXT: (i32.const 28) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $4) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (local.set $10 - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (local.tee $9 - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 44) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.tee $11 + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 20) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.get $9) - ;; CHECK-NEXT: (local.get $4) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.tee $12 - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 28) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.get $5) + ;; CHECK-NEXT: (i32.const 80) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.tee $13 + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 16) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $4) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.tee $11 - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 20) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.add ;; CHECK-NEXT: (local.get $4) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.get $5) ;; CHECK-NEXT: (i32.const 80) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.tee $13 - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 16) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (local.get $4) - ;; CHECK-NEXT: (i32.const 80) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $2 - ;; CHECK-NEXT: (call $_printf_core - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 672) - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (local.get $7) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (call $_printf_core + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 672) + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (local.get $7) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $10) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call_indirect (type $FUNCSIG$iiii) - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.load offset=36 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 7) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $10) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call_indirect (type $FUNCSIG$iiii) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.load offset=36 + ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (i32.const 7) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (local.get $11) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.get $9) - ;; CHECK-NEXT: (local.get $10) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.get $5) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.get $13) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.get $12) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.load ;; CHECK-NEXT: (local.get $11) - ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.get $9) + ;; CHECK-NEXT: (local.get $10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.get $5) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.get $13) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.get $12) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.get $11) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.or - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (local.get $3) - ;; CHECK-NEXT: (i32.const 32) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.or + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: (i32.const 32) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/vacuum-intrinsics.wast b/test/lit/passes/vacuum-intrinsics.wast index 8f62ea36d46..6e12be47f98 100644 --- a/test/lit/passes/vacuum-intrinsics.wast +++ b/test/lit/passes/vacuum-intrinsics.wast @@ -145,21 +145,23 @@ ) ;; CHECK: (func $unused-fallthrough (type $0) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (if (result i32) - ;; CHECK-NEXT: (block $condition (result i32) - ;; CHECK-NEXT: (call $nop) - ;; CHECK-NEXT: (call $call.without.effects - ;; CHECK-NEXT: (ref.func $i) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (block $condition (result i32) + ;; CHECK-NEXT: (call $nop) + ;; CHECK-NEXT: (call $call.without.effects + ;; CHECK-NEXT: (ref.func $i) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block $ifTrue (result i32) ;; CHECK-NEXT: (call $nop) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block $ifFalse (result i32) ;; CHECK-NEXT: (call $nop) ;; CHECK-NEXT: (i32.const 0) @@ -194,15 +196,17 @@ ) ;; CHECK: (func $unused-fallthrough-bad-type (type $0) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (if (result (ref any)) - ;; CHECK-NEXT: (call $i) - ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (call $i) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $call.without.effects-ref ;; CHECK-NEXT: (ref.func $ref) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $call.without.effects-ref ;; CHECK-NEXT: (ref.func $ref) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/vacuum_all-features.wast b/test/lit/passes/vacuum_all-features.wast index bdc313e7b1b..788ac197eb6 100644 --- a/test/lit/passes/vacuum_all-features.wast +++ b/test/lit/passes/vacuum_all-features.wast @@ -604,13 +604,15 @@ ) ) ;; CHECK: (func $if2drops (type $3) (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (if (result i32) - ;; CHECK-NEXT: (call $if2drops) - ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (call $if2drops) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $if2drops) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $if2drops) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -1218,43 +1220,49 @@ ;; CHECK-NEXT: (return) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (if (result i32) - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (local.tee $0 - ;; CHECK-NEXT: (call $_deflate - ;; CHECK-NEXT: (local.get $3) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.tee $0 + ;; CHECK-NEXT: (call $_deflate + ;; CHECK-NEXT: (local.get $3) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (i32.load offset=20 - ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (i32.load offset=20 + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (call $_deflateEnd + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (call $_deflateEnd + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $global$1 ;; CHECK-NEXT: (local.get $3) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $global$1 - ;; CHECK-NEXT: (local.get $3) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $_deflateEnd + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $_deflateEnd + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $global$1 ;; CHECK-NEXT: (local.get $3) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $global$1 - ;; CHECK-NEXT: (local.get $3) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -1417,3 +1425,107 @@ ) ) ) + +(module + ;; Sinking drop into arms of concrete if + ;; CHECK: (type $struct (struct (field (mut i32)))) + (type $struct (struct (field (mut i32)))) + + ;; CHECK: (type $1 (func (result i32))) + + ;; CHECK: (type $2 (func (param i32 i32 i32))) + + ;; CHECK: (type $3 (func (param i32))) + + ;; CHECK: (type $4 (func (param (ref null $struct) i32) (result (ref null $struct)))) + + ;; CHECK: (type $5 (func (param i32 (ref null $struct)))) + + ;; CHECK: (func $bar (type $1) (result i32) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + (func $bar (result i32) + (i32.const 1) + ) + + ;; CHECK: (func $pure-arms (type $2) (param $c i32) (param $x i32) (param $y i32) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $pure-arms (param $c i32) (param $x i32) (param $y i32) + (drop + (if (result i32) + (local.get $c) + (then (local.get $x)) + (else (local.get $y)) + ) + ) + ) + + ;; CHECK: (func $effectful-arms (type $3) (param $c i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $c) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $bar) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $bar) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $effectful-arms (param $c i32) + (drop + (if (result i32) + (local.get $c) + (then (call $bar)) + (else (call $bar)) + ) + ) + ) + + ;; CHECK: (func $put (type $4) (param $this (ref null $struct)) (param $v i32) (result (ref null $struct)) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $this) + ;; CHECK-NEXT: (local.get $v) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $this) + ;; CHECK-NEXT: ) + (func $put (param $this (ref null $struct)) (param $v i32) (result (ref null $struct)) + (struct.set $struct 0 (local.get $this) (local.get $v)) + (local.get $this) + ) + + ;; CHECK: (func $gc-effectful-arms (type $5) (param $c i32) (param $b (ref null $struct)) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $c) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $put + ;; CHECK-NEXT: (local.get $b) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $put + ;; CHECK-NEXT: (local.get $b) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gc-effectful-arms (param $c i32) (param $b (ref null $struct)) + (drop + (if (result (ref null $struct)) + (local.get $c) + (then (call $put (local.get $b) (i32.const 1))) + (else (call $put (local.get $b) (i32.const 2))) + ) + ) + ) +)