Skip to content

Commit 625a0c9

Browse files
[Coro][WebAssembly] Add tail-call check for async lowering (llvm#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 96e8d7c commit 625a0c9

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
@@ -357,10 +357,6 @@ class TargetTransformInfoImplBase {
357357

358358
bool supportsTailCalls() const { return true; }
359359

360-
bool supportsTailCallFor(const CallBase *CB) const {
361-
return supportsTailCalls();
362-
}
363-
364360
bool enableAggressiveInterleaving(bool LoopHasReductions) const {
365361
return false;
366362
}
@@ -1380,6 +1376,10 @@ class TargetTransformInfoImplCRTPBase : public TargetTransformInfoImplBase {
13801376
I, Ops, TargetTransformInfo::TCK_SizeAndLatency);
13811377
return Cost >= TargetTransformInfo::TCC_Expensive;
13821378
}
1379+
1380+
bool supportsTailCallFor(const CallBase *CB) const {
1381+
return static_cast<const T *>(this)->supportsTailCalls();
1382+
}
13831383
};
13841384
} // namespace llvm
13851385

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2984,7 +2984,7 @@ static void doRematerializations(
29842984
}
29852985

29862986
void coro::buildCoroutineFrame(
2987-
Function &F, Shape &Shape,
2987+
Function &F, Shape &Shape, TargetTransformInfo &TTI,
29882988
const std::function<bool(Instruction &)> &MaterializableCallback) {
29892989
// Don't eliminate swifterror in async functions that won't be split.
29902990
if (Shape.ABI != coro::ABI::Async || !Shape.CoroSuspends.empty())
@@ -3020,7 +3020,7 @@ void coro::buildCoroutineFrame(
30203020
SmallVector<Value *, 8> Args(AsyncEnd->args());
30213021
auto Arguments = ArrayRef<Value *>(Args).drop_front(3);
30223022
auto *Call = createMustTailCall(AsyncEnd->getDebugLoc(), MustTailCallFn,
3023-
Arguments, Builder);
3023+
TTI, Arguments, Builder);
30243024
splitAround(Call, "MustTailCall.Before.CoroEnd");
30253025
}
30263026
}

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 {
@@ -265,9 +266,10 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
265266

266267
bool defaultMaterializable(Instruction &V);
267268
void buildCoroutineFrame(
268-
Function &F, Shape &Shape,
269+
Function &F, Shape &Shape, TargetTransformInfo &TTI,
269270
const std::function<bool(Instruction &)> &MaterializableCallback);
270271
CallInst *createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
272+
TargetTransformInfo &TTI,
271273
ArrayRef<Value *> Arguments, IRBuilder<> &);
272274
} // End namespace coro.
273275
} // End namespace llvm

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

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

17001700
CallInst *coro::createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
1701+
TargetTransformInfo &TTI,
17011702
ArrayRef<Value *> Arguments,
17021703
IRBuilder<> &Builder) {
17031704
auto *FnTy = MustTailCallFn->getFunctionType();
@@ -1707,14 +1708,18 @@ CallInst *coro::createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
17071708
coerceArguments(Builder, FnTy, Arguments, CallArgs);
17081709

17091710
auto *TailCall = Builder.CreateCall(FnTy, MustTailCallFn, CallArgs);
1710-
TailCall->setTailCallKind(CallInst::TCK_MustTail);
1711+
// Skip targets which don't support tail call.
1712+
if (TTI.supportsTailCallFor(TailCall)) {
1713+
TailCall->setTailCallKind(CallInst::TCK_MustTail);
1714+
}
17111715
TailCall->setDebugLoc(Loc);
17121716
TailCall->setCallingConv(MustTailCallFn->getCallingConv());
17131717
return TailCall;
17141718
}
17151719

17161720
static void splitAsyncCoroutine(Function &F, coro::Shape &Shape,
1717-
SmallVectorImpl<Function *> &Clones) {
1721+
SmallVectorImpl<Function *> &Clones,
1722+
TargetTransformInfo &TTI) {
17181723
assert(Shape.ABI == coro::ABI::Async);
17191724
assert(Clones.empty());
17201725
// Reset various things that the optimizer might have decided it
@@ -1789,7 +1794,7 @@ static void splitAsyncCoroutine(Function &F, coro::Shape &Shape,
17891794
SmallVector<Value *, 8> Args(Suspend->args());
17901795
auto FnArgs = ArrayRef<Value *>(Args).drop_front(
17911796
CoroSuspendAsyncInst::MustTailCallFuncArg + 1);
1792-
coro::createMustTailCall(Suspend->getDebugLoc(), Fn, FnArgs, Builder);
1797+
coro::createMustTailCall(Suspend->getDebugLoc(), Fn, TTI, FnArgs, Builder);
17931798
Builder.CreateRetVoid();
17941799

17951800
// Replace the lvm.coro.async.resume intrisic call.
@@ -1964,7 +1969,7 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
19641969
return Shape;
19651970

19661971
simplifySuspendPoints(Shape);
1967-
buildCoroutineFrame(F, Shape, MaterializableCallback);
1972+
buildCoroutineFrame(F, Shape, TTI, MaterializableCallback);
19681973
replaceFrameSizeAndAlignment(Shape);
19691974

19701975
// If there are no suspend points, no split required, just remove
@@ -1977,7 +1982,7 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
19771982
splitSwitchCoroutine(F, Shape, Clones, TTI);
19781983
break;
19791984
case coro::ABI::Async:
1980-
splitAsyncCoroutine(F, Shape, Clones);
1985+
splitAsyncCoroutine(F, Shape, Clones, TTI);
19811986
break;
19821987
case coro::ABI::Retcon:
19831988
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)