blob: 27f9a214d11a0cf6160f69158918a4143029d6b6 [file] [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
(module
;; CHECK: (type $"{}" (sub (struct)))
(type $"{}" (sub (struct)))
;; CHECK: (type $"{i32}" (sub $"{}" (struct (field i32))))
(type $"{i32}" (sub $"{}" (struct (field i32))))
;; CHECK: (type $"{i32_i64}" (sub $"{i32}" (struct (field i32) (field i64))))
;; CHECK: (type $"{i32_f32}" (sub $"{i32}" (struct (field i32) (field f32))))
;; CHECK: (type $"{f64}" (sub $"{}" (struct (field f64))))
(type $"{f64}" (sub $"{}" (struct (field f64))))
(type $"{i32_i64}" (sub $"{i32}" (struct (field i32) (field i64))))
(type $"{i32_f32}" (sub $"{i32}" (struct (field i32) (field f32))))
;; CHECK: (func $call-various-params-no (type $2)
;; 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: )
(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 (type $8) (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: )
(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_{}" (type $9) (result (ref null $"{}"))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $"get_{}" (result (ref null $"{}"))
(unreachable)
)
;; CHECK: (func $"get_{i32}" (type $5) (result (ref null $"{i32}"))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $"get_{i32}" (result (ref null $"{i32}"))
(unreachable)
)
;; CHECK: (func $"get_{f64}" (type $10) (result (ref null $"{f64}"))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $"get_{f64}" (result (ref null $"{f64}"))
(unreachable)
)
;; CHECK: (func $call-various-params-yes (type $2)
;; 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: )
(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 (type $11) (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: )
(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 (type $2)
;; 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: )
(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 (type $12) (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: )
(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 (type $2)
;; CHECK-NEXT: (call $various-params-tee
;; CHECK-NEXT: (call $"get_null_{i32}")
;; CHECK-NEXT: )
;; CHECK-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 (type $6) (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: )
(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 (type $2)
;; 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: )
(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 (type $13) (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: )
(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 (type $2)
;; 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: )
(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 (type $6) (param $x (ref null $"{i32}"))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-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 (type $2)
;; CHECK-NEXT: (local $0 structref)
;; CHECK-NEXT: )
(func $unused-and-refinable (param $0 structref)
;; 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 (type $2)
;; CHECK-NEXT: (call $unused-and-refinable)
;; CHECK-NEXT: )
(func $call-unused-and-refinable
(call $unused-and-refinable
(struct.new_default $"{}")
)
)
;; CHECK: (func $non-nullable-fixup (type $14) (param $0 (ref (exact $"{}")))
;; CHECK-NEXT: (local $1 structref)
;; 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: )
(func $non-nullable-fixup (param $0 structref)
;; 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 (type $2)
;; CHECK-NEXT: (call $non-nullable-fixup
;; CHECK-NEXT: (struct.new_default $"{}")
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $call-non-nullable-fixup
(call $non-nullable-fixup
(struct.new_default $"{}")
)
)
;; CHECK: (func $call-update-null (type $2)
;; 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: )
(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 (type $15) (param $x (ref null (exact $"{}")))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-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}" (type $5) (result (ref null $"{i32}"))
;; CHECK-NEXT: (select (result (ref null $"{i32}"))
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: (select (result (ref $"{i32}"))
;; CHECK-NEXT: (struct.new_default $"{i32}")
;; CHECK-NEXT: (struct.new_default $"{i32_i64}")
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-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. Use two
;; selects to create a return type that cannot be improved to be non-null, a
;; subtype, or exact.
(select (result (ref null $"{i32}"))
(ref.null none)
(select (result (ref $"{i32}"))
(struct.new_default $"{i32}")
(struct.new_default $"{i32_i64}")
(i32.const 0)
)
(i32.const 0)
)
)
;; CHECK: (func $"get_null_{i32_i64}" (type $16) (result (ref null (exact $"{i32_i64}")))
;; CHECK-NEXT: (select (result (ref null (exact $"{i32_i64}")))
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: (struct.new_default $"{i32_i64}")
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $"get_null_{i32_i64}" (result (ref null $"{i32_i64}"))
(select
(ref.null none)
(struct.new_default $"{i32_i64}")
(i32.const 0)
)
)
;; CHECK: (func $"get_null_{i32_f32}" (type $17) (result (ref null (exact $"{i32_f32}")))
;; CHECK-NEXT: (select (result (ref null (exact $"{i32_f32}")))
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: (struct.new_default $"{i32_f32}")
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $"get_null_{i32_f32}" (result (ref null $"{i32_f32}"))
(select
(ref.null none)
(struct.new_default $"{i32_f32}")
(i32.const 0)
)
)
)