| ; RUN: llc < %s -mtriple=wasm32-unknown-unknown -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+tail-call | FileCheck -DPTR=i32 %s |
| ; RUN: llc < %s -mtriple=wasm64-unknown-unknown -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+tail-call | FileCheck -DPTR=i64 %s |
| |
| ; Test swifttailcc (SwiftTail calling convention) support for WebAssembly. |
| ; Requires the tail-call feature for return_call / return_call_indirect. |
| |
| ; Basic swifttailcc function definition with swiftasync parameter. |
| ; Missing swiftself and swifterror are padded automatically. |
| define swifttailcc void @basic_swifttailcc(ptr swiftasync %ctx) { |
| ; CHECK-LABEL: basic_swifttailcc: |
| ; CHECK: .functype basic_swifttailcc ([[PTR]], [[PTR]], [[PTR]]) -> () |
| ret void |
| } |
| |
| ; All Swift parameter attributes together — no padding needed. |
| define swifttailcc void @full_swift_params(i32 %x, ptr swiftasync %ctx, ptr swiftself %self, ptr swifterror %err) { |
| ; CHECK-LABEL: full_swift_params: |
| ; CHECK: .functype full_swift_params (i32, [[PTR]], [[PTR]], [[PTR]]) -> () |
| ret void |
| } |
| |
| ; Direct musttail call produces return_call. |
| define swifttailcc void @direct_tail_call(ptr swiftasync %ctx) { |
| ; CHECK-LABEL: direct_tail_call: |
| ; CHECK: return_call direct_tail_call |
| musttail call swifttailcc void @direct_tail_call(ptr swiftasync %ctx) |
| ret void |
| } |
| |
| ; Indirect musttail call produces return_call_indirect. |
| define swifttailcc void @indirect_tail_call(ptr swiftasync %ctx, ptr %fn) { |
| ; CHECK-LABEL: indirect_tail_call: |
| ; CHECK: return_call_indirect |
| musttail call swifttailcc void %fn(ptr swiftasync %ctx) |
| ret void |
| } |
| |
| ; Tail call with swiftasync and swiftself parameters. |
| ; Note: swifterror is not allowed in swifttailcc musttail caller/callee. |
| ; Errors are handled through the async context rather than through the |
| ; swifterror register convention. |
| declare swifttailcc void @callee_swift_params(i32, ptr swiftasync, ptr swiftself) |
| |
| define swifttailcc void @tail_call_swift_params(i32 %x, ptr swiftasync %ctx, ptr swiftself %self) { |
| ; CHECK-LABEL: tail_call_swift_params: |
| ; CHECK: return_call callee_swift_params |
| musttail call swifttailcc void @callee_swift_params(i32 %x, ptr swiftasync %ctx, ptr swiftself %self) |
| ret void |
| } |
| |
| ; Non-tail call: regular call instruction, not return_call. |
| declare swifttailcc void @other_func(ptr swiftasync) |
| declare void @side_effect() |
| |
| define swifttailcc void @regular_call(ptr swiftasync %ctx) { |
| ; CHECK-LABEL: regular_call: |
| ; CHECK: call other_func |
| ; CHECK: call side_effect |
| ; CHECK: return |
| call swifttailcc void @other_func(ptr swiftasync %ctx) |
| call void @side_effect() |
| ret void |
| } |
| |
| ; Tail call with return value. |
| declare swifttailcc i32 @callee_i32(ptr swiftasync, i32) |
| |
| define swifttailcc i32 @with_return(ptr swiftasync %ctx, i32 %x) { |
| ; CHECK-LABEL: with_return: |
| ; CHECK: return_call callee_i32 |
| %r = musttail call swifttailcc i32 @callee_i32(ptr swiftasync %ctx, i32 %x) |
| ret i32 %r |
| } |
| |
| ; Mixed calling conventions: swifttailcc function calling a C function. |
| declare void @c_function(i32) |
| |
| define swifttailcc void @mixed_cc(ptr swiftasync %ctx) { |
| ; CHECK-LABEL: mixed_cc: |
| ; CHECK: call c_function |
| call void @c_function(i32 42) |
| ret void |
| } |
| |
| ; Signature padding: a swifttailcc function with no Swift attributes gets |
| ; swiftself, swifterror, and swiftasync dummy params padded automatically. |
| define swifttailcc void @no_swift_params(i32 %x) { |
| ; CHECK-LABEL: no_swift_params: |
| ; CHECK: .functype no_swift_params (i32, [[PTR]], [[PTR]], [[PTR]]) -> () |
| ret void |
| } |
| |
| ; Return type mismatch: advisory tail call falls back to regular call. |
| declare swifttailcc i32 @returns_i32_callee(ptr swiftasync) |
| |
| define swifttailcc void @return_type_mismatch(ptr swiftasync %ctx) { |
| ; CHECK-LABEL: return_type_mismatch: |
| ; CHECK-NOT: return_call |
| ; CHECK: call $drop=, returns_i32_callee |
| tail call swifttailcc i32 @returns_i32_callee(ptr swiftasync %ctx) |
| ret void |
| } |
| |
| ; Varargs callee: advisory tail call falls back to regular call. |
| declare swifttailcc void @varargs_callee(ptr, ...) |
| |
| define swifttailcc void @varargs_tail(ptr swiftasync %ctx) { |
| ; CHECK-LABEL: varargs_tail: |
| ; CHECK-NOT: return_call |
| ; CHECK: call varargs_callee |
| tail call swifttailcc void @varargs_callee(ptr swiftasync %ctx) |
| ret void |
| } |
| |
| ; Indirect call signature consistency: all indirect calls to a swifttailcc |
| ; function must produce the same call_indirect signature, regardless of which |
| ; swift parameter attributes are present at the call site. This also verifies |
| ; that FixFunctionBitcasts skips swifttailcc (the IR type doesn't match the |
| ; padded Wasm type, so without the skip a wrapper would break this). |
| define swifttailcc void @indirect_target(i32, i32) { |
| ; CHECK-LABEL: indirect_target: |
| ; CHECK: .functype indirect_target (i32, i32, [[PTR]], [[PTR]], [[PTR]]) -> () |
| ret void |
| } |
| @fn_ptr = global ptr @indirect_target |
| |
| define swifttailcc void @test_indirect_consistency() { |
| ; CHECK-LABEL: test_indirect_consistency: |
| %p = load ptr, ptr @fn_ptr |
| |
| ; No swift attrs — swiftself, swifterror, swiftasync all padded. |
| ; CHECK: call_indirect __indirect_function_table, (i32, i32, [[PTR]], [[PTR]], [[PTR]]) -> () |
| call swifttailcc void %p(i32 1, i32 2) |
| |
| ; swiftasync present — swiftself and swifterror padded. |
| ; CHECK: call_indirect __indirect_function_table, (i32, i32, [[PTR]], [[PTR]], [[PTR]]) -> () |
| call swifttailcc void %p(i32 1, i32 2, ptr swiftasync null) |
| |
| ; swiftself present — swifterror and swiftasync padded. |
| ; CHECK: call_indirect __indirect_function_table, (i32, i32, [[PTR]], [[PTR]], [[PTR]]) -> () |
| call swifttailcc void %p(i32 1, i32 2, ptr swiftself null) |
| |
| ; swiftasync + swiftself present — swifterror padded. |
| ; CHECK: call_indirect __indirect_function_table, (i32, i32, [[PTR]], [[PTR]], [[PTR]]) -> () |
| call swifttailcc void %p(i32 1, i32 2, ptr swiftasync null, ptr swiftself null) |
| |
| ret void |
| } |