@@ -129,49 +129,7 @@ static SmallString<32> buildSuspendPrefixStr(CGCoroData &Coro, AwaitKind Kind) {
129129 return Prefix;
130130}
131131
132- // Check if function can throw based on prototype noexcept, also works for
133- // destructors which are implicitly noexcept but can be marked noexcept(false).
134- static bool FunctionCanThrow (const FunctionDecl *D) {
135- const auto *Proto = D->getType ()->getAs <FunctionProtoType>();
136- if (!Proto) {
137- // Function proto is not found, we conservatively assume throwing.
138- return true ;
139- }
140- return !isNoexceptExceptionSpec (Proto->getExceptionSpecType ()) ||
141- Proto->canThrow () != CT_Cannot;
142- }
143-
144- static bool ResumeStmtCanThrow (const Stmt *S) {
145- if (const auto *CE = dyn_cast<CallExpr>(S)) {
146- const auto *Callee = CE->getDirectCallee ();
147- if (!Callee)
148- // We don't have direct callee. Conservatively assume throwing.
149- return true ;
150-
151- if (FunctionCanThrow (Callee))
152- return true ;
153-
154- // Fall through to visit the children.
155- }
156-
157- if (const auto *TE = dyn_cast<CXXBindTemporaryExpr>(S)) {
158- // Special handling of CXXBindTemporaryExpr here as calling of Dtor of the
159- // temporary is not part of `children()` as covered in the fall through.
160- // We need to mark entire statement as throwing if the destructor of the
161- // temporary throws.
162- const auto *Dtor = TE->getTemporary ()->getDestructor ();
163- if (FunctionCanThrow (Dtor))
164- return true ;
165-
166- // Fall through to visit the children.
167- }
168-
169- for (const auto *child : S->children ())
170- if (ResumeStmtCanThrow (child))
171- return true ;
172-
173- return false ;
174- }
132+ namespace {
175133
176134// Emit suspend expression which roughly looks like:
177135//
@@ -200,117 +158,198 @@ static bool ResumeStmtCanThrow(const Stmt *S) {
200158//
201159// See llvm's docs/Coroutines.rst for more details.
202160//
203- namespace {
204- struct LValueOrRValue {
205- LValue LV;
206- RValue RV;
207- };
208- }
209- static LValueOrRValue emitSuspendExpression (CodeGenFunction &CGF, CGCoroData &Coro,
210- CoroutineSuspendExpr const &S,
211- AwaitKind Kind, AggValueSlot aggSlot,
212- bool ignoreResult, bool forLValue) {
213- auto *E = S.getCommonExpr ();
214-
215- auto Binder =
216- CodeGenFunction::OpaqueValueMappingData::bind (CGF, S.getOpaqueValue (), E);
217- auto UnbindOnExit = llvm::make_scope_exit ([&] { Binder.unbind (CGF); });
218-
219- auto Prefix = buildSuspendPrefixStr (Coro, Kind);
220- BasicBlock *ReadyBlock = CGF.createBasicBlock (Prefix + Twine (" .ready" ));
221- BasicBlock *SuspendBlock = CGF.createBasicBlock (Prefix + Twine (" .suspend" ));
222- BasicBlock *CleanupBlock = CGF.createBasicBlock (Prefix + Twine (" .cleanup" ));
223-
224- // If expression is ready, no need to suspend.
225- CGF.EmitBranchOnBoolExpr (S.getReadyExpr (), ReadyBlock, SuspendBlock, 0 );
226-
227- // Otherwise, emit suspend logic.
228- CGF.EmitBlock (SuspendBlock);
229-
230- auto &Builder = CGF.Builder ;
231- llvm::Function *CoroSave = CGF.CGM .getIntrinsic (llvm::Intrinsic::coro_save);
232- auto *NullPtr = llvm::ConstantPointerNull::get (CGF.CGM .Int8PtrTy );
233- auto *SaveCall = Builder.CreateCall (CoroSave, {NullPtr});
234-
235- CGF.CurCoro .InSuspendBlock = true ;
236- auto *SuspendRet = CGF.EmitScalarExpr (S.getSuspendExpr ());
237- CGF.CurCoro .InSuspendBlock = false ;
238-
239- if (SuspendRet != nullptr && SuspendRet->getType ()->isIntegerTy (1 )) {
240- // Veto suspension if requested by bool returning await_suspend.
241- BasicBlock *RealSuspendBlock =
242- CGF.createBasicBlock (Prefix + Twine (" .suspend.bool" ));
243- CGF.Builder .CreateCondBr (SuspendRet, RealSuspendBlock, ReadyBlock);
244- CGF.EmitBlock (RealSuspendBlock);
161+ class SuspendExpressionEmitter final {
162+ public:
163+ SuspendExpressionEmitter (CodeGenFunction &CGF, CGCoroData &Coro,
164+ CoroutineSuspendExpr const &S, AwaitKind Kind)
165+ : CGF(CGF), Coro(Coro), SuspendExpr(S), Kind(Kind),
166+ SuspendPrefix (buildSuspendPrefixStr(Coro, Kind)) {
167+ CommonExpr = SuspendExpr.getCommonExpr ();
168+ Binder = CodeGenFunction::OpaqueValueMappingData::bind (
169+ CGF, SuspendExpr.getOpaqueValue (), CommonExpr);
245170 }
246171
247- // Emit the suspend point.
248- const bool IsFinalSuspend = (Kind == AwaitKind::Final);
249- llvm::Function *CoroSuspend =
250- CGF.CGM .getIntrinsic (llvm::Intrinsic::coro_suspend);
251- auto *SuspendResult = Builder.CreateCall (
252- CoroSuspend, {SaveCall, Builder.getInt1 (IsFinalSuspend)});
253-
254- // Create a switch capturing three possible continuations.
255- auto *Switch = Builder.CreateSwitch (SuspendResult, Coro.SuspendBB , 2 );
256- Switch->addCase (Builder.getInt8 (0 ), ReadyBlock);
257- Switch->addCase (Builder.getInt8 (1 ), CleanupBlock);
258-
259- // Emit cleanup for this suspend point.
260- CGF.EmitBlock (CleanupBlock);
261- CGF.EmitBranchThroughCleanup (Coro.CleanupJD );
262-
263- // Emit await_resume expression.
264- CGF.EmitBlock (ReadyBlock);
265-
266- // Exception handling requires additional IR. If the 'await_resume' function
267- // is marked as 'noexcept', we avoid generating this additional IR.
268- CXXTryStmt *TryStmt = nullptr ;
269- if (Coro.ExceptionHandler && Kind == AwaitKind::Init &&
270- ResumeStmtCanThrow (S.getResumeExpr ())) {
271- Coro.ResumeEHVar =
272- CGF.CreateTempAlloca (Builder.getInt1Ty (), Prefix + Twine (" resume.eh" ));
273- Builder.CreateFlagStore (true , Coro.ResumeEHVar );
274-
275- auto Loc = S.getResumeExpr ()->getExprLoc ();
276- auto *Catch = new (CGF.getContext ())
277- CXXCatchStmt (Loc, /* exDecl=*/ nullptr , Coro.ExceptionHandler );
278- auto *TryBody = CompoundStmt::Create (CGF.getContext (), S.getResumeExpr (),
279- FPOptionsOverride (), Loc, Loc);
280- TryStmt = CXXTryStmt::Create (CGF.getContext (), Loc, TryBody, Catch);
281- CGF.EnterCXXTryStmt (*TryStmt);
282- CGF.EmitStmt (TryBody);
283- // We don't use EmitCXXTryStmt here. We need to store to ResumeEHVar that
284- // doesn't exist in the body.
285- Builder.CreateFlagStore (false , Coro.ResumeEHVar );
286- CGF.ExitCXXTryStmt (*TryStmt);
287- LValueOrRValue Res;
288- // We are not supposed to obtain the value from init suspend await_resume().
289- Res.RV = RValue::getIgnored ();
290- return Res;
172+ SuspendExpressionEmitter (const SuspendExpressionEmitter &) = delete;
173+ SuspendExpressionEmitter (SuspendExpressionEmitter &&) = delete;
174+
175+ ~SuspendExpressionEmitter () { Binder.unbind (CGF); }
176+
177+ LValue EmitAsLValue () {
178+ assert (Kind != AwaitKind::Init);
179+ emitCommonBlocks ();
180+ return CGF.EmitLValue (SuspendExpr.getResumeExpr ());
291181 }
292182
293- LValueOrRValue Res;
294- if (forLValue)
295- Res.LV = CGF.EmitLValue (S.getResumeExpr ());
296- else
297- Res.RV = CGF.EmitAnyExpr (S.getResumeExpr (), aggSlot, ignoreResult);
183+ RValue EmitAsRValue (AggValueSlot AggSlot, bool IgnoreResult) {
184+ emitCommonBlocks ();
185+ auto &Builder = CGF.Builder ;
186+ auto *ResumeExpr = SuspendExpr.getResumeExpr ();
187+
188+ // Exception handling requires additional IR. If the 'await_resume' function
189+ // is marked as 'noexcept', we avoid generating this additional IR.
190+ CXXTryStmt *TryStmt = nullptr ;
191+ if (Coro.ExceptionHandler && Kind == AwaitKind::Init &&
192+ resumeExprCanThrow ()) {
193+ Coro.ResumeEHVar = CGF.CreateTempAlloca (
194+ Builder.getInt1Ty (), SuspendPrefix + Twine (" resume.eh" ));
195+ Builder.CreateFlagStore (true , Coro.ResumeEHVar );
196+
197+ auto Loc = ResumeExpr->getExprLoc ();
198+ auto *Catch = new (CGF.getContext ())
199+ CXXCatchStmt (Loc, /* exDecl=*/ nullptr , Coro.ExceptionHandler );
200+
201+ auto *TryBody = CompoundStmt::Create (CGF.getContext (), ResumeExpr,
202+ FPOptionsOverride (), Loc, Loc);
203+ TryStmt = CXXTryStmt::Create (CGF.getContext (), Loc, TryBody, Catch);
204+ CGF.EnterCXXTryStmt (*TryStmt);
205+ CGF.EmitStmt (TryBody);
206+ // We don't use EmitCXXTryStmt here. We need to store to ResumeEHVar that
207+ // doesn't exist in the body.
208+ Builder.CreateFlagStore (false , Coro.ResumeEHVar );
209+ CGF.ExitCXXTryStmt (*TryStmt);
210+ // We are not supposed to obtain the value from init suspend
211+ // await_resume().
212+ return RValue::getIgnored ();
213+ }
298214
299- return Res;
300- }
215+ auto Ret = CGF.EmitAnyExpr (ResumeExpr, AggSlot, IgnoreResult);
216+ return Ret;
217+ }
218+
219+ private:
220+ CodeGenFunction &CGF;
221+ CGCoroData &Coro;
222+ CoroutineSuspendExpr const &SuspendExpr;
223+ AwaitKind Kind;
224+ SmallString<32 > SuspendPrefix;
225+ Expr *CommonExpr;
226+ CodeGenFunction::OpaqueValueMappingData Binder;
227+
228+ // Emit all the common blocks for this suspend expression until the ready
229+ // block, from which point there are three possible outcomes:
230+ // 1) Emit as LValue;
231+ // 2) Emit as RValue;
232+ // 3) This suspend is the initial suspend of the coroutine, run
233+ // `try { promise.await_resume() } catch { ... }` and store a flag if it
234+ // didn't throw. In such case we continue to the coroutine function body,
235+ // otherwise, continue to the catch logic in the coroutine's exception
236+ // handler.
237+ void emitCommonBlocks () {
238+ BasicBlock *ReadyBlock =
239+ CGF.createBasicBlock (SuspendPrefix + Twine (" .ready" ));
240+ BasicBlock *SuspendBlock =
241+ CGF.createBasicBlock (SuspendPrefix + Twine (" .suspend" ));
242+ BasicBlock *CleanupBlock =
243+ CGF.createBasicBlock (SuspendPrefix + Twine (" .cleanup" ));
244+
245+ // If expression is ready, no need to suspend.
246+ CGF.EmitBranchOnBoolExpr (SuspendExpr.getReadyExpr (), ReadyBlock,
247+ SuspendBlock, 0 );
248+
249+ // Otherwise, emit suspend logic.
250+ CGF.EmitBlock (SuspendBlock);
251+
252+ auto &Builder = CGF.Builder ;
253+ llvm::Function *CoroSave = CGF.CGM .getIntrinsic (llvm::Intrinsic::coro_save);
254+ auto *NullPtr = llvm::ConstantPointerNull::get (CGF.CGM .Int8PtrTy );
255+ auto *SaveCall = Builder.CreateCall (CoroSave, {NullPtr});
256+
257+ CGF.CurCoro .InSuspendBlock = true ;
258+ auto *SuspendRet = CGF.EmitScalarExpr (SuspendExpr.getSuspendExpr ());
259+ CGF.CurCoro .InSuspendBlock = false ;
260+
261+ if (SuspendRet != nullptr && SuspendRet->getType ()->isIntegerTy (1 )) {
262+ // Veto suspension if requested by bool returning await_suspend.
263+ BasicBlock *RealSuspendBlock =
264+ CGF.createBasicBlock (SuspendPrefix + Twine (" .suspend.bool" ));
265+ CGF.Builder .CreateCondBr (SuspendRet, RealSuspendBlock, ReadyBlock);
266+ CGF.EmitBlock (RealSuspendBlock);
267+ }
268+
269+ // Emit the suspend point.
270+ const bool IsFinalSuspend = (Kind == AwaitKind::Final);
271+ llvm::Function *CoroSuspend =
272+ CGF.CGM .getIntrinsic (llvm::Intrinsic::coro_suspend);
273+ auto *SuspendResult = Builder.CreateCall (
274+ CoroSuspend, {SaveCall, Builder.getInt1 (IsFinalSuspend)});
275+
276+ // Create a switch capturing three possible continuations.
277+ auto *Switch = Builder.CreateSwitch (SuspendResult, Coro.SuspendBB , 2 );
278+ Switch->addCase (Builder.getInt8 (0 ), ReadyBlock);
279+ Switch->addCase (Builder.getInt8 (1 ), CleanupBlock);
280+
281+ // Emit cleanup for this suspend point.
282+ CGF.EmitBlock (CleanupBlock);
283+ CGF.EmitBranchThroughCleanup (Coro.CleanupJD );
284+
285+ // Emit await_resume expression.
286+ CGF.EmitBlock (ReadyBlock);
287+ }
288+
289+ // Check if function can throw based on prototype noexcept, also works for
290+ // destructors which are implicitly noexcept but can be marked
291+ // noexcept(false).
292+ static bool functionCanThrow (const FunctionDecl *D) {
293+ const auto *Proto = D->getType ()->getAs <FunctionProtoType>();
294+ if (!Proto) {
295+ // Function proto is not found, we conservatively assume throwing.
296+ return true ;
297+ }
298+ return !isNoexceptExceptionSpec (Proto->getExceptionSpecType ()) ||
299+ Proto->canThrow () != CT_Cannot;
300+ }
301+
302+ static bool resumeStmtCanThrow (const Stmt *S) {
303+ if (const auto *CE = dyn_cast<CallExpr>(S)) {
304+ const auto *Callee = CE->getDirectCallee ();
305+ if (!Callee)
306+ // We don't have direct callee. Conservatively assume throwing.
307+ return true ;
308+
309+ if (functionCanThrow (Callee))
310+ return true ;
311+
312+ // Fall through to visit the children.
313+ }
314+
315+ if (const auto *TE = dyn_cast<CXXBindTemporaryExpr>(S)) {
316+ // Special handling of CXXBindTemporaryExpr here as calling of Dtor of the
317+ // temporary is not part of `children()` as covered in the fall through.
318+ // We need to mark entire statement as throwing if the destructor of the
319+ // temporary throws.
320+ const auto *Dtor = TE->getTemporary ()->getDestructor ();
321+ if (functionCanThrow (Dtor))
322+ return true ;
323+
324+ // Fall through to visit the children.
325+ }
326+
327+ for (const auto *child : S->children ())
328+ if (resumeStmtCanThrow (child))
329+ return true ;
330+
331+ return false ;
332+ }
333+
334+ bool resumeExprCanThrow () {
335+ const Expr *E = SuspendExpr.getResumeExpr ();
336+ return resumeStmtCanThrow (E);
337+ }
338+ };
339+ } // namespace
301340
302341RValue CodeGenFunction::EmitCoawaitExpr (const CoawaitExpr &E,
303342 AggValueSlot aggSlot,
304343 bool ignoreResult) {
305- return emitSuspendExpression (*this , *CurCoro.Data , E,
306- CurCoro.Data ->CurrentAwaitKind , aggSlot,
307- ignoreResult, /* forLValue */ false ). RV ;
344+ return SuspendExpressionEmitter (*this , *CurCoro.Data , E,
345+ CurCoro.Data ->CurrentAwaitKind )
346+ . EmitAsRValue (aggSlot, ignoreResult) ;
308347}
309348RValue CodeGenFunction::EmitCoyieldExpr (const CoyieldExpr &E,
310349 AggValueSlot aggSlot,
311350 bool ignoreResult) {
312- return emitSuspendExpression (*this , *CurCoro.Data , E, AwaitKind::Yield,
313- aggSlot, ignoreResult, /* forLValue */ false ). RV ;
351+ return SuspendExpressionEmitter (*this , *CurCoro.Data , E, AwaitKind::Yield)
352+ . EmitAsRValue ( aggSlot, ignoreResult) ;
314353}
315354
316355void CodeGenFunction::EmitCoreturnStmt (CoreturnStmt const &S) {
@@ -343,19 +382,18 @@ CodeGenFunction::EmitCoawaitLValue(const CoawaitExpr *E) {
343382 assert (getCoroutineSuspendExprReturnType (getContext (), E)->isReferenceType () &&
344383 " Can't have a scalar return unless the return type is a "
345384 " reference type!" );
346- return emitSuspendExpression (*this , *CurCoro.Data , *E,
347- CurCoro.Data ->CurrentAwaitKind , AggValueSlot::ignored (),
348- /* ignoreResult */ false , /* forLValue */ true ). LV ;
385+ return SuspendExpressionEmitter (*this , *CurCoro.Data , *E,
386+ CurCoro.Data ->CurrentAwaitKind )
387+ . EmitAsLValue () ;
349388}
350389
351390LValue
352391CodeGenFunction::EmitCoyieldLValue (const CoyieldExpr *E) {
353392 assert (getCoroutineSuspendExprReturnType (getContext (), E)->isReferenceType () &&
354393 " Can't have a scalar return unless the return type is a "
355394 " reference type!" );
356- return emitSuspendExpression (*this , *CurCoro.Data , *E,
357- AwaitKind::Yield, AggValueSlot::ignored (),
358- /* ignoreResult*/ false , /* forLValue*/ true ).LV ;
395+ return SuspendExpressionEmitter (*this , *CurCoro.Data , *E, AwaitKind::Yield)
396+ .EmitAsLValue ();
359397}
360398
361399// Hunts for the parameter reference in the parameter copy/move declaration.
0 commit comments