summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/lit/passes/gufa-refs.wast631
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)
+ )
+ )
+ )
+)