diff options
-rw-r--r-- | test/lit/passes/gufa-refs.wast | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/test/lit/passes/gufa-refs.wast b/test/lit/passes/gufa-refs.wast index 9e17a3a69..a1ff92522 100644 --- a/test/lit/passes/gufa-refs.wast +++ b/test/lit/passes/gufa-refs.wast @@ -4564,3 +4564,634 @@ ) ) ) + +;; Limited cone reads. +(module + ;; CHECK: (type $A (struct_subtype (field (mut i32)) data)) + (type $A (struct_subtype (field (mut i32)) data)) + ;; CHECK: (type $B (struct_subtype (field (mut i32)) $A)) + (type $B (struct_subtype (field (mut i32)) $A)) + ;; CHECK: (type $C (struct_subtype (field (mut i32)) $B)) + (type $C (struct_subtype (field (mut i32)) $B)) + + + + ;; CHECK: (type $i32_=>_none (func_subtype (param i32) func)) + + ;; CHECK: (export "reads" (func $reads)) + + ;; CHECK: (func $reads (type $i32_=>_none) (param $x i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local $B (ref $B)) + ;; CHECK-NEXT: (local $C (ref $C)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $B + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $C + ;; CHECK-NEXT: (struct.new $C + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (select (result (ref $A)) + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (select (result (ref $A)) + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $reads (export "reads") (param $x i32) + (local $A (ref $A)) + (local $B (ref $B)) + (local $C (ref $C)) + ;; B and C agree on their value. + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (local.set $B + (struct.new $B + (i32.const 20) + ) + ) + (local.set $C + (struct.new $C + (i32.const 20) + ) + ) + ;; We can optimize the last of these, which mixes B and C, into 20. + (drop + (struct.get $A 0 + (select + (local.get $A) + (local.get $B) + (local.get $x) + ) + ) + ) + (drop + (struct.get $A 0 + (select + (local.get $A) + (local.get $C) + (local.get $x) + ) + ) + ) + (drop + (struct.get $A 0 + (select + (local.get $B) + (local.get $C) + (local.get $x) + ) + ) + ) + ) +) + +;; As above, but now A and B agree on the value and not B and C. +(module + ;; CHECK: (type $A (struct_subtype (field (mut i32)) data)) + (type $A (struct_subtype (field (mut i32)) data)) + ;; CHECK: (type $B (struct_subtype (field (mut i32)) $A)) + (type $B (struct_subtype (field (mut i32)) $A)) + ;; CHECK: (type $C (struct_subtype (field (mut i32)) $B)) + (type $C (struct_subtype (field (mut i32)) $B)) + + ;; CHECK: (type $i32_=>_none (func_subtype (param i32) func)) + + ;; CHECK: (export "reads" (func $reads)) + + ;; CHECK: (func $reads (type $i32_=>_none) (param $x i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local $B (ref $B)) + ;; CHECK-NEXT: (local $C (ref $C)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $B + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $C + ;; CHECK-NEXT: (struct.new $C + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (select (result (ref $A)) + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (select (result (ref $A)) + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (select (result (ref $B)) + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $reads (export "reads") (param $x i32) + (local $A (ref $A)) + (local $B (ref $B)) + (local $C (ref $C)) + ;; A and B agree on their value. + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (local.set $B + (struct.new $B + (i32.const 10) + ) + ) + (local.set $C + (struct.new $C + (i32.const 20) + ) + ) + ;; We can optimize the first of these, which mixes A and B, into 10. This + ;; will require more cone opts, though TODO + (drop + (struct.get $A 0 + (select + (local.get $A) + (local.get $B) + (local.get $x) + ) + ) + ) + (drop + (struct.get $A 0 + (select + (local.get $A) + (local.get $C) + (local.get $x) + ) + ) + ) + (drop + (struct.get $A 0 + (select + (local.get $B) + (local.get $C) + (local.get $x) + ) + ) + ) + ) +) + +;; As above but now A has two subtypes, instead of a chain A->B->C +(module + ;; CHECK: (type $A (struct_subtype (field (mut i32)) data)) + (type $A (struct_subtype (field (mut i32)) data)) + ;; CHECK: (type $B (struct_subtype (field (mut i32)) $A)) + (type $B (struct_subtype (field (mut i32)) $A)) + ;; CHECK: (type $C (struct_subtype (field (mut i32)) $A)) + (type $C (struct_subtype (field (mut i32)) $A)) ;; This line changed. + + ;; CHECK: (type $i32_=>_none (func_subtype (param i32) func)) + + ;; CHECK: (export "reads" (func $reads)) + + ;; CHECK: (func $reads (type $i32_=>_none) (param $x i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local $B (ref $B)) + ;; CHECK-NEXT: (local $C (ref $C)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $B + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $C + ;; CHECK-NEXT: (struct.new $C + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (select (result (ref $A)) + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (select (result (ref $A)) + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (select (result (ref $A)) + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $reads (export "reads") (param $x i32) + (local $A (ref $A)) + (local $B (ref $B)) + (local $C (ref $C)) + ;; A and B agree on their value. + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (local.set $B + (struct.new $B + (i32.const 10) + ) + ) + (local.set $C + (struct.new $C + (i32.const 20) + ) + ) + ;; We cannot optimize any of these. The first is optimizable in theory, + ;; since A and B agree on the value, but we end up with a cone on A of depth + ;; 1, and that includes B and C. To optimize this we'd need a sum type. + (drop + (struct.get $A 0 + (select + (local.get $A) + (local.get $B) + (local.get $x) + ) + ) + ) + (drop + (struct.get $A 0 + (select + (local.get $A) + (local.get $C) + (local.get $x) + ) + ) + ) + (drop + (struct.get $A 0 + (select + (local.get $B) + (local.get $C) + (local.get $x) + ) + ) + ) + ) +) + +;; Cone writes. +(module + ;; CHECK: (type $A (struct_subtype (field (mut i32)) data)) + (type $A (struct_subtype (field (mut i32)) data)) + ;; CHECK: (type $B (struct_subtype (field (mut i32)) $A)) + (type $B (struct_subtype (field (mut i32)) $A)) + ;; CHECK: (type $C (struct_subtype (field (mut i32)) $B)) + (type $C (struct_subtype (field (mut i32)) $B)) + + ;; CHECK: (type $i32_=>_none (func_subtype (param i32) func)) + + ;; CHECK: (export "write" (func $write)) + + ;; CHECK: (func $write (type $i32_=>_none) (param $x i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local $B (ref $B)) + ;; CHECK-NEXT: (local $C (ref $C)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $B + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $C + ;; CHECK-NEXT: (struct.new $C + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (select (result (ref $A)) + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $write (export "write") (param $x i32) + (local $A (ref $A)) + (local $B (ref $B)) + (local $C (ref $C)) + ;; A and B agree on their value. + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (local.set $B + (struct.new $B + (i32.const 10) + ) + ) + (local.set $C + (struct.new $C + (i32.const 20) + ) + ) + ;; Do a cone write. This writes the same value as they already have. + (struct.set $A 0 + (select + (local.get $A) + (local.get $B) + (local.get $x) + ) + (i32.const 10) + ) + ;; Read from all the locals. We can optimize them all, to 10, 10, 20. The + ;; last requires more cone opts, however. TODO + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + (drop + (struct.get $C 0 + (local.get $C) + ) + ) + ) +) + +;; As above, but write a different value. +(module + ;; CHECK: (type $A (struct_subtype (field (mut i32)) data)) + (type $A (struct_subtype (field (mut i32)) data)) + ;; CHECK: (type $B (struct_subtype (field (mut i32)) $A)) + (type $B (struct_subtype (field (mut i32)) $A)) + ;; CHECK: (type $C (struct_subtype (field (mut i32)) $B)) + (type $C (struct_subtype (field (mut i32)) $B)) + + ;; CHECK: (type $i32_=>_none (func_subtype (param i32) func)) + + ;; CHECK: (export "write" (func $write)) + + ;; CHECK: (func $write (type $i32_=>_none) (param $x i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local $B (ref $B)) + ;; CHECK-NEXT: (local $C (ref $C)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $B + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $C + ;; CHECK-NEXT: (struct.new $C + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (select (result (ref $B)) + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $write (export "write") (param $x i32) + (local $A (ref $A)) + (local $B (ref $B)) + (local $C (ref $C)) + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (local.set $B + (struct.new $B + (i32.const 10) + ) + ) + (local.set $C + (struct.new $C + (i32.const 20) + ) + ) + ;; Do a different cone write from before: now we write to B and C. This + ;; means C can have 10 or 20, and so we don't optimize it down below. + (struct.set $A 0 + (select + (local.get $B) + (local.get $C) + (local.get $x) + ) + (i32.const 10) + ) + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + (drop + (struct.get $C 0 + (local.get $C) + ) + ) + ) +) + +;; As above, but write a different cone. +(module + ;; CHECK: (type $A (struct_subtype (field (mut i32)) data)) + (type $A (struct_subtype (field (mut i32)) data)) + ;; CHECK: (type $B (struct_subtype (field (mut i32)) $A)) + (type $B (struct_subtype (field (mut i32)) $A)) + ;; CHECK: (type $C (struct_subtype (field (mut i32)) $B)) + (type $C (struct_subtype (field (mut i32)) $B)) + + ;; CHECK: (type $i32_=>_none (func_subtype (param i32) func)) + + ;; CHECK: (export "write" (func $write)) + + ;; CHECK: (func $write (type $i32_=>_none) (param $x i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local $B (ref $B)) + ;; CHECK-NEXT: (local $C (ref $C)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $B + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $C + ;; CHECK-NEXT: (struct.new $C + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (select (result (ref $B)) + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $write (export "write") (param $x i32) + (local $A (ref $A)) + (local $B (ref $B)) + (local $C (ref $C)) + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (local.set $B + (struct.new $B + (i32.const 10) + ) + ) + (local.set $C + (struct.new $C + (i32.const 20) + ) + ) + ;; Write a different value now: 20. This prevents us from optimizing B, but + ;; we can still optimize A and C. + (struct.set $A 0 + (select + (local.get $B) + (local.get $C) + (local.get $x) + ) + (i32.const 20) + ) + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + (drop + (struct.get $C 0 + (local.get $C) + ) + ) + ) +) |