| ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. |
| ;; Test converting calls in tail position to return calls. |
| |
| ;; RUN: wasm-opt %s --tail-call --enable-tail-call --enable-gc --enable-reference-types --enable-multivalue -S -o - | filecheck %s |
| |
| (module |
| ;; CHECK: (type $none-to-i32 (func (result i32))) |
| |
| ;; CHECK: (type $i32-to-none (func (param i32))) |
| |
| ;; CHECK: (type $none-to-none (func)) |
| (type $none-to-none (func)) |
| (type $none-to-i32 (func (result i32))) |
| (type $i32-to-none (func (param i32))) |
| ;; CHECK: (type $struct (sub (struct))) |
| (type $struct (sub (struct))) |
| ;; CHECK: (import "binaryen-intrinsics" "call.without.effects" (func $cwe (type $6) (param i32 funcref) (result i32))) |
| (import "binaryen-intrinsics" "call.without.effects" (func $cwe (param i32) (param funcref) (result i32))) |
| ;; CHECK: (global $g (mut i32) (i32.const 0)) |
| (global $g (mut i32) (i32.const 0)) |
| ;; CHECK: (table $table 1 funcref) |
| (table $table 1 funcref) |
| |
| ;; CHECK: (func $void-callee (type $none-to-none) |
| ;; CHECK-NEXT: ) |
| (func $void-callee) |
| ;; CHECK: (func $void-arg-callee (type $i32-to-none) (param $x i32) |
| ;; CHECK-NEXT: ) |
| (func $void-arg-callee (param $x i32)) |
| ;; CHECK: (func $i32-to-i32-callee (type $2) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| (func $i32-to-i32-callee (param $x i32) (result i32) |
| (local.get $x) |
| ) |
| ;; CHECK: (func $value-callee (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| (func $value-callee (result i32) |
| (i32.const 1) |
| ) |
| ;; CHECK: (func $ref-callee (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (i32.const 2) |
| ;; CHECK-NEXT: ) |
| (func $ref-callee (result i32) |
| (i32.const 2) |
| ) |
| ;; CHECK: (func $param-callee (type $7) (param $x i32) (param $y i32) (result i32) |
| ;; CHECK-NEXT: (i32.add |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $param-callee (param $x i32) (param $y i32) (result i32) |
| (i32.add |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| ;; CHECK: (func $subtype-callee (type $8) (result (ref $struct)) |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| (func $subtype-callee (result (ref $struct)) |
| (struct.new_default $struct) |
| ) |
| ;; CHECK: (func $multivalue-callee (type $5) (result i32 i64) |
| ;; CHECK-NEXT: (tuple.make 2 |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: (i64.const 2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $multivalue-callee (result i32 i64) |
| (tuple.make 2 |
| (i32.const 1) |
| (i64.const 2) |
| ) |
| ) |
| (elem (i32.const 0) $value-callee) |
| |
| ;; CHECK: (func $direct-void (type $none-to-none) |
| ;; CHECK-NEXT: (return_call $void-callee) |
| ;; CHECK-NEXT: ) |
| (func $direct-void |
| ;; A direct void call at the end of a function becomes a return_call. |
| (call $void-callee) |
| ) |
| |
| ;; CHECK: (func $direct-void-ret (type $none-to-none) |
| ;; CHECK-NEXT: (return_call $void-callee) |
| ;; CHECK-NEXT: (return) |
| ;; CHECK-NEXT: ) |
| (func $direct-void-ret |
| ;; A void call immediately preceding a return becomes a return_call. |
| (call $void-callee) |
| (return) |
| ) |
| |
| ;; CHECK: (func $direct-value (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (return_call $value-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $direct-value (result i32) |
| ;; A return wrapping a call becomes a return_call. |
| (return |
| (call $value-callee) |
| ) |
| ) |
| |
| ;; CHECK: (func $call-with-params (type $2) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (return_call $param-callee |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (global.set $g |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $value-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $call-with-params (param $x i32) (result i32) |
| ;; Calls with parameters, including side-effecting parameter expressions, |
| ;; are still converted to return_call because operands are evaluated before |
| ;; the call transfers control. |
| (return |
| (call $param-callee |
| (local.get $x) |
| (block (result i32) |
| (global.set $g |
| (i32.const 1) |
| ) |
| (call $value-callee) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $subtype-return (type $9) (result anyref) |
| ;; CHECK-NEXT: (return_call $subtype-callee) |
| ;; CHECK-NEXT: ) |
| (func $subtype-return (result anyref) |
| ;; A call returning a subtype of the enclosing function's result type can be |
| ;; converted to return_call. |
| (call $subtype-callee) |
| ) |
| |
| ;; CHECK: (func $multivalue (type $5) (result i32 i64) |
| ;; CHECK-NEXT: (return_call $multivalue-callee) |
| ;; CHECK-NEXT: ) |
| (func $multivalue (result i32 i64) |
| ;; Multivalue return calls are supported. |
| (call $multivalue-callee) |
| ) |
| |
| ;; CHECK: (func $conditional (type $2) (param $condition i32) (result i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $condition) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return_call $value-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (return_call $ref-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $conditional (param $condition i32) (result i32) |
| ;; Both arms of an if in tail position are converted to return_call. |
| (if (result i32) |
| (local.get $condition) |
| (then |
| (call $value-callee) |
| ) |
| (else |
| (call $ref-callee) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $one-armed-if (type $i32-to-none) (param $condition i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $condition) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return_call $void-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $one-armed-if (param $condition i32) |
| ;; In a void function, the arm of a one-armed if in tail position is |
| ;; converted to return_call. |
| (if |
| (local.get $condition) |
| (then |
| (call $void-callee) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $indirect (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (return_call_indirect $table (type $none-to-i32) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $indirect (result i32) |
| ;; An indirect call in tail position becomes a return_call_indirect. |
| (call_indirect $table (type $none-to-i32) |
| (i32.const 0) |
| ) |
| ) |
| |
| ;; CHECK: (func $ref (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (return_call_ref $none-to-i32 |
| ;; CHECK-NEXT: (ref.func $value-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $ref (result i32) |
| ;; A call_ref in tail position becomes a return_call_ref. |
| (call_ref $none-to-i32 |
| (ref.func $value-callee) |
| ) |
| ) |
| |
| ;; CHECK: (func $break (type $2) (param $condition i32) (result i32) |
| ;; CHECK-NEXT: (block $out |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $condition) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return_call $value-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return_call $ref-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $break (param $condition i32) (result i32) |
| ;; An unconditional branch to a tail block and the fallthrough of that block |
| ;; are both in tail position. |
| (block $out (result i32) |
| (if |
| (local.get $condition) |
| (then |
| (br $out |
| (call $value-callee) |
| ) |
| ) |
| ) |
| (call $ref-callee) |
| ) |
| ) |
| |
| ;; CHECK: (func $return-break (type $2) (param $condition i32) (result i32) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (block $out |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $condition) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return_call $value-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return_call $ref-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $return-break (param $condition i32) (result i32) |
| ;; A block wrapped in an explicit return is in tail position, so branches to |
| ;; it are also in tail position. |
| (return |
| (block $out (result i32) |
| (if |
| (local.get $condition) |
| (then |
| (br $out |
| (call $value-callee) |
| ) |
| ) |
| ) |
| (call $ref-callee) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $br-if-tail (type $2) (param $condition i32) (result i32) |
| ;; CHECK-NEXT: (block $out |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (return_call $value-callee) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $condition) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $br-if-tail (param $condition i32) (result i32) |
| ;; A conditional branch in tail position to a tail block is converted |
| ;; because both taking the branch and falling through exit the function with |
| ;; the call's result. |
| (block $out (result i32) |
| (br_if $out |
| (call $value-callee) |
| (local.get $condition) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $br-if-not-tail (type $2) (param $condition i32) (result i32) |
| ;; CHECK-NEXT: (block $out (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $out |
| ;; CHECK-NEXT: (call $value-callee) |
| ;; CHECK-NEXT: (local.get $condition) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $br-if-not-tail (param $condition i32) (result i32) |
| ;; A conditional branch NOT in tail position must not be converted. |
| (block $out (result i32) |
| (drop |
| (br_if $out |
| (call $value-callee) |
| (local.get $condition) |
| ) |
| ) |
| (i32.const 42) |
| ) |
| ) |
| |
| ;; CHECK: (func $br-if-side-effect (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (block $out (result i32) |
| ;; CHECK-NEXT: (br_if $out |
| ;; CHECK-NEXT: (call $value-callee) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (global.set $g |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $br-if-side-effect (result i32) |
| ;; A conditional branch in tail position whose condition has side effects |
| ;; must not be converted because converting would skip the side effect. |
| (block $out (result i32) |
| (br_if $out |
| (call $value-callee) |
| (block (result i32) |
| (global.set $g |
| (i32.const 1) |
| ) |
| (i32.const 0) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $br-table-all-tail (type $2) (param $idx i32) (result i32) |
| ;; CHECK-NEXT: (block $out1 |
| ;; CHECK-NEXT: (block $out2 |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (return_call $value-callee) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $idx) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $br-table-all-tail (param $idx i32) (result i32) |
| ;; A br_table where all targets exit the function can be converted. |
| (block $out1 (result i32) |
| (block $out2 (result i32) |
| (br_table $out1 $out2 |
| (call $value-callee) |
| (local.get $idx) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $br-table-not-all-tail (type $2) (param $idx i32) (result i32) |
| ;; CHECK-NEXT: (block $exit (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block $not-exit (result i32) |
| ;; CHECK-NEXT: (br_table $not-exit $exit |
| ;; CHECK-NEXT: (call $value-callee) |
| ;; CHECK-NEXT: (local.get $idx) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $br-table-not-all-tail (param $idx i32) (result i32) |
| ;; A br_table where not all targets exit the function must not be converted. |
| (block $exit (result i32) |
| (drop |
| (block $not-exit (result i32) |
| (br_table $not-exit $exit |
| (call $value-callee) |
| (local.get $idx) |
| ) |
| ) |
| ) |
| (i32.const 42) |
| ) |
| ) |
| |
| ;; CHECK: (func $void-break (type $i32-to-none) (param $condition i32) |
| ;; CHECK-NEXT: (block $out |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $condition) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return_call $void-callee) |
| ;; CHECK-NEXT: (br $out) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return_call $void-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $void-break (param $condition i32) |
| ;; In a void function, statements immediately preceding an unconditional |
| ;; branch to a tail block or falling through a tail block are converted. |
| (block $out |
| (if |
| (local.get $condition) |
| (then |
| (call $void-callee) |
| (br $out) |
| ) |
| ) |
| (call $void-callee) |
| ) |
| ) |
| |
| ;; CHECK: (func $void-br-if-tail (type $i32-to-none) (param $condition i32) |
| ;; CHECK-NEXT: (block $out |
| ;; CHECK-NEXT: (return_call $void-callee) |
| ;; CHECK-NEXT: (br_if $out |
| ;; CHECK-NEXT: (local.get $condition) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $void-br-if-tail (param $condition i32) |
| ;; In a void function, a call preceding a conditional branch in tail |
| ;; position targeting a tail block is converted. |
| (block $out |
| (call $void-callee) |
| (br_if $out |
| (local.get $condition) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $void-br-if-not-tail (type $i32-to-none) (param $condition i32) |
| ;; CHECK-NEXT: (block $out |
| ;; CHECK-NEXT: (call $void-callee) |
| ;; CHECK-NEXT: (br_if $out |
| ;; CHECK-NEXT: (local.get $condition) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $void-br-if-not-tail (param $condition i32) |
| ;; In a void function, a call preceding a conditional branch that is not in |
| ;; tail position must not be converted. |
| (block $out |
| (call $void-callee) |
| (br_if $out |
| (local.get $condition) |
| ) |
| (nop) |
| ) |
| ) |
| |
| ;; CHECK: (func $void-br-table-all-tail (type $i32-to-none) (param $idx i32) |
| ;; CHECK-NEXT: (block $out1 |
| ;; CHECK-NEXT: (block $out2 |
| ;; CHECK-NEXT: (return_call $void-callee) |
| ;; CHECK-NEXT: (br_table $out1 $out2 |
| ;; CHECK-NEXT: (local.get $idx) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $void-br-table-all-tail (param $idx i32) |
| ;; In a void function, a call preceding a br_table whose targets are all tail |
| ;; blocks is converted. |
| (block $out1 |
| (block $out2 |
| (call $void-callee) |
| (br_table $out1 $out2 |
| (local.get $idx) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $void-br-table-not-all-tail (type $i32-to-none) (param $idx i32) |
| ;; CHECK-NEXT: (block $exit |
| ;; CHECK-NEXT: (block $not-exit |
| ;; CHECK-NEXT: (call $void-callee) |
| ;; CHECK-NEXT: (br_table $not-exit $exit |
| ;; CHECK-NEXT: (local.get $idx) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $void-br-table-not-all-tail (param $idx i32) |
| ;; In a void function, a call preceding a br_table where not all targets are |
| ;; tail blocks must not be converted. |
| (block $exit |
| (block $not-exit |
| (call $void-callee) |
| (br_table $not-exit $exit |
| (local.get $idx) |
| ) |
| ) |
| (nop) |
| ) |
| ) |
| |
| ;; CHECK: (func $loop-tail (type $2) (param $condition i32) (result i32) |
| ;; CHECK-NEXT: (loop $l |
| ;; CHECK-NEXT: (br_if $l |
| ;; CHECK-NEXT: (local.get $condition) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return_call $value-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $loop-tail (param $condition i32) (result i32) |
| ;; A loop whose fallthrough is in tail position converts its fallthrough call |
| ;; to a return_call. |
| (loop $l (result i32) |
| (br_if $l |
| (local.get $condition) |
| ) |
| (call $value-callee) |
| ) |
| ) |
| |
| ;; CHECK: (func $loop-backedge-not-tail (type $none-to-none) |
| ;; CHECK-NEXT: (loop $l |
| ;; CHECK-NEXT: (call $void-callee) |
| ;; CHECK-NEXT: (br $l) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $loop-backedge-not-tail |
| ;; A branch to a loop header is a backedge rather than an exit, so a call |
| ;; preceding a loop branch must not be converted. |
| (loop $l |
| (call $void-callee) |
| (br $l) |
| ) |
| ) |
| |
| ;; CHECK: (func $not-tail (type $none-to-none) |
| ;; CHECK-NEXT: (call $void-callee) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $not-tail |
| ;; A call followed by another instruction is not in tail position. |
| (call $void-callee) |
| (nop) |
| ) |
| |
| ;; CHECK: (func $mismatched-result (type $none-to-none) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call $value-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $mismatched-result |
| ;; A dropped call is not in tail position. |
| (drop |
| (call $value-callee) |
| ) |
| ) |
| |
| ;; CHECK: (func $dead-after-return (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $void-callee) |
| ;; CHECK-NEXT: ) |
| (func $dead-after-return (result i32) |
| ;; A dead void call at the end of a block after an unreachable instruction |
| ;; has a mismatched return type and is safely ignored. |
| (block |
| (return |
| (i32.const 1) |
| ) |
| (call $void-callee) |
| ) |
| ) |
| |
| ;; CHECK: (func $unreachable-arg-mismatched-call (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (call $void-arg-callee |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $unreachable-arg-mismatched-call (result i32) |
| ;; Unreachable calls (e.g. due to an unreachable operand) are not converted |
| ;; to return calls, avoiding return type mismatches and optimizing dead code. |
| (call $void-arg-callee |
| (unreachable) |
| ) |
| ) |
| |
| ;; CHECK: (func $unreachable-arg-matching-call (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (call $param-callee |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $unreachable-arg-matching-call (result i32) |
| (call $param-callee |
| (unreachable) |
| (i32.const 1) |
| ) |
| ) |
| |
| ;; CHECK: (func $unreachable-arg-mismatched-call-indirect (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (call_indirect $table (type $i32-to-none) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $unreachable-arg-mismatched-call-indirect (result i32) |
| (call_indirect $table (type $i32-to-none) |
| (unreachable) |
| (i32.const 0) |
| ) |
| ) |
| |
| ;; CHECK: (func $unreachable-arg-mismatched-call-ref (type $10) (param $f (ref $i32-to-none)) (result i32) |
| ;; CHECK-NEXT: (call_ref $i32-to-none |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (local.get $f) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $unreachable-arg-mismatched-call-ref (param $f (ref $i32-to-none)) (result i32) |
| (call_ref $i32-to-none |
| (unreachable) |
| (local.get $f) |
| ) |
| ) |
| |
| ;; CHECK: (func $unreachable-arg-mismatched-cwe (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (call $cwe |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (ref.func $void-arg-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $unreachable-arg-mismatched-cwe (result i32) |
| (call $cwe |
| (unreachable) |
| (ref.func $void-arg-callee) |
| ) |
| ) |
| |
| ;; CHECK: (func $tail-cwe (type $none-to-i32) (result i32) |
| ;; CHECK-NEXT: (return_call $cwe |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: (ref.func $i32-to-i32-callee) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $tail-cwe (result i32) |
| ;; call.without.effects is optimized when reachable. |
| (call $cwe |
| (i32.const 42) |
| (ref.func $i32-to-i32-callee) |
| ) |
| ) |
| ) |