summaryrefslogtreecommitdiff
path: root/test/lit/passes/local-subtyping.wast
diff options
context:
space:
mode:
Diffstat (limited to 'test/lit/passes/local-subtyping.wast')
-rw-r--r--test/lit/passes/local-subtyping.wast161
1 files changed, 145 insertions, 16 deletions
diff --git a/test/lit/passes/local-subtyping.wast b/test/lit/passes/local-subtyping.wast
index 37990ad24..a2a5780da 100644
--- a/test/lit/passes/local-subtyping.wast
+++ b/test/lit/passes/local-subtyping.wast
@@ -1,7 +1,12 @@
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
-;; RUN: wasm-opt %s --local-subtyping -all -S -o - \
+;; RUN: wasm-opt %s --remove-unused-names --local-subtyping -all -S -o - \
;; RUN: | filecheck %s
+;; --remove-unused-names is run to avoid adding names to blocks. Block names
+;; can prevent non-nullable local validation (we emit named blocks in the binary
+;; format, if we need them, but never emit unnamed ones), which affects some
+;; testcases.
+
(module
;; CHECK: (type ${} (struct ))
(type ${} (struct_subtype data))
@@ -65,7 +70,7 @@
;; more specific type. A similar thing with a parameter, however, is not a
;; thing we can optimize. Also, ignore a local with zero assignments.
;; CHECK: (func $simple-local-but-not-param (param $x funcref)
- ;; CHECK-NEXT: (local $y (ref null $none_=>_i32))
+ ;; CHECK-NEXT: (local $y (ref $none_=>_i32))
;; CHECK-NEXT: (local $unused funcref)
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (ref.func $i32)
@@ -87,9 +92,9 @@
;; CHECK: (func $locals-with-multiple-assignments (param $data dataref)
;; CHECK-NEXT: (local $x eqref)
- ;; CHECK-NEXT: (local $y i31ref)
+ ;; CHECK-NEXT: (local $y (ref i31))
;; CHECK-NEXT: (local $z dataref)
- ;; CHECK-NEXT: (local $w funcref)
+ ;; CHECK-NEXT: (local $w (ref func))
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i31.new
;; CHECK-NEXT: (i32.const 0)
@@ -146,8 +151,9 @@
(local.set $z
(local.get $data)
)
- ;; w is assigned two different types *without* a new LUB possible, as it
- ;; already had the optimal LUB
+ ;; w is assigned two different types *without* a new LUB heap type possible,
+ ;; as it already had the optimal LUB heap type (but it can become non-
+ ;; nullable).
(local.set $w
(ref.func $i32)
)
@@ -159,9 +165,9 @@
;; In some cases multiple iterations are necessary, as one inferred new type
;; applies to a get which then allows another inference.
;; CHECK: (func $multiple-iterations
- ;; CHECK-NEXT: (local $x (ref null $none_=>_i32))
- ;; CHECK-NEXT: (local $y (ref null $none_=>_i32))
- ;; CHECK-NEXT: (local $z (ref null $none_=>_i32))
+ ;; CHECK-NEXT: (local $x (ref $none_=>_i32))
+ ;; CHECK-NEXT: (local $y (ref $none_=>_i32))
+ ;; CHECK-NEXT: (local $z (ref $none_=>_i32))
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (ref.func $i32)
;; CHECK-NEXT: )
@@ -189,9 +195,9 @@
;; Sometimes a refinalize is necessary in between the iterations.
;; CHECK: (func $multiple-iterations-refinalize (param $i i32)
- ;; CHECK-NEXT: (local $x (ref null $none_=>_i32))
- ;; CHECK-NEXT: (local $y (ref null $none_=>_i64))
- ;; CHECK-NEXT: (local $z funcref)
+ ;; CHECK-NEXT: (local $x (ref $none_=>_i32))
+ ;; CHECK-NEXT: (local $y (ref $none_=>_i64))
+ ;; CHECK-NEXT: (local $z (ref func))
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (ref.func $i32)
;; CHECK-NEXT: )
@@ -199,7 +205,7 @@
;; CHECK-NEXT: (ref.func $i64)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $z
- ;; CHECK-NEXT: (select (result funcref)
+ ;; CHECK-NEXT: (select (result (ref func))
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: (local.get $i)
@@ -273,13 +279,13 @@
)
;; CHECK: (func $unreachables (result funcref)
- ;; CHECK-NEXT: (local $temp (ref null $none_=>_funcref))
+ ;; CHECK-NEXT: (local $temp (ref $none_=>_funcref))
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (ref.func $unreachables)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref null $none_=>_funcref))
+ ;; CHECK-NEXT: (block (result (ref $none_=>_funcref))
;; CHECK-NEXT: (local.tee $temp
;; CHECK-NEXT: (ref.func $unreachables)
;; CHECK-NEXT: )
@@ -308,7 +314,7 @@
)
;; CHECK: (func $incompatible-sets (result i32)
- ;; CHECK-NEXT: (local $temp (ref null $none_=>_i32))
+ ;; CHECK-NEXT: (local $temp (ref $none_=>_i32))
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (ref.func $incompatible-sets)
;; CHECK-NEXT: )
@@ -398,4 +404,127 @@
;; as there is no point to making something less specific in type.
(local.set $x (ref.null ${i32}))
)
+
+ ;; CHECK: (func $become-non-nullable
+ ;; CHECK-NEXT: (local $x (ref $none_=>_none))
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.func $become-non-nullable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $become-non-nullable
+ (local $x (ref null func))
+ (local.set $x
+ (ref.func $become-non-nullable)
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+
+ ;; CHECK: (func $already-non-nullable
+ ;; CHECK-NEXT: (local $x (ref $none_=>_none))
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.func $already-non-nullable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $already-non-nullable
+ (local $x (ref func))
+ (local.set $x
+ (ref.func $already-non-nullable)
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+
+ ;; CHECK: (func $cannot-become-non-nullable
+ ;; CHECK-NEXT: (local $x (ref null $none_=>_none))
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.func $become-non-nullable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cannot-become-non-nullable
+ (local $x (ref null func))
+ ;; The set is in a nested scope, so we should not make the local non-
+ ;; nullable, as it would not validate. (We can refine the heap type,
+ ;; though.)
+ (if
+ (i32.const 1)
+ (local.set $x
+ (ref.func $become-non-nullable)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+
+ ;; CHECK: (func $cannot-become-non-nullable-block
+ ;; CHECK-NEXT: (local $x (ref null $none_=>_none))
+ ;; CHECK-NEXT: (block $name
+ ;; CHECK-NEXT: (br_if $name
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.func $become-non-nullable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cannot-become-non-nullable-block
+ (local $x (ref null func))
+ ;; A named block prevents us from optimizing here, the same as above.
+ (block $name
+ ;; Add a br_if to avoid the name being removed.
+ (br_if $name
+ (i32.const 1)
+ )
+ (local.set $x
+ (ref.func $become-non-nullable)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+
+ ;; CHECK: (func $become-non-nullable-block-unnamed
+ ;; CHECK-NEXT: (local $x (ref $none_=>_none))
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.func $become-non-nullable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $become-non-nullable-block-unnamed
+ (local $x (ref null func))
+ ;; An named block does *not* prevent us from optimizing here. Unlike above,
+ ;; an unnamed block is never emitted in the binary format, so it does not
+ ;; prevent validation.
+ (block
+ (local.set $x
+ (ref.func $become-non-nullable)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ )
)