blob: a6cfb7ac571e7fc4e6480544bf035b7efdaf35d3 [file] [log] [blame] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s -all --dae -S -o - | filecheck %s
;; RUN: wasm-opt %s -all --dae --nominal -S -o - | filecheck %s --check-prefix NOMNL
(module
;; CHECK: (type ${} (struct ))
;; CHECK: (type ${i32} (struct (field i32)))
;; NOMNL: (type ${} (struct_subtype data))
;; NOMNL: (type ${i32} (struct_subtype (field i32) ${}))
(type ${i32} (struct_subtype (field i32) ${}))
(type ${} (struct))
;; CHECK: (type ${f64} (struct (field f64)))
;; CHECK: (type ${i32_i64} (struct (field i32) (field i64)))
;; NOMNL: (type ${f64} (struct_subtype (field f64) ${}))
;; NOMNL: (type ${i32_i64} (struct_subtype (field i32) (field i64) ${i32}))
(type ${i32_i64} (struct_subtype (field i32) (field i64) ${i32}))
(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
;; CHECK-NEXT: (call $various-params-no
;; CHECK-NEXT: (call $get_{})
;; CHECK-NEXT: (call $get_{i32})
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $various-params-no
;; CHECK-NEXT: (call $get_{i32})
;; CHECK-NEXT: (call $get_{f64})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $call-various-params-no (type $none_=>_none)
;; NOMNL-NEXT: (call $various-params-no
;; NOMNL-NEXT: (call $get_{})
;; NOMNL-NEXT: (call $get_{i32})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (call $various-params-no
;; NOMNL-NEXT: (call $get_{i32})
;; NOMNL-NEXT: (call $get_{f64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $call-various-params-no
;; The first argument gets {} and {i32}; the second {i32} and {f64}; none of
;; those pairs can be optimized. Note that we do not pass in all nulls, as
;; all nulls are identical and we could do other optimization work due to
;; that.
(call $various-params-no
(call $get_{})
(call $get_{i32})
)
(call $various-params-no
(call $get_{i32})
(call $get_{f64})
)
)
;; This function is called in ways that do not allow us to alter the types of
;; its parameters (see last function).
;; CHECK: (func $various-params-no (param $x (ref null ${})) (param $y (ref null ${}))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $various-params-no (type $ref?|${}|_ref?|${}|_=>_none) (param $x (ref null ${})) (param $y (ref null ${}))
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $x)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $y)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $various-params-no (param $x (ref null ${})) (param $y (ref null ${}))
;; "Use" the locals to avoid other optimizations kicking in.
(drop (local.get $x))
(drop (local.get $y))
)
;; CHECK: (func $get_{} (result (ref null ${}))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; NOMNL: (func $get_{} (type $none_=>_ref?|${}|) (result (ref null ${}))
;; NOMNL-NEXT: (unreachable)
;; NOMNL-NEXT: )
(func $get_{} (result (ref null ${}))
(unreachable)
)
;; CHECK: (func $get_{i32} (result (ref null ${i32}))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; NOMNL: (func $get_{i32} (type $none_=>_ref?|${i32}|) (result (ref null ${i32}))
;; NOMNL-NEXT: (unreachable)
;; NOMNL-NEXT: )
(func $get_{i32} (result (ref null ${i32}))
(unreachable)
)
;; CHECK: (func $get_{f64} (result (ref null ${f64}))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; NOMNL: (func $get_{f64} (type $none_=>_ref?|${f64}|) (result (ref null ${f64}))
;; NOMNL-NEXT: (unreachable)
;; NOMNL-NEXT: )
(func $get_{f64} (result (ref null ${f64}))
(unreachable)
)
;; CHECK: (func $call-various-params-yes
;; CHECK-NEXT: (call $various-params-yes
;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $various-params-yes
;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: (i32.const 1)
;; 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: (call $get_null_{i32})
;; NOMNL-NEXT: (i32.const 0)
;; NOMNL-NEXT: (call $get_null_{i32})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (call $various-params-yes
;; NOMNL-NEXT: (call $get_null_{i32})
;; NOMNL-NEXT: (i32.const 1)
;; NOMNL-NEXT: (call $get_null_{i32_i64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $call-various-params-yes
;; The first argument gets {i32} and {i32}; the second {i32} and {i32_i64};
;; 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
(call $get_null_{i32})
(i32.const 0)
(call $get_null_{i32})
)
(call $various-params-yes
(call $get_null_{i32})
(i32.const 1)
(call $get_null_{i32_i64})
)
)
;; This function is called in ways that *do* allow us to alter the types of
;; its parameters (see last function).
;; CHECK: (func $various-params-yes (param $x (ref null ${i32})) (param $i i32) (param $y (ref null ${i32}))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $various-params-yes (type $ref?|${i32}|_i32_ref?|${i32}|_=>_none) (param $x (ref null ${i32})) (param $i i32) (param $y (ref null ${i32}))
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $x)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $i)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $y)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $various-params-yes (param $x (ref null ${})) (param $i i32) (param $y (ref null ${}))
;; "Use" the locals to avoid other optimizations kicking in.
(drop (local.get $x))
(drop (local.get $i))
(drop (local.get $y))
)
;; CHECK: (func $call-various-params-set
;; CHECK-NEXT: (call $various-params-set
;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $various-params-set
;; 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: (call $get_null_{i32})
;; NOMNL-NEXT: (call $get_null_{i32})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (call $various-params-set
;; 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
(call $get_null_{i32})
(call $get_null_{i32})
)
(call $various-params-set
(call $get_null_{i32})
(call $get_null_{i32_i64})
)
)
;; This function is called in ways that *do* allow us to alter the types of
;; its parameters (see last function), however, we reuse the parameters by
;; writing to them, which causes problems in one case.
;; CHECK: (func $various-params-set (param $x (ref null ${i32})) (param $y (ref null ${i32}))
;; CHECK-NEXT: (local $2 (ref null ${}))
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (struct.new_default ${})
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $y
;; CHECK-NEXT: (call $get_null_{i32_i64})
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $various-params-set (type $ref?|${i32}|_ref?|${i32}|_=>_none) (param $x (ref null ${i32})) (param $y (ref null ${i32}))
;; NOMNL-NEXT: (local $2 (ref null ${}))
;; NOMNL-NEXT: (local.set $2
;; NOMNL-NEXT: (local.get $x)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (block
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $2)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $y)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (local.set $2
;; NOMNL-NEXT: (struct.new_default ${})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $2)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (local.set $y
;; NOMNL-NEXT: (call $get_null_{i32_i64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $y)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $various-params-set (param $x (ref null ${})) (param $y (ref null ${}))
;; "Use" the locals to avoid other optimizations kicking in.
(drop (local.get $x))
(drop (local.get $y))
;; Write to $x a value that will not fit in the refined type, which will
;; force us to do a fixup: the param will get the new type, and a new local
;; will stay at the old type, and we will use that local throughout the
;; function.
(local.set $x (struct.new ${}))
(drop
(local.get $x)
)
;; 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 (call $get_null_{i32_i64}))
(drop
(local.get $y)
)
)
;; CHECK: (func $call-various-params-tee
;; CHECK-NEXT: (call $various-params-tee
;; 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: (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
(call $get_null_{i32})
)
)
;; CHECK: (func $various-params-tee (param $x (ref null ${i32}))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref null ${i32}))
;; CHECK-NEXT: (local.tee $x
;; CHECK-NEXT: (call $get_null_{i32_i64})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $various-params-tee (type $ref?|${i32}|_=>_none) (param $x (ref null ${i32}))
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $x)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (block (result (ref null ${i32}))
;; NOMNL-NEXT: (local.tee $x
;; NOMNL-NEXT: (call $get_null_{i32_i64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $various-params-tee (param $x (ref null ${}))
;; "Use" the locals to avoid other optimizations kicking in.
(drop (local.get $x))
;; Write to $x in a way that allows us to make the type more specific. We
;; must also update the type of the tee (if we do not, a validation error
;; would occur), and that will also cause the block's type to update as well.
(drop
(block (result (ref null ${}))
(local.tee $x (call $get_null_{i32_i64}))
)
)
)
;; CHECK: (func $call-various-params-null
;; CHECK-NEXT: (call $various-params-null
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $get_null_{i32})
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $various-params-null
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $call-various-params-null (type $none_=>_none)
;; NOMNL-NEXT: (call $various-params-null
;; NOMNL-NEXT: (ref.as_non_null
;; NOMNL-NEXT: (ref.null none)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (call $get_null_{i32})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (call $various-params-null
;; NOMNL-NEXT: (ref.as_non_null
;; NOMNL-NEXT: (ref.null none)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (ref.as_non_null
;; NOMNL-NEXT: (ref.null none)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $call-various-params-null
;; The first argument gets non-null values, allowing us to refine it. The
;; second gets only one.
(call $various-params-null
(ref.as_non_null (ref.null ${i32}))
(call $get_null_{i32})
)
(call $various-params-null
(ref.as_non_null (ref.null ${i32}))
(ref.as_non_null (ref.null ${i32}))
)
)
;; This function is called in ways that allow us to make the first parameter
;; non-nullable.
;; CHECK: (func $various-params-null (param $x (ref none)) (param $y (ref null ${i32}))
;; CHECK-NEXT: (local $temp i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (local.get $temp)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $various-params-null (type $ref|none|_ref?|${i32}|_=>_none) (param $x (ref none)) (param $y (ref null ${i32}))
;; NOMNL-NEXT: (local $temp i32)
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $x)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $y)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (local.set $temp
;; NOMNL-NEXT: (local.get $temp)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $various-params-null (param $x (ref null ${})) (param $y (ref null ${}))
(local $temp i32)
;; "Use" the locals to avoid other optimizations kicking in.
(drop (local.get $x))
(drop (local.get $y))
;; Use a local in this function as well, which should be ignored by this pass
;; (when we scan and update all local.gets and sets, we should only do so on
;; parameters, and not vars - and we can crash if we scan/update things we
;; should not).
(local.set $temp (local.get $temp))
)
;; CHECK: (func $call-various-params-middle
;; CHECK-NEXT: (call $various-params-middle
;; CHECK-NEXT: (call $get_null_{i32_i64})
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $various-params-middle
;; 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: (call $get_null_{i32_i64})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (call $various-params-middle
;; 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
(call $get_null_{i32_i64})
)
(call $various-params-middle
(call $get_null_{i32_f32})
)
)
;; CHECK: (func $various-params-middle (param $x (ref null ${i32}))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $various-params-middle (type $ref?|${i32}|_=>_none) (param $x (ref null ${i32}))
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (local.get $x)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $various-params-middle (param $x (ref null ${}))
;; "Use" the local to avoid other optimizations kicking in.
(drop (local.get $x))
)
;; CHECK: (func $unused-and-refinable
;; CHECK-NEXT: (local $0 dataref)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
;; NOMNL: (func $unused-and-refinable (type $none_=>_none)
;; NOMNL-NEXT: (local $0 dataref)
;; NOMNL-NEXT: (nop)
;; NOMNL-NEXT: )
(func $unused-and-refinable (param $0 dataref)
;; This function does not use $0. It is called with ${}, so it is also
;; a parameter whose type we can refine. Do not do both operations: instead,
;; just remove it because it is ignored, without altering the type (handling
;; both operations would introduce some corner cases, and it just isn't worth
;; handling them if the param is completely unused anyhow). We should see in
;; the test output that the local $0 (the unused param) becomes a local
;; because it is unused, and that local does *not* have its type refined to
;; ${} (it will however be changed to be nullable, which it must be as a
;; local).
)
;; CHECK: (func $call-unused-and-refinable
;; CHECK-NEXT: (call $unused-and-refinable)
;; CHECK-NEXT: )
;; NOMNL: (func $call-unused-and-refinable (type $none_=>_none)
;; NOMNL-NEXT: (call $unused-and-refinable)
;; NOMNL-NEXT: )
(func $call-unused-and-refinable
(call $unused-and-refinable
(struct.new_default ${})
)
)
;; CHECK: (func $non-nullable-fixup (param $0 (ref ${}))
;; CHECK-NEXT: (local $1 dataref)
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $non-nullable-fixup (type $ref|${}|_=>_none) (param $0 (ref ${}))
;; NOMNL-NEXT: (local $1 dataref)
;; NOMNL-NEXT: (local.set $1
;; NOMNL-NEXT: (local.get $0)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (local.set $1
;; NOMNL-NEXT: (local.get $1)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $non-nullable-fixup (param $0 dataref)
;; Use the param to avoid other opts removing it, and to force us to do a
;; fixup when we refine the param's type. When doing so, we must handle the
;; fact that the new local's type is non-nullable.
(local.set $0
(local.get $0)
)
)
;; CHECK: (func $call-non-nullable-fixup
;; CHECK-NEXT: (call $non-nullable-fixup
;; CHECK-NEXT: (struct.new_default ${})
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $call-non-nullable-fixup (type $none_=>_none)
;; NOMNL-NEXT: (call $non-nullable-fixup
;; NOMNL-NEXT: (struct.new_default ${})
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $call-non-nullable-fixup
(call $non-nullable-fixup
(struct.new_default ${})
)
)
;; CHECK: (func $call-update-null
;; CHECK-NEXT: (call $update-null
;; CHECK-NEXT: (ref.null none)
;; 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 none)
;; 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 none)
;; CHECK-NEXT: )
;; NOMNL: (func $get_null_{i32} (type $none_=>_ref?|${i32}|) (result (ref null ${i32}))
;; NOMNL-NEXT: (ref.null none)
;; 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 none)
;; CHECK-NEXT: )
;; NOMNL: (func $get_null_{i32_i64} (type $none_=>_ref?|${i32_i64}|) (result (ref null ${i32_i64}))
;; NOMNL-NEXT: (ref.null none)
;; 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 none)
;; CHECK-NEXT: )
;; NOMNL: (func $get_null_{i32_f32} (type $none_=>_ref?|${i32_f32}|) (result (ref null ${i32_f32}))
;; NOMNL-NEXT: (ref.null none)
;; NOMNL-NEXT: )
(func $get_null_{i32_f32} (result (ref null ${i32_f32}))
(ref.null ${i32_f32})
)
)