; 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 }