summaryrefslogtreecommitdiff
path: root/test/lit/passes/unsubtyping-casts.wast
diff options
context:
space:
mode:
Diffstat (limited to 'test/lit/passes/unsubtyping-casts.wast')
-rw-r--r--test/lit/passes/unsubtyping-casts.wast452
1 files changed, 452 insertions, 0 deletions
diff --git a/test/lit/passes/unsubtyping-casts.wast b/test/lit/passes/unsubtyping-casts.wast
new file mode 100644
index 000000000..8019efcd8
--- /dev/null
+++ b/test/lit/passes/unsubtyping-casts.wast
@@ -0,0 +1,452 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+;; RUN: foreach %s %t wasm-opt --closed-world --unsubtyping --remove-unused-types -all -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ (type $mid (sub $top (struct)))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (func $cast-optimizable (type $1)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref none)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast-optimizable
+ (drop
+ ;; Simply casting from $top to $mid does not require $mid <: $top, since we
+ ;; haven't shown that it's possible for $mid to inhabit $top. We should
+ ;; optimize this case.
+ (ref.cast (ref $mid)
+ (struct.new $top)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub $top (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub $mid (struct )))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $cast (type $3)
+ ;; CHECK-NEXT: (local $l (ref null $top))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $bot)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $mid)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (local $l (ref null $top))
+ (local.set $l
+ ;; Require $bot <: $top.
+ (struct.new $bot)
+ )
+ (drop
+ ;; Now the cast requires $mid <: $top so that a $bot value appearing in the
+ ;; $top location would still pass the cast to $mid.
+ (ref.cast (ref $mid)
+ (struct.new $top)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub $top (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub $mid (struct )))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $cast (type $3)
+ ;; CHECK-NEXT: (local $l (ref null $top))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $bot)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.test (ref $mid)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (local $l (ref null $top))
+ (local.set $l
+ ;; Require $bot <: $top.
+ (struct.new $bot)
+ )
+ (drop
+ ;; Same as above, but with a ref.test.
+ (ref.test (ref $mid)
+ (struct.new $top)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub $top (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub $mid (struct )))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $cast (type $3)
+ ;; CHECK-NEXT: (local $l (ref null $top))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $bot)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $l (result (ref $mid))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast $l (ref $top) (ref $mid)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (local $l (ref null $top))
+ (local.set $l
+ ;; Require $bot <: $top.
+ (struct.new $bot)
+ )
+ (drop
+ (block $l (result (ref $mid))
+ ;; Same as above, but with a br_on_cast.
+ (drop
+ (br_on_cast $l anyref (ref $mid)
+ (struct.new $top)
+ )
+ )
+ (unreachable)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub $top (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub $mid (struct )))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $cast (type $3)
+ ;; CHECK-NEXT: (local $l (ref null $top))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $bot)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $l (result (ref $top))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast_fail $l (ref $top) (ref $mid)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (local $l (ref null $top))
+ (local.set $l
+ ;; Require $bot <: $top.
+ (struct.new $bot)
+ )
+ (drop
+ (block $l (result (ref $top))
+ ;; Same as above, but with a br_on_cast_fail.
+ (drop
+ (br_on_cast_fail $l anyref (ref $mid)
+ (struct.new $top)
+ )
+ )
+ (unreachable)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (func)))
+ (type $top (sub (func)))
+ ;; CHECK: (type $mid (sub $top (func)))
+ (type $mid (sub $top (func)))
+ ;; CHECK: (type $bot (sub $mid (func)))
+ (type $bot (sub $mid (func)))
+
+ ;; CHECK: (table $t 1 1 (ref null $top))
+ (table $t 1 1 (ref null $top))
+
+ ;; CHECK: (elem declare func $cast)
+
+ ;; CHECK: (func $cast (type $bot)
+ ;; CHECK-NEXT: (local $l (ref null $top))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (ref.func $cast)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call_indirect $t (type $mid)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast (type $bot)
+ (local $l (ref null $top))
+ (local.set $l
+ ;; Require $bot <: $top.
+ (ref.func $cast)
+ )
+ ;; Same as above, but with a call_indirect
+ (call_indirect $t (type $mid)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $unrelated (sub (func)))
+ (type $unrelated (sub (func)))
+
+ ;; CHECK: (type $top (sub (func)))
+ (type $top (sub (func)))
+ ;; CHECK: (type $bot (sub $top (func)))
+ (type $bot (sub $top (func)))
+ )
+
+ ;; CHECK: (table $t 1 1 (ref null $bot))
+ (table $t 1 1 (ref null $bot))
+
+ ;; CHECK: (func $call-indirect (type $bot)
+ ;; CHECK-NEXT: (call_indirect $t (type $top)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call_indirect $t (type $unrelated)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-indirect (type $bot)
+ ;; This cast is guaranteed to succeed, so this directly requires $bot <: $top.
+ (call_indirect $t (type $top)
+ (i32.const 0)
+ )
+ ;; This cast is guaraneed to fail. We should not introduce any subtype
+ ;; relationships that were not already there.
+ (call_indirect $t (type $unrelated)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub $mid (struct )))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $cast-optimizable (type $3)
+ ;; CHECK-NEXT: (local $l (ref null $mid))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $bot)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref none)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast-optimizable
+ (local $l (ref null $mid))
+ (local.set $l
+ ;; This time we require $bot <: $mid.
+ (struct.new $bot)
+ )
+ (drop
+ ;; Even though $bot can now inhabit $mid, it cannot inhabit $top, so it can
+ ;; never appear in this cast, so we do not require $mid <: $top or $bot <:
+ ;; $top.
+ (ref.cast (ref $mid)
+ (struct.new $top)
+ )
+ )
+ )
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub $top (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub (struct )))
+ (type $bot (sub $mid (struct)))
+ )
+
+ ;; CHECK: (type $3 (func (param anyref)))
+
+ ;; CHECK: (func $cast (type $3) (param $any anyref)
+ ;; CHECK-NEXT: (local $l anyref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $bot)
+ ;; CHECK-NEXT: (local.get $any)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $top)
+ ;; CHECK-NEXT: (local.get $any)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $mid)
+ ;; CHECK-NEXT: (local.get $any)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $mid)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast (param $any anyref)
+ (local $l anyref)
+ (drop
+ ;; Cast any -> $bot
+ (ref.cast (ref $bot)
+ (local.get $any)
+ )
+ )
+ (drop
+ ;; Cast any -> $top
+ (ref.cast (ref $top)
+ (local.get $any)
+ )
+ )
+ (drop
+ ;; Cast any -> $mid
+ (ref.cast (ref $mid)
+ (local.get $any)
+ )
+ )
+
+ ;; Require $mid <: any. This will transitively require $mid <: $top and
+ ;; $top <: any, but $bot is unaffected.
+ (local.set $l
+ (struct.new $mid)
+ )
+ )
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $topC (sub (struct )))
+ (type $topC (sub (struct)))
+ ;; CHECK: (type $midC (sub $topC (struct )))
+ (type $midC (sub $topC (struct)))
+ ;; CHECK: (type $botC (sub $midC (struct )))
+ (type $botC (sub $midC (struct)))
+
+ ;; CHECK: (type $topB (sub (struct (field (ref null $topC)))))
+ (type $topB (sub (struct (ref null $topC))))
+ ;; CHECK: (type $midB (sub $topB (struct (field (ref null $botC)))))
+ (type $midB (sub $topB (struct (ref null $botC))))
+ ;; CHECK: (type $botB (sub $midB (struct (field (ref null $botC)))))
+ (type $botB (sub $midB (struct (ref null $botC))))
+
+ ;; CHECK: (type $topA (sub (struct (field (ref null $topB)))))
+ (type $topA (sub (struct (ref null $topB))))
+ ;; CHECK: (type $midA (sub $topA (struct (field (ref null $botB)))))
+ (type $midA (sub $topA (struct (ref null $botB))))
+ ;; CHECK: (type $botA (sub $midA (struct (field (ref null $botB)))))
+ (type $botA (sub $midA (struct (ref null $botB))))
+ )
+
+ ;; CHECK: (type $9 (func))
+
+ ;; CHECK: (func $cast (type $9)
+ ;; CHECK-NEXT: (local $l (ref null $topA))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $botA)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $midA)
+ ;; CHECK-NEXT: (struct.new_default $topA)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $midB)
+ ;; CHECK-NEXT: (struct.new_default $topB)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $midC)
+ ;; CHECK-NEXT: (struct.new_default $topC)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (local $l (ref null $topA))
+ (local.set $l
+ ;; Require $botA <: $topA.
+ (struct.new $botA)
+ )
+ (drop
+ ;; Now the cast requires $midA <: $topA so that a $botA value appearing in
+ ;; the $topA location would still pass the cast to $midA. This will
+ ;; transitively require $botB <: $topB.
+ (ref.cast (ref $midA)
+ (struct.new $topA)
+ )
+ )
+ (drop
+ ;; Same as before, but now for the B types. This requires $botC <: $topC, but
+ ;; only after the previous cast has already been analyzed.
+ (ref.cast (ref $midB)
+ (struct.new $topB)
+ )
+ )
+ (drop
+ ;; Same as before, but now for the C types. Now no types will able to be
+ ;; optimized.
+ (ref.cast (ref $midC)
+ (struct.new $topC)
+ )
+ )
+ )
+)