| ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. |
| |
| ;; Part of cast-and-recast.wast, but containing tuples. This is split out |
| ;; because we do not roundtrip tuple-containing code properly. We also use only |
| ;; one roundtrip because of the accumulation of tuple logic, which would |
| ;; otherwise make the output here very hard to read. |
| |
| ;; RUN: wasm-opt %s -all --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: (func $test-local-tuple-1 (type $3) (param $B (ref $B)) (param $x i32) (result anyref i32) |
| ;; CHECK-NEXT: (local $scratch (tuple (ref $B) i32)) |
| ;; CHECK-NEXT: (local $scratch_3 (ref $B)) |
| ;; CHECK-NEXT: (block $block (type $2) (result (ref $A) i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref $B)) |
| ;; CHECK-NEXT: (local.set $scratch_3 |
| ;; CHECK-NEXT: (tuple.extract 2 0 |
| ;; CHECK-NEXT: (local.tee $scratch |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (tuple.make 2 |
| ;; CHECK-NEXT: (local.get $B) |
| ;; CHECK-NEXT: (i32.const 3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (tuple.extract 2 1 |
| ;; CHECK-NEXT: (local.get $scratch) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test-local-tuple-1 (param $B (ref $B)) (param $x i32) (result anyref i32) |
| ;; A dropped tuple that contains a ref. As it is dropped, we do not need to |
| ;; do anything for this br_if. However, due to our general handling of |
| ;; tuples the code here will grow quite a bit due the roundtrip, but we can |
| ;; at least verify that there is no ref.cast added anywhere here. |
| (block $out (result (ref $A) i32) |
| (tuple.drop 2 |
| (br_if $out |
| (tuple.make 2 |
| (local.get $B) |
| (i32.const 3) |
| ) |
| (local.get $x) |
| ) |
| ) |
| (unreachable) |
| ) |
| ) |
| |
| ;; CHECK: (func $test-local-tuple-2 (type $7) (param $B (ref $B)) (param $x i32) (result i32 i32) |
| ;; CHECK-NEXT: (local $temp i32) |
| ;; CHECK-NEXT: (local $3 i32) |
| ;; CHECK-NEXT: (local $scratch (tuple i32 i32)) |
| ;; CHECK-NEXT: (local $scratch_5 i32) |
| ;; CHECK-NEXT: (block $block (type $4) (result i32 i32) |
| ;; CHECK-NEXT: (local.set $temp |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (local.set $scratch_5 |
| ;; CHECK-NEXT: (tuple.extract 2 0 |
| ;; CHECK-NEXT: (local.tee $scratch |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (tuple.make 2 |
| ;; CHECK-NEXT: (i32.const -1) |
| ;; CHECK-NEXT: (i32.const 3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (tuple.extract 2 1 |
| ;; CHECK-NEXT: (local.get $scratch) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_5) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test-local-tuple-2 (param $B (ref $B)) (param $x i32) (result i32 i32) |
| (local $temp (tuple i32 i32)) |
| ;; This tuple is not dropped, but it contains no references, so we do not |
| ;; need to do anything for the br_if, and we add no casts. |
| (block $out (result i32 i32) |
| (local.set $temp |
| (br_if $out |
| (tuple.make 2 |
| (i32.const -1) |
| (i32.const 3) |
| ) |
| (local.get $x) |
| ) |
| ) |
| (unreachable) |
| ) |
| ) |
| |
| ;; CHECK: (func $test-local-tuple-3 (type $3) (param $B (ref $B)) (param $x i32) (result anyref i32) |
| ;; CHECK-NEXT: (local $temp (ref $B)) |
| ;; CHECK-NEXT: (local $3 i32) |
| ;; CHECK-NEXT: (local $scratch (tuple (ref $B) i32)) |
| ;; CHECK-NEXT: (local $scratch_5 (ref $B)) |
| ;; CHECK-NEXT: (block $block (type $5) (result (ref $B) i32) |
| ;; CHECK-NEXT: (local.set $temp |
| ;; CHECK-NEXT: (block (result (ref $B)) |
| ;; CHECK-NEXT: (local.set $scratch_5 |
| ;; CHECK-NEXT: (tuple.extract 2 0 |
| ;; CHECK-NEXT: (local.tee $scratch |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (tuple.make 2 |
| ;; CHECK-NEXT: (local.get $B) |
| ;; CHECK-NEXT: (i32.const 3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (tuple.extract 2 1 |
| ;; CHECK-NEXT: (local.get $scratch) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_5) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test-local-tuple-3 (param $B (ref $B)) (param $x i32) (result anyref i32) |
| (local $temp (tuple (ref $B) i32)) |
| ;; This is not dropped and has a reference, but it has the right type, so no |
| ;; cast is needed. |
| (block $out (result (ref $B) i32) |
| (local.set $temp |
| (br_if $out |
| (tuple.make 2 |
| (local.get $B) |
| (i32.const 3) |
| ) |
| (local.get $x) |
| ) |
| ) |
| (unreachable) |
| ) |
| ) |
| |
| ;; CHECK: (func $test-local-tuple-4-bad (type $3) (param $B (ref $B)) (param $x i32) (result anyref i32) |
| ;; CHECK-NEXT: (local $temp (ref $B)) |
| ;; CHECK-NEXT: (local $3 (ref $B)) |
| ;; CHECK-NEXT: (local $4 i32) |
| ;; CHECK-NEXT: (local $5 i32) |
| ;; CHECK-NEXT: (local $6 i32) |
| ;; CHECK-NEXT: (local $scratch i32) |
| ;; CHECK-NEXT: (local $scratch_8 (ref $B)) |
| ;; CHECK-NEXT: (local $scratch_9 (tuple (ref $B) i32)) |
| ;; CHECK-NEXT: (local $scratch_10 (ref $B)) |
| ;; CHECK-NEXT: (local $scratch_11 (ref $B)) |
| ;; CHECK-NEXT: (block $block (type $2) (result (ref $A) i32) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (block (result (ref $B)) |
| ;; CHECK-NEXT: (local.set $scratch_8 |
| ;; CHECK-NEXT: (local.get $B) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $6 |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (local.set $scratch |
| ;; CHECK-NEXT: (i32.const 3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $5 |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_8) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref $B)) |
| ;; CHECK-NEXT: (local.set $scratch_10 |
| ;; CHECK-NEXT: (tuple.extract 2 0 |
| ;; CHECK-NEXT: (local.tee $scratch_9 |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (tuple.make 2 |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: (local.get $6) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $5) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (tuple.extract 2 1 |
| ;; CHECK-NEXT: (local.get $scratch_9) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $temp |
| ;; CHECK-NEXT: (block (result (ref $B)) |
| ;; CHECK-NEXT: (local.set $scratch_11 |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $4 |
| ;; CHECK-NEXT: (local.get $6) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_11) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test-local-tuple-4-bad (param $B (ref $B)) (param $x i32) (result anyref i32) |
| (local $temp (tuple (ref $B) i32)) |
| ;; As above, but none of the mitigating circumstances happens: we have a |
| ;; tuple with a reference that is refined compared to the break target. As a |
| ;; result we must fix this up, which we do by adding locals, saving the |
| ;; br_if's refined input to them, reloading those values for the br_if, then |
| ;; reloading them again afterwards. |
| ;; |
| ;; Comparing to $test-local-tuple-4, we end up with 6 more locals. |
| (block $out (result (ref $A) i32) |
| (local.set $temp |
| (br_if $out |
| (tuple.make 2 |
| (local.get $B) |
| (i32.const 3) |
| ) |
| (local.get $x) |
| ) |
| ) |
| (unreachable) |
| ) |
| ) |
| |
| ;; CHECK: (func $test-local-tuple-4-bad-dupes (type $8) (param $B (ref $B)) (param $x i32) (result i32 anyref i32) |
| ;; CHECK-NEXT: (local $temp (ref $B)) |
| ;; CHECK-NEXT: (local $3 (ref $B)) |
| ;; CHECK-NEXT: (local $4 (ref $B)) |
| ;; CHECK-NEXT: (local $5 i32) |
| ;; CHECK-NEXT: (local $scratch i32) |
| ;; CHECK-NEXT: (local $7 i32) |
| ;; CHECK-NEXT: (local $8 i32) |
| ;; CHECK-NEXT: (local $9 i32) |
| ;; CHECK-NEXT: (local $10 i32) |
| ;; CHECK-NEXT: (local $scratch_11 i32) |
| ;; CHECK-NEXT: (local $scratch_12 (ref $B)) |
| ;; CHECK-NEXT: (local $scratch_13 i32) |
| ;; CHECK-NEXT: (local $scratch_14 (tuple i32 (ref $B) i32)) |
| ;; CHECK-NEXT: (local $scratch_15 (ref $B)) |
| ;; CHECK-NEXT: (local $scratch_16 i32) |
| ;; CHECK-NEXT: (local $scratch_17 (ref $B)) |
| ;; CHECK-NEXT: (local $scratch_18 i32) |
| ;; CHECK-NEXT: (local $scratch_19 (ref $B)) |
| ;; CHECK-NEXT: (block $block (type $6) (result i32 (ref $A) i32) |
| ;; CHECK-NEXT: (local.set $10 |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (local.set $scratch_13 |
| ;; CHECK-NEXT: (i32.const -3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $4 |
| ;; CHECK-NEXT: (block (result (ref $B)) |
| ;; CHECK-NEXT: (local.set $scratch_12 |
| ;; CHECK-NEXT: (local.get $B) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $9 |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (local.set $scratch_11 |
| ;; CHECK-NEXT: (i32.const 3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $8 |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_11) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_12) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_13) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (local.set $scratch_16 |
| ;; CHECK-NEXT: (tuple.extract 3 0 |
| ;; CHECK-NEXT: (local.tee $scratch_14 |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (tuple.make 3 |
| ;; CHECK-NEXT: (local.get $10) |
| ;; CHECK-NEXT: (local.get $4) |
| ;; CHECK-NEXT: (local.get $9) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $8) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref $B)) |
| ;; CHECK-NEXT: (local.set $scratch_15 |
| ;; CHECK-NEXT: (tuple.extract 3 1 |
| ;; CHECK-NEXT: (local.get $scratch_14) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (tuple.extract 3 2 |
| ;; CHECK-NEXT: (local.get $scratch_14) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_15) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_16) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.tee $scratch |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (local.set $scratch_18 |
| ;; CHECK-NEXT: (local.get $10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (block (result (ref $B)) |
| ;; CHECK-NEXT: (local.set $scratch_17 |
| ;; CHECK-NEXT: (local.get $4) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $7 |
| ;; CHECK-NEXT: (local.get $9) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_17) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_18) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $temp |
| ;; CHECK-NEXT: (block (result (ref $B)) |
| ;; CHECK-NEXT: (local.set $scratch_19 |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $5 |
| ;; CHECK-NEXT: (local.get $7) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $scratch_19) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test-local-tuple-4-bad-dupes (param $B (ref $B)) (param $x i32) (result i32 anyref i32) |
| (local $temp (tuple (ref $B) i32)) |
| ;; As above, but now the tuple has multiple appearances of the same type in |
| ;; it, each of which needs its own scratch local. We can see in the output |
| ;; that the tuple.extracts use different locals for the first and last i32. |
| ;; For easier reading, here is the wami output of the binary: |
| ;; |
| ;; (func $func0 (param $var0 (ref $type1)) (param $var1 i32) (result i32) (result anyref) (result i32) |
| ;; (local $var2 (ref $type1)) |
| ;; (local $var3 (ref $type1)) |
| ;; (local $var4 (ref $type1)) |
| ;; (local $var5 i32) |
| ;; (local $var6 i32) |
| ;; (local $var7 i32) |
| ;; (local $var8 i32) |
| ;; (local $var9 i32) |
| ;; (local $var10 i32) |
| ;; block $label0 (result i32) (result (ref $type0)) (result i32) |
| ;; i32.const -3 |
| ;; local.get $var0 |
| ;; i32.const 3 |
| ;; local.get $var1 |
| ;; local.set $var8 ;; saves condition |
| ;; local.set $var9 ;; saves 3 |
| ;; local.set $var4 ;; saves ref |
| ;; local.set $var10 ;; saves -3 |
| ;; local.get $var10 ;; gets -3 |
| ;; local.get $var4 ;; gets ref |
| ;; local.get $var9 ;; gets 3 |
| ;; local.get $var8 ;; gets condition |
| ;; br_if $label0 |
| ;; drop |
| ;; drop |
| ;; drop |
| ;; local.get $var10 ;; gets -3 |
| ;; local.get $var4 ;; gets ref |
| ;; local.get $var9 ;; gets 3 |
| ;; local.set $var7 |
| ;; local.set $var3 |
| ;; local.tee $var6 |
| ;; drop |
| ;; local.get $var3 |
| ;; local.get $var7 |
| ;; local.set $var5 |
| ;; local.set $var2 |
| ;; unreachable |
| ;; end $label0 |
| ;; ) |
| ;; |
| (block $out (result i32 (ref $A) i32) |
| (local.set $temp |
| (br_if $out |
| (tuple.make 3 |
| (i32.const -3) ;; this was added |
| (local.get $B) |
| (i32.const 3) |
| ) |
| (local.get $x) |
| ) |
| ) |
| (unreachable) |
| ) |
| ) |
| ) |