diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/lit/passes/dae-gc-refine-params.wast | 271 | ||||
-rw-r--r-- | test/lit/passes/dae-gc-refine-return.wast | 385 | ||||
-rw-r--r-- | test/lit/passes/dae-gc.wast | 633 |
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) - ) - ) ) |