summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/lit/passes/dae-gc-refine-params.wast166
-rw-r--r--test/lit/passes/dae-gc-refine-return.wast283
-rw-r--r--test/lit/passes/local-subtyping.wast49
-rw-r--r--test/lit/passes/type-refining.wast217
4 files changed, 607 insertions, 108 deletions
diff --git a/test/lit/passes/dae-gc-refine-params.wast b/test/lit/passes/dae-gc-refine-params.wast
index 818c943b2..e7059b0e0 100644
--- a/test/lit/passes/dae-gc-refine-params.wast
+++ b/test/lit/passes/dae-gc-refine-params.wast
@@ -15,12 +15,14 @@
;; NOMNL: (type ${i32_i64} (struct_subtype (field i32) (field i64) ${i32}))
(type ${i32_i64} (struct_subtype (field i32) (field i64) ${i32}))
+ ;; CHECK: (type ${i32_f32} (struct (field i32) (field f32)))
+
;; CHECK: (type ${f64} (struct (field f64)))
+ ;; NOMNL: (type ${i32_f32} (struct_subtype (field i32) (field f32) ${i32}))
+
;; NOMNL: (type ${f64} (struct_subtype (field f64) ${}))
(type ${f64} (struct_subtype (field f64) ${}))
- ;; CHECK: (type ${i32_f32} (struct (field i32) (field f32)))
- ;; NOMNL: (type ${i32_f32} (struct_subtype (field i32) (field f32) ${i32}))
(type ${i32_f32} (struct_subtype (field i32) (field f32) ${i32}))
;; CHECK: (func $call-various-params-no
@@ -81,26 +83,26 @@
;; CHECK: (func $call-various-params-yes
;; CHECK-NEXT: (call $various-params-yes
- ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $various-params-yes
- ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (ref.null ${i32_i64})
+ ;; CHECK-NEXT: (call $get_null_{i32_i64})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $call-various-params-yes (type $none_=>_none)
;; NOMNL-NEXT: (call $various-params-yes
- ;; NOMNL-NEXT: (ref.null ${i32})
+ ;; NOMNL-NEXT: (call $get_null_{i32})
;; NOMNL-NEXT: (i32.const 0)
- ;; NOMNL-NEXT: (ref.null ${i32})
+ ;; NOMNL-NEXT: (call $get_null_{i32})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (call $various-params-yes
- ;; NOMNL-NEXT: (ref.null ${i32})
+ ;; NOMNL-NEXT: (call $get_null_{i32})
;; NOMNL-NEXT: (i32.const 1)
- ;; NOMNL-NEXT: (ref.null ${i32_i64})
+ ;; NOMNL-NEXT: (call $get_null_{i32_i64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $call-various-params-yes
@@ -108,14 +110,14 @@
;; both of those pairs can be optimized to {i32}.
;; There is also an i32 in the middle, which should not confuse us.
(call $various-params-yes
- (ref.null ${i32})
+ (call $get_null_{i32})
(i32.const 0)
- (ref.null ${i32})
+ (call $get_null_{i32})
)
(call $various-params-yes
- (ref.null ${i32})
+ (call $get_null_{i32})
(i32.const 1)
- (ref.null ${i32_i64})
+ (call $get_null_{i32_i64})
)
)
;; This function is called in ways that *do* allow us to alter the types of
@@ -151,34 +153,34 @@
;; CHECK: (func $call-various-params-set
;; CHECK-NEXT: (call $various-params-set
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: (call $get_null_{i32})
+ ;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $various-params-set
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: (ref.null ${i32_i64})
+ ;; CHECK-NEXT: (call $get_null_{i32})
+ ;; CHECK-NEXT: (call $get_null_{i32_i64})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $call-various-params-set (type $none_=>_none)
;; NOMNL-NEXT: (call $various-params-set
- ;; NOMNL-NEXT: (ref.null ${i32})
- ;; NOMNL-NEXT: (ref.null ${i32})
+ ;; NOMNL-NEXT: (call $get_null_{i32})
+ ;; NOMNL-NEXT: (call $get_null_{i32})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (call $various-params-set
- ;; NOMNL-NEXT: (ref.null ${i32})
- ;; NOMNL-NEXT: (ref.null ${i32_i64})
+ ;; NOMNL-NEXT: (call $get_null_{i32})
+ ;; NOMNL-NEXT: (call $get_null_{i32_i64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $call-various-params-set
;; The first argument gets {i32} and {i32}; the second {i32} and {i32_i64;
;; both of those pairs can be optimized to {i32}
(call $various-params-set
- (ref.null ${i32})
- (ref.null ${i32})
+ (call $get_null_{i32})
+ (call $get_null_{i32})
)
(call $various-params-set
- (ref.null ${i32})
- (ref.null ${i32_i64})
+ (call $get_null_{i32})
+ (call $get_null_{i32_i64})
)
)
;; This function is called in ways that *do* allow us to alter the types of
@@ -203,7 +205,7 @@
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $y
- ;; CHECK-NEXT: (ref.null ${i32_i64})
+ ;; CHECK-NEXT: (call $get_null_{i32_i64})
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $y)
@@ -229,7 +231,7 @@
;; NOMNL-NEXT: (local.get $2)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (local.set $y
- ;; NOMNL-NEXT: (ref.null ${i32_i64})
+ ;; NOMNL-NEXT: (call $get_null_{i32_i64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $y)
@@ -250,7 +252,7 @@
)
;; Write to $y in a way that does not cause any issue, and we should not do
;; any fixup while we refine the type.
- (local.set $y (ref.null ${i32_i64}))
+ (local.set $y (call $get_null_{i32_i64}))
(drop
(local.get $y)
)
@@ -258,18 +260,18 @@
;; CHECK: (func $call-various-params-tee
;; CHECK-NEXT: (call $various-params-tee
- ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $call-various-params-tee (type $none_=>_none)
;; NOMNL-NEXT: (call $various-params-tee
- ;; NOMNL-NEXT: (ref.null ${i32})
+ ;; NOMNL-NEXT: (call $get_null_{i32})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $call-various-params-tee
;; The argument gets {i32}, which allows us to refine.
(call $various-params-tee
- (ref.null ${i32})
+ (call $get_null_{i32})
)
)
;; CHECK: (func $various-params-tee (param $x (ref null ${i32}))
@@ -279,7 +281,7 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $block (result (ref null ${i32}))
;; CHECK-NEXT: (local.tee $x
- ;; CHECK-NEXT: (ref.null ${i32_i64})
+ ;; CHECK-NEXT: (call $get_null_{i32_i64})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -291,7 +293,7 @@
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (block $block (result (ref null ${i32}))
;; NOMNL-NEXT: (local.tee $x
- ;; NOMNL-NEXT: (ref.null ${i32_i64})
+ ;; NOMNL-NEXT: (call $get_null_{i32_i64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
@@ -304,7 +306,7 @@
;; would occur), and that will also cause the block's type to update as well.
(drop
(block (result (ref null ${}))
- (local.tee $x (ref.null ${i32_i64}))
+ (local.tee $x (call $get_null_{i32_i64}))
)
)
)
@@ -314,7 +316,7 @@
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (ref.null ${i32})
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $various-params-null
;; CHECK-NEXT: (ref.as_non_null
@@ -330,7 +332,7 @@
;; NOMNL-NEXT: (ref.as_non_null
;; NOMNL-NEXT: (ref.null ${i32})
;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: (ref.null ${i32})
+ ;; NOMNL-NEXT: (call $get_null_{i32})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (call $various-params-null
;; NOMNL-NEXT: (ref.as_non_null
@@ -346,7 +348,7 @@
;; second gets only one.
(call $various-params-null
(ref.as_non_null (ref.null ${i32}))
- (ref.null ${i32})
+ (call $get_null_{i32})
)
(call $various-params-null
(ref.as_non_null (ref.null ${i32}))
@@ -393,28 +395,28 @@
;; CHECK: (func $call-various-params-middle
;; CHECK-NEXT: (call $various-params-middle
- ;; CHECK-NEXT: (ref.null ${i32_i64})
+ ;; CHECK-NEXT: (call $get_null_{i32_i64})
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $various-params-middle
- ;; CHECK-NEXT: (ref.null ${i32_f32})
+ ;; CHECK-NEXT: (call $get_null_{i32_f32})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $call-various-params-middle (type $none_=>_none)
;; NOMNL-NEXT: (call $various-params-middle
- ;; NOMNL-NEXT: (ref.null ${i32_i64})
+ ;; NOMNL-NEXT: (call $get_null_{i32_i64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (call $various-params-middle
- ;; NOMNL-NEXT: (ref.null ${i32_f32})
+ ;; NOMNL-NEXT: (call $get_null_{i32_f32})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $call-various-params-middle
;; The argument gets {i32_i64} and {i32_f32}. This allows us to refine from
;; {} to {i32}, a type "in the middle".
(call $various-params-middle
- (ref.null ${i32_i64})
+ (call $get_null_{i32_i64})
)
(call $various-params-middle
- (ref.null ${i32_f32})
+ (call $get_null_{i32_f32})
)
)
;; CHECK: (func $various-params-middle (param $x (ref null ${i32}))
@@ -510,4 +512,80 @@
(struct.new_default ${})
)
)
+
+ ;; CHECK: (func $call-update-null
+ ;; CHECK-NEXT: (call $update-null
+ ;; CHECK-NEXT: (ref.null ${})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $update-null
+ ;; CHECK-NEXT: (struct.new_default ${})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $call-update-null (type $none_=>_none)
+ ;; NOMNL-NEXT: (call $update-null
+ ;; NOMNL-NEXT: (ref.null ${})
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (call $update-null
+ ;; NOMNL-NEXT: (struct.new_default ${})
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $call-update-null
+ ;; Call a function with one of the parameters a null of a type that we can
+ ;; update in order to get a better LUB.
+ (call $update-null
+ (ref.null any)
+ )
+ (call $update-null
+ (struct.new_default ${})
+ )
+ )
+
+ ;; CHECK: (func $update-null (param $x (ref null ${}))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $update-null (type $ref?|${}|_=>_none) (param $x (ref null ${}))
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $update-null (param $x (ref null any))
+ ;; "Use" the param to avoid other optimizations kicking in. We should only
+ ;; see the type of the param refined to a null ${} after updating the null
+ ;; in the caller.
+ (drop (local.get $x))
+ )
+
+ ;; CHECK: (func $get_null_{i32} (result (ref null ${i32}))
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $get_null_{i32} (type $none_=>_ref?|${i32}|) (result (ref null ${i32}))
+ ;; NOMNL-NEXT: (ref.null ${i32})
+ ;; NOMNL-NEXT: )
+ (func $get_null_{i32} (result (ref null ${i32}))
+ ;; Helper function that returns a null value of ${i32}. We use this instead of
+ ;; a direct ref.null because those can be rewritten by LUBFinder.
+ (ref.null ${i32})
+ )
+
+ ;; CHECK: (func $get_null_{i32_i64} (result (ref null ${i32_i64}))
+ ;; CHECK-NEXT: (ref.null ${i32_i64})
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $get_null_{i32_i64} (type $none_=>_ref?|${i32_i64}|) (result (ref null ${i32_i64}))
+ ;; NOMNL-NEXT: (ref.null ${i32_i64})
+ ;; NOMNL-NEXT: )
+ (func $get_null_{i32_i64} (result (ref null ${i32_i64}))
+ (ref.null ${i32_i64})
+ )
+
+ ;; CHECK: (func $get_null_{i32_f32} (result (ref null ${i32_f32}))
+ ;; CHECK-NEXT: (ref.null ${i32_f32})
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $get_null_{i32_f32} (type $none_=>_ref?|${i32_f32}|) (result (ref null ${i32_f32}))
+ ;; NOMNL-NEXT: (ref.null ${i32_f32})
+ ;; NOMNL-NEXT: )
+ (func $get_null_{i32_f32} (result (ref null ${i32_f32}))
+ (ref.null ${i32_f32})
+ )
)
diff --git a/test/lit/passes/dae-gc-refine-return.wast b/test/lit/passes/dae-gc-refine-return.wast
index c5ea326d9..15a40652d 100644
--- a/test/lit/passes/dae-gc-refine-return.wast
+++ b/test/lit/passes/dae-gc-refine-return.wast
@@ -7,16 +7,20 @@
;; NOMNL: (type $return_{} (func_subtype (result (ref ${})) func))
(type $return_{} (func (result (ref ${}))))
+ ;; CHECK: (type ${i32} (struct (field i32)))
+
+ ;; CHECK: (type ${i32_i64} (struct (field i32) (field i64)))
+
;; CHECK: (type ${i32_f32} (struct (field i32) (field f32)))
+ ;; NOMNL: (type ${i32} (struct_subtype (field i32) ${}))
+
+ ;; NOMNL: (type ${i32_i64} (struct_subtype (field i32) (field i64) ${i32}))
+
;; NOMNL: (type ${i32_f32} (struct_subtype (field i32) (field f32) ${i32}))
(type ${i32_f32} (struct_subtype (field i32) (field f32) ${i32}))
- ;; CHECK: (type ${i32_i64} (struct (field i32) (field i64)))
- ;; NOMNL: (type ${i32_i64} (struct_subtype (field i32) (field i64) ${i32}))
(type ${i32_i64} (struct_subtype (field i32) (field i64) ${i32}))
- ;; CHECK: (type ${i32} (struct (field i32)))
- ;; NOMNL: (type ${i32} (struct_subtype (field i32) ${}))
(type ${i32} (struct_subtype (field i32) ${}))
;; CHECK: (type ${} (struct ))
@@ -53,45 +57,53 @@
;; We cannot refine the return type if it is already the best it can be.
;; CHECK: (func $refine-return-no-refining (result anyref)
;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local $any anyref)
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (call $refine-return-no-refining)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: (local.get $any)
;; CHECK-NEXT: )
;; NOMNL: (func $refine-return-no-refining (type $none_=>_anyref) (result anyref)
;; NOMNL-NEXT: (local $temp anyref)
+ ;; NOMNL-NEXT: (local $any anyref)
;; NOMNL-NEXT: (local.set $temp
;; NOMNL-NEXT: (call $refine-return-no-refining)
;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: (ref.null any)
+ ;; NOMNL-NEXT: (local.get $any)
;; NOMNL-NEXT: )
(func $refine-return-no-refining (result anyref)
(local $temp anyref)
+ (local $any anyref)
+
(local.set $temp (call $refine-return-no-refining))
- (ref.null any)
+ (local.get $any)
)
;; Refine the return type based on the value flowing out.
;; CHECK: (func $refine-return-flow (result funcref)
;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local $func funcref)
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (call $refine-return-flow)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; NOMNL: (func $refine-return-flow (type $none_=>_funcref) (result funcref)
;; NOMNL-NEXT: (local $temp anyref)
+ ;; NOMNL-NEXT: (local $func funcref)
;; NOMNL-NEXT: (local.set $temp
;; NOMNL-NEXT: (call $refine-return-flow)
;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: (ref.null func)
+ ;; NOMNL-NEXT: (local.get $func)
;; NOMNL-NEXT: )
(func $refine-return-flow (result anyref)
(local $temp anyref)
+ (local $func funcref)
+
(local.set $temp (call $refine-return-flow))
- (ref.null func)
+ (local.get $func)
)
;; CHECK: (func $call-refine-return-flow (result funcref)
;; CHECK-NEXT: (local $temp anyref)
@@ -132,257 +144,294 @@
;; Refine the return type based on a return.
;; CHECK: (func $refine-return-return (result funcref)
;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local $func funcref)
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (call $refine-return-return)
;; CHECK-NEXT: )
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $refine-return-return (type $none_=>_funcref) (result funcref)
;; NOMNL-NEXT: (local $temp anyref)
+ ;; NOMNL-NEXT: (local $func funcref)
;; NOMNL-NEXT: (local.set $temp
;; NOMNL-NEXT: (call $refine-return-return)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null func)
+ ;; NOMNL-NEXT: (local.get $func)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $refine-return-return (result anyref)
(local $temp anyref)
+ (local $func funcref)
+
(local.set $temp (call $refine-return-return))
- (return (ref.null func))
+ (return (local.get $func))
)
;; Refine the return type based on multiple values.
;; CHECK: (func $refine-return-many (result funcref)
;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local $func funcref)
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (call $refine-return-many)
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; NOMNL: (func $refine-return-many (type $none_=>_funcref) (result funcref)
;; NOMNL-NEXT: (local $temp anyref)
+ ;; NOMNL-NEXT: (local $func funcref)
;; NOMNL-NEXT: (local.set $temp
;; NOMNL-NEXT: (call $refine-return-many)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (if
;; NOMNL-NEXT: (i32.const 1)
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null func)
+ ;; NOMNL-NEXT: (local.get $func)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (if
;; NOMNL-NEXT: (i32.const 2)
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null func)
+ ;; NOMNL-NEXT: (local.get $func)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: (ref.null func)
+ ;; NOMNL-NEXT: (local.get $func)
;; NOMNL-NEXT: )
(func $refine-return-many (result anyref)
(local $temp anyref)
+ (local $func funcref)
+
(local.set $temp (call $refine-return-many))
(if
(i32.const 1)
- (return (ref.null func))
+ (return (local.get $func))
)
(if
(i32.const 2)
- (return (ref.null func))
+ (return (local.get $func))
)
- (ref.null func)
+ (local.get $func)
)
;; CHECK: (func $refine-return-many-blocked (result anyref)
;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local $func funcref)
+ ;; CHECK-NEXT: (local $data (ref null data))
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (call $refine-return-many-blocked)
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null data)
+ ;; CHECK-NEXT: (local.get $data)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; NOMNL: (func $refine-return-many-blocked (type $none_=>_anyref) (result anyref)
;; NOMNL-NEXT: (local $temp anyref)
+ ;; NOMNL-NEXT: (local $func funcref)
+ ;; NOMNL-NEXT: (local $data (ref null data))
;; NOMNL-NEXT: (local.set $temp
;; NOMNL-NEXT: (call $refine-return-many-blocked)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (if
;; NOMNL-NEXT: (i32.const 1)
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null func)
+ ;; NOMNL-NEXT: (local.get $func)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (if
;; NOMNL-NEXT: (i32.const 2)
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null data)
+ ;; NOMNL-NEXT: (local.get $data)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: (ref.null func)
+ ;; NOMNL-NEXT: (local.get $func)
;; NOMNL-NEXT: )
(func $refine-return-many-blocked (result anyref)
(local $temp anyref)
+ (local $func funcref)
+ (local $data (ref null data))
+
(local.set $temp (call $refine-return-many-blocked))
(if
(i32.const 1)
- (return (ref.null func))
+ (return (local.get $func))
)
(if
(i32.const 2)
;; The refined return value is blocked by this return.
- (return (ref.null data))
+ (return (local.get $data))
)
- (ref.null func)
+ (local.get $func)
)
;; CHECK: (func $refine-return-many-blocked-2 (result anyref)
;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local $func funcref)
+ ;; CHECK-NEXT: (local $data (ref null data))
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (call $refine-return-many-blocked-2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null data)
+ ;; CHECK-NEXT: (local.get $data)
;; CHECK-NEXT: )
;; NOMNL: (func $refine-return-many-blocked-2 (type $none_=>_anyref) (result anyref)
;; NOMNL-NEXT: (local $temp anyref)
+ ;; NOMNL-NEXT: (local $func funcref)
+ ;; NOMNL-NEXT: (local $data (ref null data))
;; NOMNL-NEXT: (local.set $temp
;; NOMNL-NEXT: (call $refine-return-many-blocked-2)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (if
;; NOMNL-NEXT: (i32.const 1)
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null func)
+ ;; NOMNL-NEXT: (local.get $func)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (if
;; NOMNL-NEXT: (i32.const 2)
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null func)
+ ;; NOMNL-NEXT: (local.get $func)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: (ref.null data)
+ ;; NOMNL-NEXT: (local.get $data)
;; NOMNL-NEXT: )
(func $refine-return-many-blocked-2 (result anyref)
(local $temp anyref)
+ (local $func funcref)
+ (local $data (ref null data))
+
(local.set $temp (call $refine-return-many-blocked-2))
(if
(i32.const 1)
- (return (ref.null func))
+ (return (local.get $func))
)
(if
(i32.const 2)
- (return (ref.null func))
+ (return (local.get $func))
)
;; The refined return value is blocked by this value.
- (ref.null data)
+ (local.get $data)
)
;; CHECK: (func $refine-return-many-middle (result (ref null ${i32}))
;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local ${i32_i64} (ref null ${i32_i64}))
+ ;; CHECK-NEXT: (local ${i32_f32} (ref null ${i32_f32}))
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (call $refine-return-many-middle)
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null ${i32_i64})
+ ;; CHECK-NEXT: (local.get ${i32_i64})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null ${i32_f32})
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (local.get ${i32_f32})
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $refine-return-many-middle (type $none_=>_ref?|${i32}|) (result (ref null ${i32}))
;; NOMNL-NEXT: (local $temp anyref)
+ ;; NOMNL-NEXT: (local ${i32_i64} (ref null ${i32_i64}))
+ ;; NOMNL-NEXT: (local ${i32_f32} (ref null ${i32_f32}))
;; NOMNL-NEXT: (local.set $temp
;; NOMNL-NEXT: (call $refine-return-many-middle)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (if
;; NOMNL-NEXT: (i32.const 1)
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null ${i32_i64})
+ ;; NOMNL-NEXT: (local.get ${i32_i64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: (ref.null ${i32_f32})
+ ;; NOMNL-NEXT: (return
+ ;; NOMNL-NEXT: (local.get ${i32_f32})
+ ;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $refine-return-many-middle (result anyref)
(local $temp anyref)
+ (local ${i32_i64} (ref null ${i32_i64}))
+ (local ${i32_f32} (ref null ${i32_f32}))
+
(local.set $temp (call $refine-return-many-middle))
;; Return two different struct types, with an LUB that is not equal to either
;; of them.
(if
(i32.const 1)
- (return (ref.null ${i32_i64}))
+ (return (local.get ${i32_i64}))
)
- (ref.null ${i32_f32})
+ (return (local.get ${i32_f32}))
)
;; We can refine the return types of tuples.
;; CHECK: (func $refine-return-tuple (result funcref i32)
;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local $func funcref)
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (tuple.extract 0
;; CHECK-NEXT: (call $refine-return-tuple)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (tuple.make
- ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $refine-return-tuple (type $none_=>_funcref_i32) (result funcref i32)
;; NOMNL-NEXT: (local $temp anyref)
+ ;; NOMNL-NEXT: (local $func funcref)
;; NOMNL-NEXT: (local.set $temp
;; NOMNL-NEXT: (tuple.extract 0
;; NOMNL-NEXT: (call $refine-return-tuple)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (tuple.make
- ;; NOMNL-NEXT: (ref.null func)
+ ;; NOMNL-NEXT: (local.get $func)
;; NOMNL-NEXT: (i32.const 1)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $refine-return-tuple (result anyref i32)
(local $temp anyref)
+ (local $func funcref)
+
(local.set $temp
(tuple.extract 0
(call $refine-return-tuple)
@@ -390,7 +439,7 @@
)
(tuple.make
- (ref.null func)
+ (local.get $func)
(i32.const 1)
)
)
@@ -441,28 +490,32 @@
(return_call $tail-callee)
)
;; CHECK: (func $tail-caller-no (result anyref)
+ ;; CHECK-NEXT: (local $any anyref)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: (local.get $any)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (return_call $tail-callee)
;; CHECK-NEXT: )
;; NOMNL: (func $tail-caller-no (type $none_=>_anyref) (result anyref)
+ ;; NOMNL-NEXT: (local $any anyref)
;; NOMNL-NEXT: (if
;; NOMNL-NEXT: (i32.const 1)
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null any)
+ ;; NOMNL-NEXT: (local.get $any)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (return_call $tail-callee)
;; NOMNL-NEXT: )
(func $tail-caller-no (result anyref)
+ (local $any anyref)
+
;; This function's return type cannot be refined because of another return
;; whose type prevents it.
(if (i32.const 1)
- (return (ref.null any))
+ (return (local.get $any))
)
(return_call $tail-callee)
)
@@ -516,10 +569,11 @@
(return_call_indirect (type $return_{}) (i32.const 0))
)
;; CHECK: (func $tail-caller-indirect-no (result anyref)
+ ;; CHECK-NEXT: (local $any anyref)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: (local.get $any)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (return_call_indirect $0 (type $return_{})
@@ -527,10 +581,11 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $tail-caller-indirect-no (type $none_=>_anyref) (result anyref)
+ ;; NOMNL-NEXT: (local $any anyref)
;; NOMNL-NEXT: (if
;; NOMNL-NEXT: (i32.const 1)
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null any)
+ ;; NOMNL-NEXT: (local.get $any)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (return_call_indirect $0 (type $return_{})
@@ -538,8 +593,10 @@
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $tail-caller-indirect-no (result anyref)
+ (local $any anyref)
+
(if (i32.const 1)
- (return (ref.null any))
+ (return (local.get $any))
)
(return_call_indirect (type $return_{}) (i32.const 0))
)
@@ -579,45 +636,56 @@
(unreachable)
)
;; CHECK: (func $tail-caller-call_ref-yes (result (ref ${}))
+ ;; CHECK-NEXT: (local $return_{} (ref null $return_{}))
;; CHECK-NEXT: (return_call_ref
- ;; CHECK-NEXT: (ref.null $return_{})
+ ;; CHECK-NEXT: (local.get $return_{})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $tail-caller-call_ref-yes (type $return_{}) (result (ref ${}))
+ ;; NOMNL-NEXT: (local $return_{} (ref null $return_{}))
;; NOMNL-NEXT: (return_call_ref
- ;; NOMNL-NEXT: (ref.null $return_{})
+ ;; NOMNL-NEXT: (local.get $return_{})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $tail-caller-call_ref-yes (result anyref)
- (return_call_ref (ref.null $return_{}))
+ (local $return_{} (ref null $return_{}))
+
+ (return_call_ref (local.get $return_{}))
)
;; CHECK: (func $tail-caller-call_ref-no (result anyref)
+ ;; CHECK-NEXT: (local $any anyref)
+ ;; CHECK-NEXT: (local $return_{} (ref null $return_{}))
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: (local.get $any)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (return_call_ref
- ;; CHECK-NEXT: (ref.null $return_{})
+ ;; CHECK-NEXT: (local.get $return_{})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $tail-caller-call_ref-no (type $none_=>_anyref) (result anyref)
+ ;; NOMNL-NEXT: (local $any anyref)
+ ;; NOMNL-NEXT: (local $return_{} (ref null $return_{}))
;; NOMNL-NEXT: (if
;; NOMNL-NEXT: (i32.const 1)
;; NOMNL-NEXT: (return
- ;; NOMNL-NEXT: (ref.null any)
+ ;; NOMNL-NEXT: (local.get $any)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (return_call_ref
- ;; NOMNL-NEXT: (ref.null $return_{})
+ ;; NOMNL-NEXT: (local.get $return_{})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $tail-caller-call_ref-no (result anyref)
+ (local $any anyref)
+ (local $return_{} (ref null $return_{}))
+
(if (i32.const 1)
- (return (ref.null any))
+ (return (local.get $any))
)
- (return_call_ref (ref.null $return_{}))
+ (return_call_ref (local.get $return_{}))
)
;; CHECK: (func $tail-caller-call_ref-unreachable
;; CHECK-NEXT: (unreachable)
@@ -659,4 +727,91 @@
(call $tail-caller-call_ref-unreachable)
)
)
+
+ ;; CHECK: (func $update-null (param $x i32) (param $y i32) (result (ref null ${i32}))
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $y)
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (struct.new_default ${i32_f32})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (struct.new_default ${i32_i64})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $update-null (type $i32_i32_=>_ref?|${i32}|) (param $x i32) (param $y i32) (result (ref null ${i32}))
+ ;; NOMNL-NEXT: (if
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: (if
+ ;; NOMNL-NEXT: (local.get $y)
+ ;; NOMNL-NEXT: (return
+ ;; NOMNL-NEXT: (struct.new_default ${i32_f32})
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (return
+ ;; NOMNL-NEXT: (ref.null ${i32})
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (return
+ ;; NOMNL-NEXT: (struct.new_default ${i32_i64})
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $update-null (param $x i32) (param $y i32) (result anyref)
+ ;; Of the three returns here, the null can be updated, and the LUB is
+ ;; determined by the other two, and is their shared parent ${}.
+ (if
+ (local.get $x)
+ (if
+ (local.get $y)
+ (return (struct.new ${i32_f32}))
+ (return (ref.null any))
+ )
+ (return (struct.new ${i32_i64}))
+ )
+ )
+
+ ;; CHECK: (func $call-update-null (result anyref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $update-null
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $update-null
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $call-update-null (type $none_=>_anyref) (result anyref)
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (call $update-null
+ ;; NOMNL-NEXT: (i32.const 0)
+ ;; NOMNL-NEXT: (i32.const 1)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (call $update-null
+ ;; NOMNL-NEXT: (i32.const 1)
+ ;; NOMNL-NEXT: (i32.const 0)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $call-update-null (result anyref)
+ ;; Call $update-null so it gets optimized. (Call it with various values so
+ ;; that other opts do not inline the constants.)
+ (drop
+ ($call $update-null
+ (i32.const 0)
+ (i32.const 1)
+ )
+ )
+ ($call $update-null
+ (i32.const 1)
+ (i32.const 0)
+ )
+ )
)
diff --git a/test/lit/passes/local-subtyping.wast b/test/lit/passes/local-subtyping.wast
index f655eba1e..99a59d005 100644
--- a/test/lit/passes/local-subtyping.wast
+++ b/test/lit/passes/local-subtyping.wast
@@ -3,6 +3,12 @@
;; RUN: | filecheck %s
(module
+ ;; CHECK: (type ${} (struct ))
+ (type ${} (struct_subtype data))
+
+ ;; CHECK: (type ${i32} (struct (field i32)))
+ (type ${i32} (struct_subtype (field i32) data))
+
;; CHECK: (import "out" "i32" (func $i32 (result i32)))
(import "out" "i32" (func $i32 (result i32)))
;; CHECK: (import "out" "i64" (func $i64 (result i64)))
@@ -333,4 +339,47 @@
)
(unreachable)
)
+
+ ;; CHECK: (func $update-nulls
+ ;; CHECK-NEXT: (local $x (ref null ${}))
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.null ${})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.null ${})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (struct.new_default ${})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.null ${})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.null ${})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.null ${})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $update-nulls
+ (local $x anyref)
+ (local.set $x (ref.null any))
+ (local.set $x (ref.null eq))
+ ;; All the nulls can be changed into other nulls here, for the new LUB,
+ ;; which will be ${}
+ (local.set $x (struct.new ${}))
+ (local.set $x (ref.null data))
+ ;; Note that this func null is even of a type that is incompatible with the
+ ;; new lub (func vs data). Still, we can just update it along with the
+ ;; others.
+ (local.set $x (ref.null func))
+ ;; This null is equal to the LUB we'll find, and will not change.
+ (local.set $x (ref.null ${}))
+ ;; This null is more specific than the LUB we'll find, and will not change,
+ ;; as there is no point to making something less specific in type.
+ (local.set $x (ref.null ${i32}))
+ )
)
diff --git a/test/lit/passes/type-refining.wast b/test/lit/passes/type-refining.wast
index 38a6319f3..eb33f058a 100644
--- a/test/lit/passes/type-refining.wast
+++ b/test/lit/passes/type-refining.wast
@@ -551,3 +551,220 @@
)
)
)
+
+(module
+ ;; CHECK: (type $struct (struct_subtype (field (mut (ref null $struct))) data))
+ (type $struct (struct_subtype (field (mut (ref null data))) data))
+
+ ;; CHECK: (type $ref|$struct|_=>_none (func_subtype (param (ref $struct)) func))
+
+ ;; CHECK: (func $update-null (type $ref|$struct|_=>_none) (param $struct (ref $struct))
+ ;; CHECK-NEXT: (struct.set $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.set $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $update-null (param $struct (ref $struct))
+ (struct.set $struct 0
+ (local.get $struct)
+ ;; Write a $struct to the field.
+ (local.get $struct)
+ )
+ (struct.set $struct 0
+ (local.get $struct)
+ ;; This null can be updated, allowing us to refine the type of the field
+ ;; to a null of $struct.
+ (ref.null data)
+ )
+ )
+)
+
+(module
+ ;; As above, but now the null is in a child. The result should be the same:
+ ;; refine the field to nullable $struct.
+
+ ;; CHECK: (type $struct (struct_subtype (field (mut (ref null $struct))) data))
+ (type $struct (struct_subtype (field (mut (ref null data))) data))
+ ;; CHECK: (type $child (struct_subtype (field (mut (ref null $struct))) $struct))
+ (type $child (struct_subtype (field (mut (ref null data))) $struct))
+
+ ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func_subtype (param (ref $struct) (ref $child)) func))
+
+ ;; CHECK: (func $update-null (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child))
+ ;; CHECK-NEXT: (struct.set $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.set $child 0
+ ;; CHECK-NEXT: (local.get $child)
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $update-null (param $struct (ref $struct)) (param $child (ref $child))
+ (struct.set $struct 0
+ (local.get $struct)
+ (local.get $struct)
+ )
+ (struct.set $child 0
+ (local.get $child)
+ (ref.null data)
+ )
+ )
+)
+
+(module
+ ;; As above, but now the null is in a parent. The result should be the same.
+
+ ;; CHECK: (type $struct (struct_subtype (field (mut (ref null $struct))) data))
+ (type $struct (struct_subtype (field (mut (ref null data))) data))
+ ;; CHECK: (type $child (struct_subtype (field (mut (ref null $struct))) $struct))
+ (type $child (struct_subtype (field (mut (ref null data))) $struct))
+
+ ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func_subtype (param (ref $struct) (ref $child)) func))
+
+ ;; CHECK: (func $update-null (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child))
+ ;; CHECK-NEXT: (struct.set $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.set $child 0
+ ;; CHECK-NEXT: (local.get $child)
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $update-null (param $struct (ref $struct)) (param $child (ref $child))
+ (struct.set $struct 0
+ (local.get $struct)
+ (ref.null data)
+ )
+ (struct.set $child 0
+ (local.get $child)
+ (local.get $struct)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $struct (struct_subtype (field (mut (ref null data))) data))
+ (type $struct (struct_subtype (field (mut (ref null data))) data))
+
+ ;; CHECK: (type $ref|$struct|_=>_none (func_subtype (param (ref $struct)) func))
+
+ ;; CHECK: (func $work (type $ref|$struct|_=>_none) (param $struct (ref $struct))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $work (param $struct (ref $struct))
+ ;; The only write to this struct is of a null default value. There is
+ ;; nothing to optimize here.
+ (drop
+ (struct.new_default $struct)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $struct (struct_subtype (field (mut (ref null $struct))) data))
+ (type $struct (struct_subtype (field (mut (ref null data))) data))
+
+ ;; CHECK: (type $ref|$struct|_=>_none (func_subtype (param (ref $struct)) func))
+
+ ;; CHECK: (func $work (type $ref|$struct|_=>_none) (param $struct (ref $struct))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.set $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $work (param $struct (ref $struct))
+ (drop
+ (struct.new_default $struct)
+ )
+ ;; Also write a $struct. The null default should not prevent us from
+ ;; refining the field's type to $struct (but nullable).
+ (struct.set $struct 0
+ (local.get $struct)
+ (local.get $struct)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $struct (struct_subtype (field (mut (ref null $struct))) data))
+ (type $struct (struct_subtype (field (mut (ref null data))) data))
+
+ ;; CHECK: (type $ref|$struct|_=>_none (func_subtype (param (ref $struct)) func))
+
+ ;; CHECK: (func $work (type $ref|$struct|_=>_none) (param $struct (ref $struct))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.set $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $work (param $struct (ref $struct))
+ ;; As before, but instead of new_default, new, and use a null in the given
+ ;; value, which should be updated.
+ (drop
+ (struct.new $struct
+ (ref.null data)
+ )
+ )
+ (struct.set $struct 0
+ (local.get $struct)
+ (local.get $struct)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $struct (struct_subtype (field (mut (ref null $child))) (field (mut (ref null $struct))) data))
+ (type $struct (struct_subtype (field (mut (ref null data))) (field (mut (ref null data))) data))
+
+ ;; CHECK: (type $child (struct_subtype (field (mut (ref null $child))) (field (mut (ref null $struct))) $struct))
+ (type $child (struct_subtype (field (mut (ref null data))) (field (mut (ref null data))) $struct))
+
+ ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func_subtype (param (ref $struct) (ref $child)) func))
+
+ ;; CHECK: (func $update-null (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (local.get $child)
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (ref.null $child)
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $update-null (param $struct (ref $struct)) (param $child (ref $child))
+ ;; Update nulls in two fields that are separately optimized to separate
+ ;; values.
+ (drop
+ (struct.new $struct
+ (local.get $child)
+ (ref.null data)
+ )
+ )
+ (drop
+ (struct.new $struct
+ (ref.null data)
+ (local.get $struct)
+ )
+ )
+ )
+)