Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clang/lib/Basic/Targets/WebAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
case CC_Swift:
return CCCR_OK;
case CC_SwiftAsync:
return CCCR_Error;
return HasTailCall ? CCCR_OK : CCCR_Error;
default:
return CCCR_Warning;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,11 @@ bool FixFunctionBitcasts::runOnModule(Module &M) {

// Collect all the places that need wrappers.
for (Function &F : M) {
// Skip to fix when the function is swiftcc because swiftcc allows
// bitcast type difference for swiftself and swifterror.
if (F.getCallingConv() == CallingConv::Swift)
// Skip to fix when the function is swiftcc or swifttailcc because these
// calling conventions allow bitcast type difference for swiftself,
// swifterror, and swiftasync.
if (F.getCallingConv() == CallingConv::Swift ||
F.getCallingConv() == CallingConv::SwiftTail)
continue;
findUses(&F, F, Uses);

Expand Down
39 changes: 28 additions & 11 deletions llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,7 @@ static bool callingConvSupported(CallingConv::ID CallConv) {
CallConv == CallingConv::PreserveAll ||
CallConv == CallingConv::CXX_FAST_TLS ||
CallConv == CallingConv::WASM_EmscriptenInvoke ||
CallConv == CallingConv::Swift;
CallConv == CallingConv::Swift || CallConv == CallingConv::SwiftTail;
}

SDValue
Expand Down Expand Up @@ -1274,12 +1274,14 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,

bool HasSwiftSelfArg = false;
bool HasSwiftErrorArg = false;
bool HasSwiftAsyncArg = false;
unsigned NumFixedArgs = 0;
for (unsigned I = 0; I < Outs.size(); ++I) {
const ISD::OutputArg &Out = Outs[I];
SDValue &OutVal = OutVals[I];
HasSwiftSelfArg |= Out.Flags.isSwiftSelf();
HasSwiftErrorArg |= Out.Flags.isSwiftError();
HasSwiftAsyncArg |= Out.Flags.isSwiftAsync();
if (Out.Flags.isSwiftCoro())
fail(DL, DAG, "WebAssembly hasn't implemented swiftcoro arguments");
if (Out.Flags.isNest())
Expand Down Expand Up @@ -1312,11 +1314,12 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
bool IsVarArg = CLI.IsVarArg;
auto PtrVT = getPointerTy(Layout);

// For swiftcc, emit additional swiftself and swifterror arguments
// if there aren't. These additional arguments are also added for callee
// signature They are necessary to match callee and caller signature for
// indirect call.
if (CallConv == CallingConv::Swift) {
// For swiftcc and swifttailcc, emit additional swiftself, swifterror, and
// (for swifttailcc) swiftasync arguments if there aren't. These additional
// arguments are also added for callee signature. They are necessary to match
// callee and caller signature for indirect call.
if (CallConv == CallingConv::Swift || CallConv == CallingConv::SwiftTail) {
Type *PtrTy = PointerType::getUnqual(*DAG.getContext());
if (!HasSwiftSelfArg) {
NumFixedArgs++;
ISD::OutputArg Arg;
Expand All @@ -1333,6 +1336,15 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
SDValue ArgVal = DAG.getUNDEF(PtrVT);
CLI.OutVals.push_back(ArgVal);
}
if (CallConv == CallingConv::SwiftTail && !HasSwiftAsyncArg) {
NumFixedArgs++;
ISD::ArgFlagsTy Flags;
Flags.setSwiftAsync();
ISD::OutputArg Arg(Flags, PtrVT, EVT(PtrVT), PtrTy, 0, 0);
CLI.Outs.push_back(Arg);
SDValue ArgVal = DAG.getUNDEF(PtrVT);
CLI.OutVals.push_back(ArgVal);
}
}

// Analyze operands of the call, assigning locations to each operand.
Expand Down Expand Up @@ -1528,9 +1540,11 @@ SDValue WebAssemblyTargetLowering::LowerFormalArguments(

bool HasSwiftErrorArg = false;
bool HasSwiftSelfArg = false;
bool HasSwiftAsyncArg = false;
for (const ISD::InputArg &In : Ins) {
HasSwiftSelfArg |= In.Flags.isSwiftSelf();
HasSwiftErrorArg |= In.Flags.isSwiftError();
HasSwiftAsyncArg |= In.Flags.isSwiftAsync();
if (In.Flags.isInAlloca())
fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments");
if (In.Flags.isNest())
Expand All @@ -1550,18 +1564,21 @@ SDValue WebAssemblyTargetLowering::LowerFormalArguments(
MFI->addParam(In.VT);
}

// For swiftcc, emit additional swiftself and swifterror arguments
// if there aren't. These additional arguments are also added for callee
// signature They are necessary to match callee and caller signature for
// indirect call.
// For swiftcc and swifttailcc, emit additional swiftself, swifterror, and
// (for swifttailcc) swiftasync arguments if there aren't. These additional
// arguments are also added for callee signature. They are necessary to match
// callee and caller signature for indirect call.
auto PtrVT = getPointerTy(MF.getDataLayout());
if (CallConv == CallingConv::Swift) {
if (CallConv == CallingConv::Swift || CallConv == CallingConv::SwiftTail) {
if (!HasSwiftSelfArg) {
MFI->addParam(PtrVT);
}
if (!HasSwiftErrorArg) {
MFI->addParam(PtrVT);
}
if (CallConv == CallingConv::SwiftTail && !HasSwiftAsyncArg) {
MFI->addParam(PtrVT);
}
}
// Varargs are copied into a buffer allocated by the caller, and a pointer to
// the buffer is passed as an argument.
Expand Down
18 changes: 12 additions & 6 deletions llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,22 +86,28 @@ void llvm::computeSignatureVTs(const FunctionType *Ty,
if (Ty->isVarArg())
Params.push_back(PtrVT);

// For swiftcc, emit additional swiftself and swifterror parameters
// if there aren't. These additional parameters are also passed for caller.
// They are necessary to match callee and caller signature for indirect
// call.
// For swiftcc and swifttailcc, emit additional swiftself, swifterror, and
// (for swifttailcc) swiftasync parameters if there aren't. These additional
// parameters are also passed for caller. They are necessary to match callee
// and caller signature for indirect call.

if (TargetFunc && TargetFunc->getCallingConv() == CallingConv::Swift) {
if (TargetFunc && (TargetFunc->getCallingConv() == CallingConv::Swift ||
TargetFunc->getCallingConv() == CallingConv::SwiftTail)) {
MVT PtrVT = MVT::getIntegerVT(TM.createDataLayout().getPointerSizeInBits());
bool HasSwiftErrorArg = false;
bool HasSwiftSelfArg = false;
bool HasSwiftAsyncArg = false;
for (const auto &Arg : TargetFunc->args()) {
HasSwiftErrorArg |= Arg.hasAttribute(Attribute::SwiftError);
HasSwiftSelfArg |= Arg.hasAttribute(Attribute::SwiftSelf);
HasSwiftAsyncArg |= Arg.hasAttribute(Attribute::SwiftAsync);
}
if (!HasSwiftSelfArg)
Params.push_back(PtrVT);
if (!HasSwiftErrorArg)
Params.push_back(PtrVT);
if (!HasSwiftSelfArg)
if (TargetFunc->getCallingConv() == CallingConv::SwiftTail &&
!HasSwiftAsyncArg)
Params.push_back(PtrVT);
}
}
Expand Down
41 changes: 41 additions & 0 deletions llvm/test/CodeGen/WebAssembly/swiftasync-coroutine.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
; RUN: opt < %s -passes='module(coro-early),cgscc(coro-split),module(coro-cleanup)' -S -mtriple=wasm32-unknown-unknown -mattr=+tail-call | FileCheck --check-prefix=IR %s
; RUN: opt < %s -passes='module(coro-early),cgscc(coro-split),module(coro-cleanup)' -mtriple=wasm32-unknown-unknown -mattr=+tail-call | \
; RUN: llc -mtriple=wasm32-unknown-unknown -verify-machineinstrs \
; RUN: -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals \
; RUN: -wasm-keep-registers -mattr=+tail-call | FileCheck --check-prefix=ASM %s

; End-to-end test: verify that async coroutine splitting with swifttailcc
; produces musttail calls (at the IR level) that lower to return_call (at the
; assembly level) when the tail-call feature is enabled.

%swift.async_func_pointer = type <{ i32, i32 }>
@checkTu = global %swift.async_func_pointer <{ i32 ptrtoint (ptr @check to i32), i32 8 }>

define swifttailcc void @check(ptr swiftasync %0) {
; IR-LABEL: define swifttailcc void @check(
; IR: musttail call swifttailcc void @check.0()
;
; Coroutine splitting generates a resume function with swifttailcc and swiftasync.
; IR-LABEL: define internal swifttailcc void @checkTQ0_(ptr swiftasync
;
; ASM-LABEL: check:
; ASM: return_call check.0
entry:
%1 = call token @llvm.coro.id.async(i32 0, i32 0, i32 0, ptr @checkTu)
%2 = call ptr @llvm.coro.begin(token %1, ptr null)
%3 = call ptr @llvm.coro.async.resume()
store ptr %3, ptr %0, align 4
%4 = call { ptr, i32 } (i32, ptr, ptr, ...) @llvm.coro.suspend.async.sl_p0i32s(i32 0, ptr %3, ptr @__swift_async_resume_project_context, ptr @check.0, ptr null, ptr null)
ret void
}

declare swifttailcc void @check.0()
declare { ptr, i32 } @llvm.coro.suspend.async.sl_p0i32s(i32, ptr, ptr, ...)
declare token @llvm.coro.id.async(i32, i32, i32, ptr)
declare ptr @llvm.coro.begin(token, ptr writeonly)
declare ptr @llvm.coro.async.resume()

define ptr @__swift_async_resume_project_context(ptr %0) {
entry:
ret ptr null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
; RUN: not llc < %s -mtriple=wasm32-unknown-unknown -verify-machineinstrs 2>&1 | FileCheck %s

; musttail with swifttailcc requires the +tail-call feature.
; CHECK: error:
; CHECK-SAME: WebAssembly 'tail-call' feature not enabled

define swifttailcc void @musttail_no_tailcall(ptr swiftasync %ctx) {
musttail call swifttailcc void @musttail_no_tailcall(ptr swiftasync %ctx)
ret void
}
19 changes: 19 additions & 0 deletions llvm/test/CodeGen/WebAssembly/swifttailcc-no-tailcall.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
; RUN: llc < %s -mtriple=wasm32-unknown-unknown -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s

; Without +tail-call, advisory `tail` calls silently fall back to regular calls.

define swifttailcc void @basic_no_tailcall(ptr swiftasync %ctx) {
; CHECK-LABEL: basic_no_tailcall:
; CHECK: .functype basic_no_tailcall (i32, i32, i32) -> ()
; CHECK: return
ret void
}

; Advisory tail call without +tail-call: falls back to regular call.
define swifttailcc void @tail_no_tailcall(ptr swiftasync %ctx) {
; CHECK-LABEL: tail_no_tailcall:
; CHECK-NOT: return_call
; CHECK: call tail_no_tailcall
tail call swifttailcc void @tail_no_tailcall(ptr swiftasync %ctx)
ret void
}
148 changes: 148 additions & 0 deletions llvm/test/CodeGen/WebAssembly/swifttailcc.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
; 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
}