|
3 | 3 | // Vefifies that parameter copies are used in the body of the coroutine
|
4 | 4 | // Verifies that parameter copies are used to construct the promise type, if that type has a matching constructor
|
5 | 5 | // RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s
|
| 6 | +// RUN: %clang_cc1 -std=c++20 -triple=x86_64-pc-win32 -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s --check-prefix=MSABI |
6 | 7 |
|
7 | 8 | namespace std {
|
8 | 9 | template <typename... T> struct coroutine_traits;
|
@@ -59,46 +60,65 @@ struct MoveAndCopy {
|
59 | 60 | ~MoveAndCopy();
|
60 | 61 | };
|
61 | 62 |
|
62 |
| -void consume(int,int,int) noexcept; |
| 63 | +struct [[clang::trivial_abi]] TrivialABI { |
| 64 | + int val; |
| 65 | + TrivialABI(TrivialABI&&) noexcept; |
| 66 | + ~TrivialABI(); |
| 67 | +}; |
| 68 | + |
| 69 | +void consume(int,int,int,int) noexcept; |
63 | 70 |
|
64 | 71 | // TODO: Add support for CopyOnly params
|
65 |
| -// CHECK: define{{.*}} void @_Z1fi8MoveOnly11MoveAndCopy(i32 noundef %val, ptr noundef %[[MoParam:.+]], ptr noundef %[[McParam:.+]]) #0 personality ptr @__gxx_personality_v0 |
66 |
| -void f(int val, MoveOnly moParam, MoveAndCopy mcParam) { |
| 72 | +// CHECK: define{{.*}} void @_Z1fi8MoveOnly11MoveAndCopy10TrivialABI(i32 noundef %val, ptr noundef %[[MoParam:.+]], ptr noundef %[[McParam:.+]], i32 %[[TrivialParam:.+]]) #0 personality ptr @__gxx_personality_v0 |
| 73 | +void f(int val, MoveOnly moParam, MoveAndCopy mcParam, TrivialABI trivialParam) { |
| 74 | + // CHECK: %[[TrivialAlloca:.+]] = alloca %struct.TrivialABI, |
| 75 | + // CHECK-SAME: !coro.outside.frame |
67 | 76 | // CHECK: %[[MoCopy:.+]] = alloca %struct.MoveOnly,
|
68 | 77 | // CHECK: %[[McCopy:.+]] = alloca %struct.MoveAndCopy,
|
| 78 | + // CHECK: %[[TrivialCopy:.+]] = alloca %struct.TrivialABI, |
69 | 79 | // CHECK: store i32 %val, ptr %[[ValAddr:.+]]
|
70 | 80 |
|
71 | 81 | // CHECK: call ptr @llvm.coro.begin(
|
72 | 82 | // CHECK: call void @_ZN8MoveOnlyC1EOS_(ptr {{[^,]*}} %[[MoCopy]], ptr noundef nonnull align 4 dereferenceable(4) %[[MoParam]])
|
73 | 83 | // CHECK-NEXT: call void @llvm.lifetime.start.p0(
|
74 | 84 | // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(ptr {{[^,]*}} %[[McCopy]], ptr noundef nonnull align 4 dereferenceable(4) %[[McParam]]) #
|
75 | 85 | // CHECK-NEXT: call void @llvm.lifetime.start.p0(
|
76 |
| - // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev( |
| 86 | + // CHECK-NEXT: call void @_ZN10TrivialABIC1EOS_(ptr {{[^,]*}} %[[TrivialCopy]], ptr {{[^,]*}} %[[TrivialAlloca]]) |
| 87 | + // CHECK-NEXT: call void @llvm.lifetime.start.p0( |
| 88 | + // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_typeC1Ev( |
77 | 89 |
|
78 | 90 | // CHECK: call void @_ZN14suspend_always12await_resumeEv(
|
79 | 91 | // CHECK: %[[IntParam:.+]] = load i32, ptr %{{.*}}
|
80 | 92 | // CHECK: %[[MoGep:.+]] = getelementptr inbounds nuw %struct.MoveOnly, ptr %[[MoCopy]], i32 0, i32 0
|
81 | 93 | // CHECK: %[[MoVal:.+]] = load i32, ptr %[[MoGep]]
|
82 |
| - // CHECK: %[[McGep:.+]] = getelementptr inbounds nuw %struct.MoveAndCopy, ptr %[[McCopy]], i32 0, i32 0 |
| 94 | + // CHECK: %[[McGep:.+]] = getelementptr inbounds nuw %struct.MoveAndCopy, ptr %[[McCopy]], i32 0, i32 0 |
83 | 95 | // CHECK: %[[McVal:.+]] = load i32, ptr %[[McGep]]
|
84 |
| - // CHECK: call void @_Z7consumeiii(i32 noundef %[[IntParam]], i32 noundef %[[MoVal]], i32 noundef %[[McVal]]) |
| 96 | + // CHECK: %[[TrivialGep:.+]] = getelementptr inbounds nuw %struct.TrivialABI, ptr %[[TrivialCopy]], i32 0, i32 0 |
| 97 | + // CHECK: %[[TrivialVal:.+]] = load i32, ptr %[[TrivialGep]] |
| 98 | + // CHECK: call void @_Z7consumeiiii(i32 noundef %[[IntParam]], i32 noundef %[[MoVal]], i32 noundef %[[McVal]], i32 noundef %[[TrivialVal]]) |
85 | 99 |
|
86 |
| - consume(val, moParam.val, mcParam.val); |
| 100 | + consume(val, moParam.val, mcParam.val, trivialParam.val); |
87 | 101 | co_return;
|
88 | 102 |
|
89 | 103 | // Skip to final suspend:
|
90 |
| - // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_type13final_suspendEv( |
| 104 | + // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_type13final_suspendEv( |
91 | 105 | // CHECK: call void @_ZN14suspend_always12await_resumeEv(
|
92 | 106 |
|
93 | 107 | // Destroy promise, then parameter copies:
|
94 |
| - // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(ptr {{[^,]*}} %__promise) |
| 108 | + // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_typeD1Ev(ptr {{[^,]*}} %__promise) |
| 109 | + // CHECK-NEXT: call void @llvm.lifetime.end.p0( |
| 110 | + // CHECK-NEXT: call void @_ZN10TrivialABID1Ev(ptr {{[^,]*}} %[[TrivialCopy]]) |
95 | 111 | // CHECK-NEXT: call void @llvm.lifetime.end.p0(
|
96 | 112 | // CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(ptr {{[^,]*}} %[[McCopy]])
|
97 | 113 | // CHECK-NEXT: call void @llvm.lifetime.end.p0(
|
98 | 114 | // CHECK-NEXT: call void @_ZN8MoveOnlyD1Ev(ptr {{[^,]*}} %[[MoCopy]]
|
99 | 115 | // CHECK-NEXT: call void @llvm.lifetime.end.p0(
|
100 | 116 | // CHECK-NEXT: call void @llvm.lifetime.end.p0(
|
101 | 117 | // CHECK-NEXT: call ptr @llvm.coro.free(
|
| 118 | + |
| 119 | + // The original trivial_abi parameter is destroyed when returning from the ramp. |
| 120 | + // CHECK: call i1 @llvm.coro.end |
| 121 | + // CHECK: call void @_ZN10TrivialABID1Ev(ptr {{[^,]*}} %[[TrivialAlloca]]) |
102 | 122 | }
|
103 | 123 |
|
104 | 124 | // CHECK-LABEL: void @_Z16dependent_paramsI1A1BEvT_T0_S3_(ptr noundef %x, ptr noundef %0, ptr noundef %y)
|
@@ -190,3 +210,38 @@ method some_class::good_coroutine_calls_custom_constructor(float) {
|
190 | 210 | // CHECK: invoke void @_ZNSt16coroutine_traitsIJ6methodR10some_classfEE12promise_typeC1ES2_f(ptr {{[^,]*}} %__promise, ptr noundef nonnull align 1 dereferenceable(1) %{{.+}}, float
|
191 | 211 | co_return;
|
192 | 212 | }
|
| 213 | + |
| 214 | + |
| 215 | +struct MSParm { |
| 216 | + int val; |
| 217 | + ~MSParm(); |
| 218 | +}; |
| 219 | + |
| 220 | +void consume(int) noexcept; |
| 221 | + |
| 222 | +// Similarly to the [[clang::trivial_abi]] parameters, with the MSVC ABI |
| 223 | +// parameters are also destroyed by the callee, and on x86-64 such parameters |
| 224 | +// may get passed in registers. In that case it's again important that the |
| 225 | +// parameter's local alloca does not become part of the coro frame since that |
| 226 | +// may be destroyed before the destructor call. |
| 227 | +void msabi(MSParm p) { |
| 228 | + // MSABI: define{{.*}} void @"?msabi@@YAXUMSParm@@@Z"(i32 %[[Param:.+]]) |
| 229 | + |
| 230 | + // The parameter's local alloca is marked not part of the frame. |
| 231 | + // MSABI: %[[ParamAlloca:.+]] = alloca %struct.MSParm |
| 232 | + // MSABI-SAME: !coro.outside.frame |
| 233 | + |
| 234 | + // MSABI: %[[ParamCopy:.+]] = alloca %struct.MSParm |
| 235 | + |
| 236 | + consume(p.val); |
| 237 | + // The parameter's copy is used by the coroutine. |
| 238 | + // MSABI: %[[ValPtr:.+]] = getelementptr inbounds nuw %struct.MSParm, ptr %[[ParamCopy]], i32 0, i32 0 |
| 239 | + // MSABI: %[[Val:.+]] = load i32, ptr %[[ValPtr]] |
| 240 | + // MSABI: call void @"?consume@@YAXH@Z"(i32{{.*}} %[[Val]]) |
| 241 | + |
| 242 | + co_return; |
| 243 | + |
| 244 | + // The local alloca is used for the destructor call at the end of the ramp. |
| 245 | + // MSABI: call i1 @llvm.coro.end |
| 246 | + // MSABI: call void @"??1MSParm@@QEAA@XZ"(ptr{{.*}} %[[ParamAlloca]]) |
| 247 | +} |
0 commit comments