;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. ;; Test that our hack for br_if output types does not cause the binary to grow ;; linearly with each roundtrip (note the three roundtrips here). When we emit ;; a br_if whose output type is not refined enough (Binaryen IR uses the value's ;; type; wasm uses the target's) then we add a cast. ;; RUN: wasm-opt %s -all --roundtrip --roundtrip --roundtrip -S -o - | filecheck %s (module (rec ;; CHECK: (rec ;; CHECK-NEXT: (type $A (sub (struct))) (type $A (sub (struct))) ;; CHECK: (type $B (sub $A (struct))) (type $B (sub $A (struct))) ;; CHECK: (type $C (sub $B (struct))) (type $C (sub $B (struct))) ) ;; CHECK: (func $test (type $3) (param $B (ref $B)) (param $x i32) (result anyref) ;; CHECK-NEXT: (block $block (result (ref $A)) ;; CHECK-NEXT: (ref.cast (ref $B) ;; CHECK-NEXT: (br_if $block ;; CHECK-NEXT: (local.get $B) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test (param $B (ref $B)) (param $x i32) (result anyref) (block $out (result (ref $A)) ;; The br_if's value is of type $B which is more precise than the block's ;; type, $A, so we emit a cast here, but only one despite the three ;; roundtrips. (br_if $out (local.get $B) (local.get $x) ) ) ) ;; CHECK: (func $test-cast (type $3) (param $B (ref $B)) (param $x i32) (result anyref) ;; CHECK-NEXT: (block $block (result (ref $A)) ;; CHECK-NEXT: (ref.cast (ref $B) ;; CHECK-NEXT: (br_if $block ;; CHECK-NEXT: (local.get $B) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test-cast (param $B (ref $B)) (param $x i32) (result anyref) ;; This is the result of a single roundtrip: there is a cast. We should not ;; modify this function at all in additional roundtrips. (block $out (result (ref $A)) (ref.cast (ref $B) (br_if $out (local.get $B) (local.get $x) ) ) ) ) ;; CHECK: (func $test-cast-more (type $3) (param $B (ref $B)) (param $x i32) (result anyref) ;; CHECK-NEXT: (block $block (result (ref $A)) ;; CHECK-NEXT: (ref.cast (ref $C) ;; CHECK-NEXT: (br_if $block ;; CHECK-NEXT: (local.get $B) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test-cast-more (param $B (ref $B)) (param $x i32) (result anyref) ;; As above but the cast is more refined. Again, we do not need an ;; additional cast. (block $out (result (ref $A)) (ref.cast (ref $C) ;; this changed (br_if $out (local.get $B) (local.get $x) ) ) ) ) ;; CHECK: (func $test-cast-less (type $3) (param $B (ref $B)) (param $x i32) (result anyref) ;; CHECK-NEXT: (block $block (result (ref $A)) ;; CHECK-NEXT: (ref.cast (ref $B) ;; CHECK-NEXT: (br_if $block ;; CHECK-NEXT: (local.get $B) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test-cast-less (param $B (ref $B)) (param $x i32) (result anyref) ;; As above but the cast is less refined. As a result we'd add a cast to $B ;; (but we refine casts automatically in finalize(), so this cast becomes a ;; cast to $B anyhow, and as a result we have only one cast here). (block $out (result (ref $A)) (ref.cast (ref $A) ;; this changed (br_if $out (local.get $B) (local.get $x) ) ) ) ) ;; CHECK: (func $test-local (type $3) (param $B (ref $B)) (param $x i32) (result anyref) ;; CHECK-NEXT: (local $temp (ref $B)) ;; CHECK-NEXT: (block $block (result (ref $A)) ;; CHECK-NEXT: (local.set $temp ;; CHECK-NEXT: (ref.cast (ref $B) ;; CHECK-NEXT: (br_if $block ;; CHECK-NEXT: (local.get $B) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test-local (param $B (ref $B)) (param $x i32) (result anyref) (local $temp (ref $B)) ;; As above, but with local.set that receives the br_if's value, verifying ;; it is refined. We emit a cast here. (block $out (result (ref $A)) (local.set $temp (br_if $out (local.get $B) (local.get $x) ) ) (unreachable) ) ) ;; CHECK: (func $test-drop (type $3) (param $B (ref $B)) (param $x i32) (result anyref) ;; CHECK-NEXT: (block $block (result (ref $A)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (br_if $block ;; CHECK-NEXT: (local.get $B) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test-drop (param $B (ref $B)) (param $x i32) (result anyref) ;; As above, but with a drop of the br_if value. We do not emit a cast here. (block $out (result (ref $A)) (drop (br_if $out (local.get $B) (local.get $x) ) ) (unreachable) ) ) ;; CHECK: (func $test-same (type $4) (param $A (ref $A)) (param $x i32) (result anyref) ;; CHECK-NEXT: (block $block (result (ref $A)) ;; CHECK-NEXT: (br_if $block ;; CHECK-NEXT: (local.get $A) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test-same (param $A (ref $A)) (param $x i32) (result anyref) ;; As above, but now we use $A everywhere, which means there is no ;; difference between the type in Binaryen IR and wasm, so we do not need ;; to emit any extra cast here. (block $out (result (ref $A)) (br_if $out (local.get $A) (local.get $x) ) ) ) )