summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/lit/passes/dae-gc-refine-params.wast271
-rw-r--r--test/lit/passes/dae-gc-refine-return.wast385
-rw-r--r--test/lit/passes/dae-gc.wast633
3 files changed, 656 insertions, 633 deletions
diff --git a/test/lit/passes/dae-gc-refine-params.wast b/test/lit/passes/dae-gc-refine-params.wast
new file mode 100644
index 000000000..47ae2d2e5
--- /dev/null
+++ b/test/lit/passes/dae-gc-refine-params.wast
@@ -0,0 +1,271 @@
+;; 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 ${i32} (struct (field i32)))
+ (type ${i32} (struct (field i32)))
+
+ ;; CHECK: (type ${} (struct ))
+ (type ${} (struct))
+
+ ;; CHECK: (type ${i32_i64} (struct (field i32) (field i64)))
+ (type ${i32_i64} (struct (field i32) (field i64)))
+
+ ;; CHECK: (type ${f64} (struct (field f64)))
+ (type ${f64} (struct (field f64)))
+
+ ;; CHECK: (type ${i32_f32} (struct (field i32) (field f32)))
+ (type ${i32_f32} (struct (field i32) (field f32)))
+
+ ;; CHECK: (func $call-various-params-no
+ ;; CHECK-NEXT: (call $various-params-no
+ ;; CHECK-NEXT: (ref.null ${})
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $various-params-no
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: (ref.null ${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.
+ (call $various-params-no
+ (ref.null ${})
+ (ref.null ${i32})
+ )
+ (call $various-params-no
+ (ref.null ${i32})
+ (ref.null ${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: )
+ (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 $call-various-params-yes
+ ;; CHECK-NEXT: (call $various-params-yes
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $various-params-yes
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (ref.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
+ (ref.null ${i32})
+ (i32.const 0)
+ (ref.null ${i32})
+ )
+ (call $various-params-yes
+ (ref.null ${i32})
+ (i32.const 1)
+ (ref.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: )
+ (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: (ref.null ${i32})
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $various-params-set
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: (ref.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
+ (ref.null ${i32})
+ (ref.null ${i32})
+ )
+ (call $various-params-set
+ (ref.null ${i32})
+ (ref.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 ${})) (param $y (ref null ${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 $x
+ ;; CHECK-NEXT: (ref.null ${})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $y
+ ;; CHECK-NEXT: (ref.null ${i32_i64})
+ ;; 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 in a way that prevents us making the type more specific.
+ (local.set $x (ref.null ${}))
+ ;; Write to $y in a way that still allows us to make the type more specific.
+ (local.set $y (ref.null ${i32_i64}))
+ )
+
+ ;; CHECK: (func $call-various-params-tee
+ ;; CHECK-NEXT: (call $various-params-tee
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-various-params-tee
+ ;; The argument gets {i32}, which allows us to refine.
+ (call $various-params-tee
+ (ref.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 $block (result (ref null ${i32}))
+ ;; CHECK-NEXT: (local.tee $x
+ ;; CHECK-NEXT: (ref.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 (ref.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 ${i32})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $various-params-null
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.null ${i32})
+ ;; 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}))
+ (ref.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 ${i32})) (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
+ ;; CHECK-NEXT: (call $various-params-middle
+ ;; CHECK-NEXT: (ref.null ${i32_i64})
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $various-params-middle
+ ;; CHECK-NEXT: (ref.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
+ (ref.null ${i32_i64})
+ )
+ (call $various-params-middle
+ (ref.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: )
+ (func $various-params-middle (param $x (ref null ${}))
+ ;; "Use" the local to avoid other optimizations kicking in.
+ (drop (local.get $x))
+ )
+)
diff --git a/test/lit/passes/dae-gc-refine-return.wast b/test/lit/passes/dae-gc-refine-return.wast
new file mode 100644
index 000000000..9f945a5b3
--- /dev/null
+++ b/test/lit/passes/dae-gc-refine-return.wast
@@ -0,0 +1,385 @@
+;; 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 $return_{} (func (result (ref ${}))))
+ (type $return_{} (func (result (ref ${}))))
+
+ ;; CHECK: (type ${i32_f32} (struct (field i32) (field f32)))
+ (type ${i32_f32} (struct (field i32) (field f32)))
+
+ ;; CHECK: (type ${i32_i64} (struct (field i32) (field i64)))
+ (type ${i32_i64} (struct (field i32) (field i64)))
+
+ ;; CHECK: (type ${i32} (struct (field i32)))
+ (type ${i32} (struct (field i32)))
+
+ ;; CHECK: (type ${} (struct ))
+ (type ${} (struct))
+
+ (table 1 1 funcref)
+
+ ;; We cannot refine the return type if nothing is actually returned.
+ ;; CHECK: (func $refine-return-no-return (result anyref)
+ ;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local.set $temp
+ ;; CHECK-NEXT: (call $refine-return-no-return)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $refine-return-no-return (result anyref)
+ ;; Call this function, so that we attempt to optimize it. Note that we do not
+ ;; just drop the result, as that would cause the drop optimizations to kick
+ ;; in.
+ (local $temp anyref)
+ (local.set $temp (call $refine-return-no-return))
+
+ (unreachable)
+ )
+
+ ;; 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.set $temp
+ ;; CHECK-NEXT: (call $refine-return-no-refining)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: )
+ (func $refine-return-no-refining (result anyref)
+ (local $temp anyref)
+ (local.set $temp (call $refine-return-no-refining))
+
+ (ref.null 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.set $temp
+ ;; CHECK-NEXT: (call $refine-return-flow)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: )
+ (func $refine-return-flow (result anyref)
+ (local $temp anyref)
+ (local.set $temp (call $refine-return-flow))
+
+ (ref.null func)
+ )
+ ;; CHECK: (func $call-refine-return-flow (result funcref)
+ ;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local.set $temp
+ ;; CHECK-NEXT: (call $call-refine-return-flow)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if (result funcref)
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (call $refine-return-flow)
+ ;; CHECK-NEXT: (call $refine-return-flow)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-refine-return-flow (result anyref)
+ (local $temp anyref)
+ (local.set $temp (call $call-refine-return-flow))
+
+ ;; After refining the return value of the above function, refinalize will
+ ;; update types here, which will lead to updating the if, and then the entire
+ ;; function's return value.
+ (if (result anyref)
+ (i32.const 1)
+ (call $refine-return-flow)
+ (call $refine-return-flow)
+ )
+ )
+
+ ;; Refine the return type based on a return.
+ ;; CHECK: (func $refine-return-return (result funcref)
+ ;; CHECK-NEXT: (local $temp anyref)
+ ;; CHECK-NEXT: (local.set $temp
+ ;; CHECK-NEXT: (call $refine-return-return)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $refine-return-return (result anyref)
+ (local $temp anyref)
+ (local.set $temp (call $refine-return-return))
+
+ (return (ref.null func))
+ )
+
+ ;; Refine the return type based on multiple values.
+ ;; CHECK: (func $refine-return-many (result funcref)
+ ;; CHECK-NEXT: (local $temp anyref)
+ ;; 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: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: )
+ (func $refine-return-many (result anyref)
+ (local $temp anyref)
+ (local.set $temp (call $refine-return-many))
+
+ (if
+ (i32.const 1)
+ (return (ref.null func))
+ )
+ (if
+ (i32.const 2)
+ (return (ref.null func))
+ )
+ (ref.null func)
+ )
+
+ ;; CHECK: (func $refine-return-many-blocked (result anyref)
+ ;; CHECK-NEXT: (local $temp anyref)
+ ;; 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: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (ref.null data)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: )
+ (func $refine-return-many-blocked (result anyref)
+ (local $temp anyref)
+ (local.set $temp (call $refine-return-many-blocked))
+
+ (if
+ (i32.const 1)
+ (return (ref.null func))
+ )
+ (if
+ (i32.const 2)
+ ;; The refined return value is blocked by this return.
+ (return (ref.null data))
+ )
+ (ref.null func)
+ )
+
+ ;; CHECK: (func $refine-return-many-blocked-2 (result anyref)
+ ;; CHECK-NEXT: (local $temp anyref)
+ ;; 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: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null data)
+ ;; CHECK-NEXT: )
+ (func $refine-return-many-blocked-2 (result anyref)
+ (local $temp anyref)
+ (local.set $temp (call $refine-return-many-blocked-2))
+
+ (if
+ (i32.const 1)
+ (return (ref.null func))
+ )
+ (if
+ (i32.const 2)
+ (return (ref.null func))
+ )
+ ;; The refined return value is blocked by this value.
+ (ref.null data)
+ )
+
+ ;; CHECK: (func $refine-return-many-middle (result (ref null ${i32}))
+ ;; CHECK-NEXT: (local $temp anyref)
+ ;; 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: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null ${i32_f32})
+ ;; CHECK-NEXT: )
+ (func $refine-return-many-middle (result anyref)
+ (local $temp anyref)
+ (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}))
+ )
+ (ref.null ${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.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: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $refine-return-tuple (result anyref i32)
+ (local $temp anyref)
+ (local.set $temp
+ (tuple.extract 0
+ (call $refine-return-tuple)
+ )
+ )
+
+ (tuple.make
+ (ref.null func)
+ (i32.const 1)
+ )
+ )
+
+ ;; This function does a return call of the one after it. The one after it
+ ;; returns a ref.func of this one. They both begin by returning a funcref;
+ ;; after refining the return type of the second function, it will have a more
+ ;; specific type (which is ok as subtyping is allowed with tail calls).
+ ;; CHECK: (func $do-return-call (result funcref)
+ ;; CHECK-NEXT: (return_call $return-ref-func)
+ ;; CHECK-NEXT: )
+ (func $do-return-call (result funcref)
+ (return_call $return-ref-func)
+ )
+ ;; CHECK: (func $return-ref-func (result (ref $none_=>_funcref))
+ ;; CHECK-NEXT: (ref.func $do-return-call)
+ ;; CHECK-NEXT: )
+ (func $return-ref-func (result funcref)
+ (ref.func $do-return-call)
+ )
+
+ ;; Show that we can optimize the return type of a function that does a tail
+ ;; call.
+ ;; CHECK: (func $tail-callee (result (ref ${}))
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $tail-callee (result (ref ${}))
+ (unreachable)
+ )
+ ;; CHECK: (func $tail-caller-yes (result (ref ${}))
+ ;; CHECK-NEXT: (return_call $tail-callee)
+ ;; CHECK-NEXT: )
+ (func $tail-caller-yes (result anyref)
+ ;; This function's return type can be refined because of this call, whose
+ ;; target's return type is more specific than anyref.
+ (return_call $tail-callee)
+ )
+ ;; CHECK: (func $tail-caller-no (result anyref)
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return_call $tail-callee)
+ ;; CHECK-NEXT: )
+ (func $tail-caller-no (result 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_call $tail-callee)
+ )
+ ;; CHECK: (func $tail-call-caller
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $tail-caller-yes)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $tail-caller-no)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $tail-call-caller
+ ;; Call the functions to cause optimization to happen.
+ (drop
+ (call $tail-caller-yes)
+ )
+ (drop
+ (call $tail-caller-no)
+ )
+ )
+
+ ;; As above, but with an indirect tail call.
+ ;; CHECK: (func $tail-callee-indirect (result (ref ${}))
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $tail-callee-indirect (result (ref ${}))
+ (unreachable)
+ )
+ ;; CHECK: (func $tail-caller-indirect-yes (result (ref ${}))
+ ;; CHECK-NEXT: (return_call_indirect $0 (type $return_{})
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $tail-caller-indirect-yes (result anyref)
+ (return_call_indirect (type $return_{}) (i32.const 0))
+ )
+ ;; CHECK: (func $tail-caller-indirect-no (result anyref)
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return_call_indirect $0 (type $return_{})
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $tail-caller-indirect-no (result anyref)
+ (if (i32.const 1)
+ (return (ref.null any))
+ )
+ (return_call_indirect (type $return_{}) (i32.const 0))
+ )
+ ;; CHECK: (func $tail-call-caller-indirect
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $tail-caller-indirect-yes)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $tail-caller-indirect-no)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $tail-call-caller-indirect
+ (drop
+ (call $tail-caller-indirect-yes)
+ )
+ (drop
+ (call $tail-caller-indirect-no)
+ )
+ )
+)
diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast
index 07bc84f5e..65d65d4c9 100644
--- a/test/lit/passes/dae-gc.wast
+++ b/test/lit/passes/dae-gc.wast
@@ -2,26 +2,9 @@
;; RUN: wasm-opt %s -all --dae -S -o - | filecheck %s
(module
- ;; CHECK: (type ${i32} (struct (field i32)))
- (type ${i32} (struct (field i32)))
-
;; CHECK: (type ${} (struct ))
(type ${} (struct))
- ;; CHECK: (type ${i32_i64} (struct (field i32) (field i64)))
- (type ${i32_i64} (struct (field i32) (field i64)))
-
- ;; CHECK: (type $return_{} (func (result (ref ${}))))
- (type $return_{} (func (result (ref ${}))))
-
- ;; CHECK: (type ${i32_f32} (struct (field i32) (field f32)))
- (type ${i32_f32} (struct (field i32) (field f32)))
-
- ;; CHECK: (type ${f64} (struct (field f64)))
- (type ${f64} (struct (field f64)))
-
- (table 1 1 funcref)
-
;; CHECK: (func $foo
;; CHECK-NEXT: (call $bar)
;; CHECK-NEXT: )
@@ -84,620 +67,4 @@
(rtt.canon ${})
)
)
-
- ;; CHECK: (func $call-various-params-no
- ;; CHECK-NEXT: (call $various-params-no
- ;; CHECK-NEXT: (ref.null ${})
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (call $various-params-no
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: (ref.null ${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.
- (call $various-params-no
- (ref.null ${})
- (ref.null ${i32})
- )
- (call $various-params-no
- (ref.null ${i32})
- (ref.null ${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: )
- (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 $call-various-params-yes
- ;; CHECK-NEXT: (call $various-params-yes
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (call $various-params-yes
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (ref.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
- (ref.null ${i32})
- (i32.const 0)
- (ref.null ${i32})
- )
- (call $various-params-yes
- (ref.null ${i32})
- (i32.const 1)
- (ref.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: )
- (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: (ref.null ${i32})
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (call $various-params-set
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: (ref.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
- (ref.null ${i32})
- (ref.null ${i32})
- )
- (call $various-params-set
- (ref.null ${i32})
- (ref.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 ${})) (param $y (ref null ${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 $x
- ;; CHECK-NEXT: (ref.null ${})
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (local.set $y
- ;; CHECK-NEXT: (ref.null ${i32_i64})
- ;; 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 in a way that prevents us making the type more specific.
- (local.set $x (ref.null ${}))
- ;; Write to $y in a way that still allows us to make the type more specific.
- (local.set $y (ref.null ${i32_i64}))
- )
-
- ;; CHECK: (func $call-various-params-tee
- ;; CHECK-NEXT: (call $various-params-tee
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $call-various-params-tee
- ;; The argument gets {i32}, which allows us to refine.
- (call $various-params-tee
- (ref.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 $block (result (ref null ${i32}))
- ;; CHECK-NEXT: (local.tee $x
- ;; CHECK-NEXT: (ref.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 (ref.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 ${i32})
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (call $various-params-null
- ;; CHECK-NEXT: (ref.as_non_null
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.as_non_null
- ;; CHECK-NEXT: (ref.null ${i32})
- ;; 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}))
- (ref.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 ${i32})) (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
- ;; CHECK-NEXT: (call $various-params-middle
- ;; CHECK-NEXT: (ref.null ${i32_i64})
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (call $various-params-middle
- ;; CHECK-NEXT: (ref.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
- (ref.null ${i32_i64})
- )
- (call $various-params-middle
- (ref.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: )
- (func $various-params-middle (param $x (ref null ${}))
- ;; "Use" the local to avoid other optimizations kicking in.
- (drop (local.get $x))
- )
-
- ;; We cannot refine the return type if nothing is actually returned.
- ;; CHECK: (func $refine-return-no-return (result anyref)
- ;; CHECK-NEXT: (local $temp anyref)
- ;; CHECK-NEXT: (local.set $temp
- ;; CHECK-NEXT: (call $refine-return-no-return)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (unreachable)
- ;; CHECK-NEXT: )
- (func $refine-return-no-return (result anyref)
- ;; Call this function, so that we attempt to optimize it. Note that we do not
- ;; just drop the result, as that would cause the drop optimizations to kick
- ;; in.
- (local $temp anyref)
- (local.set $temp (call $refine-return-no-return))
-
- (unreachable)
- )
-
- ;; 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.set $temp
- ;; CHECK-NEXT: (call $refine-return-no-refining)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null any)
- ;; CHECK-NEXT: )
- (func $refine-return-no-refining (result anyref)
- (local $temp anyref)
- (local.set $temp (call $refine-return-no-refining))
-
- (ref.null 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.set $temp
- ;; CHECK-NEXT: (call $refine-return-flow)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null func)
- ;; CHECK-NEXT: )
- (func $refine-return-flow (result anyref)
- (local $temp anyref)
- (local.set $temp (call $refine-return-flow))
-
- (ref.null func)
- )
- ;; CHECK: (func $call-refine-return-flow (result funcref)
- ;; CHECK-NEXT: (local $temp anyref)
- ;; CHECK-NEXT: (local.set $temp
- ;; CHECK-NEXT: (call $call-refine-return-flow)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (if (result funcref)
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (call $refine-return-flow)
- ;; CHECK-NEXT: (call $refine-return-flow)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $call-refine-return-flow (result anyref)
- (local $temp anyref)
- (local.set $temp (call $call-refine-return-flow))
-
- ;; After refining the return value of the above function, refinalize will
- ;; update types here, which will lead to updating the if, and then the entire
- ;; function's return value.
- (if (result anyref)
- (i32.const 1)
- (call $refine-return-flow)
- (call $refine-return-flow)
- )
- )
-
- ;; Refine the return type based on a return.
- ;; CHECK: (func $refine-return-return (result funcref)
- ;; CHECK-NEXT: (local $temp anyref)
- ;; CHECK-NEXT: (local.set $temp
- ;; CHECK-NEXT: (call $refine-return-return)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null func)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $refine-return-return (result anyref)
- (local $temp anyref)
- (local.set $temp (call $refine-return-return))
-
- (return (ref.null func))
- )
-
- ;; Refine the return type based on multiple values.
- ;; CHECK: (func $refine-return-many (result funcref)
- ;; CHECK-NEXT: (local $temp anyref)
- ;; 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: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (if
- ;; CHECK-NEXT: (i32.const 2)
- ;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null func)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null func)
- ;; CHECK-NEXT: )
- (func $refine-return-many (result anyref)
- (local $temp anyref)
- (local.set $temp (call $refine-return-many))
-
- (if
- (i32.const 1)
- (return (ref.null func))
- )
- (if
- (i32.const 2)
- (return (ref.null func))
- )
- (ref.null func)
- )
-
- ;; CHECK: (func $refine-return-many-blocked (result anyref)
- ;; CHECK-NEXT: (local $temp anyref)
- ;; 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: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (if
- ;; CHECK-NEXT: (i32.const 2)
- ;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null data)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null func)
- ;; CHECK-NEXT: )
- (func $refine-return-many-blocked (result anyref)
- (local $temp anyref)
- (local.set $temp (call $refine-return-many-blocked))
-
- (if
- (i32.const 1)
- (return (ref.null func))
- )
- (if
- (i32.const 2)
- ;; The refined return value is blocked by this return.
- (return (ref.null data))
- )
- (ref.null func)
- )
-
- ;; CHECK: (func $refine-return-many-blocked-2 (result anyref)
- ;; CHECK-NEXT: (local $temp anyref)
- ;; 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: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (if
- ;; CHECK-NEXT: (i32.const 2)
- ;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null func)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null data)
- ;; CHECK-NEXT: )
- (func $refine-return-many-blocked-2 (result anyref)
- (local $temp anyref)
- (local.set $temp (call $refine-return-many-blocked-2))
-
- (if
- (i32.const 1)
- (return (ref.null func))
- )
- (if
- (i32.const 2)
- (return (ref.null func))
- )
- ;; The refined return value is blocked by this value.
- (ref.null data)
- )
-
- ;; CHECK: (func $refine-return-many-middle (result (ref null ${i32}))
- ;; CHECK-NEXT: (local $temp anyref)
- ;; 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: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null ${i32_f32})
- ;; CHECK-NEXT: )
- (func $refine-return-many-middle (result anyref)
- (local $temp anyref)
- (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}))
- )
- (ref.null ${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.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: (i32.const 1)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $refine-return-tuple (result anyref i32)
- (local $temp anyref)
- (local.set $temp
- (tuple.extract 0
- (call $refine-return-tuple)
- )
- )
-
- (tuple.make
- (ref.null func)
- (i32.const 1)
- )
- )
-
- ;; This function does a return call of the one after it. The one after it
- ;; returns a ref.func of this one. They both begin by returning a funcref;
- ;; after refining the return type of the second function, it will have a more
- ;; specific type (which is ok as subtyping is allowed with tail calls).
- ;; CHECK: (func $do-return-call (result funcref)
- ;; CHECK-NEXT: (return_call $return-ref-func)
- ;; CHECK-NEXT: )
- (func $do-return-call (result funcref)
- (return_call $return-ref-func)
- )
- ;; CHECK: (func $return-ref-func (result (ref $none_=>_funcref))
- ;; CHECK-NEXT: (ref.func $do-return-call)
- ;; CHECK-NEXT: )
- (func $return-ref-func (result funcref)
- (ref.func $do-return-call)
- )
-
- ;; Show that we can optimize the return type of a function that does a tail
- ;; call.
- ;; CHECK: (func $tail-callee (result (ref ${}))
- ;; CHECK-NEXT: (unreachable)
- ;; CHECK-NEXT: )
- (func $tail-callee (result (ref ${}))
- (unreachable)
- )
- ;; CHECK: (func $tail-caller-yes (result (ref ${}))
- ;; CHECK-NEXT: (return_call $tail-callee)
- ;; CHECK-NEXT: )
- (func $tail-caller-yes (result anyref)
- ;; This function's return type can be refined because of this call, whose
- ;; target's return type is more specific than anyref.
- (return_call $tail-callee)
- )
- ;; CHECK: (func $tail-caller-no (result anyref)
- ;; CHECK-NEXT: (if
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null any)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (return_call $tail-callee)
- ;; CHECK-NEXT: )
- (func $tail-caller-no (result 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_call $tail-callee)
- )
- ;; CHECK: (func $tail-call-caller
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (call $tail-caller-yes)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (call $tail-caller-no)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $tail-call-caller
- ;; Call the functions to cause optimization to happen.
- (drop
- (call $tail-caller-yes)
- )
- (drop
- (call $tail-caller-no)
- )
- )
-
- ;; As above, but with an indirect tail call.
- ;; CHECK: (func $tail-callee-indirect (result (ref ${}))
- ;; CHECK-NEXT: (unreachable)
- ;; CHECK-NEXT: )
- (func $tail-callee-indirect (result (ref ${}))
- (unreachable)
- )
- ;; CHECK: (func $tail-caller-indirect-yes (result (ref ${}))
- ;; CHECK-NEXT: (return_call_indirect $0 (type $return_{})
- ;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $tail-caller-indirect-yes (result anyref)
- (return_call_indirect (type $return_{}) (i32.const 0))
- )
- ;; CHECK: (func $tail-caller-indirect-no (result anyref)
- ;; CHECK-NEXT: (if
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (return
- ;; CHECK-NEXT: (ref.null any)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (return_call_indirect $0 (type $return_{})
- ;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $tail-caller-indirect-no (result anyref)
- (if (i32.const 1)
- (return (ref.null any))
- )
- (return_call_indirect (type $return_{}) (i32.const 0))
- )
- ;; CHECK: (func $tail-call-caller-indirect
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (call $tail-caller-indirect-yes)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (call $tail-caller-indirect-no)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $tail-call-caller-indirect
- (drop
- (call $tail-caller-indirect-yes)
- )
- (drop
- (call $tail-caller-indirect-no)
- )
- )
)