Skip to content

Commit 8c5c4d9

Browse files
[Coro][WebAssembly] Add tail-call check for async lowering (#81481)
This patch fixes a verifier error when async lowering is used for WebAssembly target without tail-call feature. This missing check was revealed by b1ac052, which removed inlining of the musttail'ed call and it started leaving the invalid call at the verification stage. Additionally, `TTI::supportsTailCallFor` did not respect the concrete TTI's `supportsTailCalls` implementation, so it always returned true even though `supportsTailCalls` returned false, so this patch also fixes the wrong CRTP base class implementation.
1 parent 8ac7c4f commit 8c5c4d9

File tree

5 files changed

+50
-12
lines changed

5 files changed

+50
-12
lines changed

llvm/include/llvm/Analysis/TargetTransformInfoImpl.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,6 @@ class TargetTransformInfoImplBase {
367367

368368
bool supportsTailCalls() const { return true; }
369369

370-
bool supportsTailCallFor(const CallBase *CB) const {
371-
return supportsTailCalls();
372-
}
373-
374370
bool enableAggressiveInterleaving(bool LoopHasReductions) const {
375371
return false;
376372
}
@@ -1427,6 +1423,10 @@ class TargetTransformInfoImplCRTPBase : public TargetTransformInfoImplBase {
14271423
I, Ops, TargetTransformInfo::TCK_SizeAndLatency);
14281424
return Cost >= TargetTransformInfo::TCC_Expensive;
14291425
}
1426+
1427+
bool supportsTailCallFor(const CallBase *CB) const {
1428+
return static_cast<const T *>(this)->supportsTailCalls();
1429+
}
14301430
};
14311431
} // namespace llvm
14321432

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3064,7 +3064,7 @@ static void doRematerializations(
30643064
}
30653065

30663066
void coro::buildCoroutineFrame(
3067-
Function &F, Shape &Shape,
3067+
Function &F, Shape &Shape, TargetTransformInfo &TTI,
30683068
const std::function<bool(Instruction &)> &MaterializableCallback) {
30693069
// Don't eliminate swifterror in async functions that won't be split.
30703070
if (Shape.ABI != coro::ABI::Async || !Shape.CoroSuspends.empty())
@@ -3100,7 +3100,7 @@ void coro::buildCoroutineFrame(
31003100
SmallVector<Value *, 8> Args(AsyncEnd->args());
31013101
auto Arguments = ArrayRef<Value *>(Args).drop_front(3);
31023102
auto *Call = createMustTailCall(AsyncEnd->getDebugLoc(), MustTailCallFn,
3103-
Arguments, Builder);
3103+
TTI, Arguments, Builder);
31043104
splitAround(Call, "MustTailCall.Before.CoroEnd");
31053105
}
31063106
}

llvm/lib/Transforms/Coroutines/CoroInternal.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H
1313

1414
#include "CoroInstr.h"
15+
#include "llvm/Analysis/TargetTransformInfo.h"
1516
#include "llvm/IR/IRBuilder.h"
1617

1718
namespace llvm {
@@ -272,9 +273,10 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
272273

273274
bool defaultMaterializable(Instruction &V);
274275
void buildCoroutineFrame(
275-
Function &F, Shape &Shape,
276+
Function &F, Shape &Shape, TargetTransformInfo &TTI,
276277
const std::function<bool(Instruction &)> &MaterializableCallback);
277278
CallInst *createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
279+
TargetTransformInfo &TTI,
278280
ArrayRef<Value *> Arguments, IRBuilder<> &);
279281
} // End namespace coro.
280282
} // End namespace llvm

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,6 +1746,7 @@ static void coerceArguments(IRBuilder<> &Builder, FunctionType *FnTy,
17461746
}
17471747

17481748
CallInst *coro::createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
1749+
TargetTransformInfo &TTI,
17491750
ArrayRef<Value *> Arguments,
17501751
IRBuilder<> &Builder) {
17511752
auto *FnTy = MustTailCallFn->getFunctionType();
@@ -1755,14 +1756,18 @@ CallInst *coro::createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
17551756
coerceArguments(Builder, FnTy, Arguments, CallArgs);
17561757

17571758
auto *TailCall = Builder.CreateCall(FnTy, MustTailCallFn, CallArgs);
1758-
TailCall->setTailCallKind(CallInst::TCK_MustTail);
1759+
// Skip targets which don't support tail call.
1760+
if (TTI.supportsTailCallFor(TailCall)) {
1761+
TailCall->setTailCallKind(CallInst::TCK_MustTail);
1762+
}
17591763
TailCall->setDebugLoc(Loc);
17601764
TailCall->setCallingConv(MustTailCallFn->getCallingConv());
17611765
return TailCall;
17621766
}
17631767

17641768
static void splitAsyncCoroutine(Function &F, coro::Shape &Shape,
1765-
SmallVectorImpl<Function *> &Clones) {
1769+
SmallVectorImpl<Function *> &Clones,
1770+
TargetTransformInfo &TTI) {
17661771
assert(Shape.ABI == coro::ABI::Async);
17671772
assert(Clones.empty());
17681773
// Reset various things that the optimizer might have decided it
@@ -1837,7 +1842,7 @@ static void splitAsyncCoroutine(Function &F, coro::Shape &Shape,
18371842
SmallVector<Value *, 8> Args(Suspend->args());
18381843
auto FnArgs = ArrayRef<Value *>(Args).drop_front(
18391844
CoroSuspendAsyncInst::MustTailCallFuncArg + 1);
1840-
coro::createMustTailCall(Suspend->getDebugLoc(), Fn, FnArgs, Builder);
1845+
coro::createMustTailCall(Suspend->getDebugLoc(), Fn, TTI, FnArgs, Builder);
18411846
Builder.CreateRetVoid();
18421847

18431848
// Replace the lvm.coro.async.resume intrisic call.
@@ -2010,7 +2015,7 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
20102015
return Shape;
20112016

20122017
simplifySuspendPoints(Shape);
2013-
buildCoroutineFrame(F, Shape, MaterializableCallback);
2018+
buildCoroutineFrame(F, Shape, TTI, MaterializableCallback);
20142019
replaceFrameSizeAndAlignment(Shape);
20152020

20162021
// If there are no suspend points, no split required, just remove
@@ -2023,7 +2028,7 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
20232028
SwitchCoroutineSplitter::split(F, Shape, Clones, TTI);
20242029
break;
20252030
case coro::ABI::Async:
2026-
splitAsyncCoroutine(F, Shape, Clones);
2031+
splitAsyncCoroutine(F, Shape, Clones, TTI);
20272032
break;
20282033
case coro::ABI::Retcon:
20292034
case coro::ABI::RetconOnce:
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
; RUN: opt < %s -O0 -S -mtriple=wasm32-unknown-unknown | FileCheck %s
2+
; REQUIRES: webassembly-registered-target
3+
4+
%swift.async_func_pointer = type <{ i32, i32 }>
5+
@checkTu = global %swift.async_func_pointer <{ i32 ptrtoint (ptr @check to i32), i32 8 }>
6+
7+
define swiftcc void @check(ptr %0) {
8+
entry:
9+
%1 = call token @llvm.coro.id.async(i32 0, i32 0, i32 0, ptr @checkTu)
10+
%2 = call ptr @llvm.coro.begin(token %1, ptr null)
11+
%3 = call ptr @llvm.coro.async.resume()
12+
store ptr %3, ptr %0, align 4
13+
%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)
14+
ret void
15+
}
16+
17+
declare swiftcc void @check.0()
18+
declare { ptr, i32 } @llvm.coro.suspend.async.sl_p0i32s(i32, ptr, ptr, ...)
19+
declare token @llvm.coro.id.async(i32, i32, i32, ptr)
20+
declare ptr @llvm.coro.begin(token, ptr writeonly)
21+
declare ptr @llvm.coro.async.resume()
22+
23+
define ptr @__swift_async_resume_project_context(ptr %0) {
24+
entry:
25+
ret ptr null
26+
}
27+
28+
; Verify that the resume call is not marked as musttail.
29+
; CHECK-LABEL: define swiftcc void @check(
30+
; CHECK-NOT: musttail call swiftcc void @check.0()
31+
; CHECK: call swiftcc void @check.0()

0 commit comments

Comments
 (0)