clang 24.0.0git
CIRGenCleanup.cpp
Go to the documentation of this file.
1//===--- CIRGenCleanup.cpp - Bookkeeping and code emission for cleanups ---===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains code dealing with the IR generation for cleanups
10// and related information.
11//
12// A "cleanup" is a piece of code which needs to be executed whenever
13// control transfers out of a particular scope. This can be
14// conditionalized to occur only on exceptional control flow, only on
15// normal control flow, or both.
16//
17//===----------------------------------------------------------------------===//
18
19#include "CIRGenCleanup.h"
20#include "CIRGenFunction.h"
21
23#include "llvm/Support/SaveAndRestore.h"
24
25using namespace clang;
26using namespace clang::CIRGen;
27
28namespace {
29/// Return true if the expression tree contains a construct that causes cleanups
30/// to be deferred via pushFullExprCleanup.
31class ConditionalEvaluationFinder
32 : public RecursiveASTVisitor<ConditionalEvaluationFinder> {
33 bool foundConditional = false;
34 bool inResumeExpr = false;
35
36 bool traverseSuspendExpr(CoroutineSuspendExpr *e) {
37 return TraverseStmt(e->getCommonExpr()) && traverseResumeExpr(e);
38 }
39
40 /// Traverse the resume expression of \p e, except for the binding of its
41 /// result, which emitSuspendExpression destroys after the cir.await.
42 bool traverseResumeExpr(CoroutineSuspendExpr *e) {
43 Expr *resume = e->getResumeExpr();
44 if (auto *bind = dyn_cast_if_present<CXXBindTemporaryExpr>(resume))
45 resume = bind->getSubExpr();
46 llvm::SaveAndRestore inResume(inResumeExpr, true);
47 return TraverseStmt(resume);
48 }
49
50public:
51 static bool needsCleanupScope(const Stmt *s) {
52 ConditionalEvaluationFinder finder;
53 finder.TraverseStmt(const_cast<Stmt *>(s));
54 return finder.foundConditional;
55 }
56
57 static bool resumeExprDefersCleanups(const CoroutineSuspendExpr &s) {
58 ConditionalEvaluationFinder finder;
59 finder.traverseResumeExpr(const_cast<CoroutineSuspendExpr *>(&s));
60 return finder.foundConditional;
61 }
62
63 // Default arguments and default member initializers can contain conditional
64 // temporaries whose cleanups belong to the enclosing full-expression.
65 bool shouldVisitImplicitCode() const { return true; }
66
67 bool VisitAbstractConditionalOperator(AbstractConditionalOperator *) {
68 foundConditional = true;
69 return false;
70 }
71
72 bool VisitBinaryOperator(BinaryOperator *e) {
73 if (e->isLogicalOp()) {
74 foundConditional = true;
75 return false;
76 }
77 return true;
78 }
79
80 bool VisitCXXNewExpr(CXXNewExpr *e) {
81 // If the new expression has an initializer, the initializer may contain a
82 // a temporary expression that requires deferred cleanup. If we're emitting
83 // a null check, we need to make this cleanup conditional.
85 foundConditional = true;
86 return false;
87 }
88 return true;
89 }
90
91 // Don't cross evaluation-context boundaries. Only the initializers of a
92 // lambda's captures are part of the enclosing full-expression.
93 bool TraverseLambdaExpr(LambdaExpr *e) {
94 for (Expr *init : e->capture_inits())
95 if (init && !TraverseStmt(init))
96 return false;
97 return true;
98 }
99 bool TraverseBlockExpr(BlockExpr *) { return true; }
100 bool TraverseStmtExpr(StmtExpr *) { return true; }
101
102 bool TraverseGCCAsmStmt(GCCAsmStmt *s) {
103 for (Stmt *operand : s->children())
104 if (!TraverseStmt(operand))
105 return false;
106 return true;
107 }
108
109 // Traverse the common and resume expressions, whose temporaries belong to
110 // the enclosing full-expression. Ready and suspend have their own cleanup
111 // boundaries. The resume result's cleanup is handled separately.
112 bool TraverseCoawaitExpr(CoawaitExpr *e) { return traverseSuspendExpr(e); }
113 bool TraverseCoyieldExpr(CoyieldExpr *e) { return traverseSuspendExpr(e); }
114
115 bool VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *) {
116 if (!inResumeExpr)
117 return true;
118 foundConditional = true;
119 return false;
120 }
121
122 bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *e) {
123 if (!inResumeExpr || !e->getType().isDestructedType())
124 return true;
125 foundConditional = true;
126 return false;
127 }
128};
129} // namespace
130
131//===----------------------------------------------------------------------===//
132// CIRGenFunction cleanup related
133//===----------------------------------------------------------------------===//
134
136 return ConditionalEvaluationFinder::resumeExprDefersCleanups(s);
137}
138
139/// Emits all the code to cause the given temporary to be cleaned up.
141 QualType tempType, Address ptr) {
143}
144
146 assert(isInConditionalBranch());
147 mlir::Location loc = builder.getUnknownLoc();
148
149 // Place the alloca in the function entry block so it dominates everything,
150 // including both regions of any enclosing cir.cleanup.scope. We can't rely
151 // on the default curLexScope path because we may be inside a ternary branch
152 // whose LexicalScope would capture the alloca.
154 builder.getBoolTy(), CharUnits::One(), loc, "cleanup.cond",
155 /*arraySize=*/nullptr,
156 builder.getBestAllocaInsertPoint(getCurFunctionEntryBlock()));
157
158 // Initialize to false before the outermost conditional.
159 {
160 mlir::OpBuilder::InsertionGuard guard(builder);
161 builder.restoreInsertionPoint(outermostConditional->getInsertPoint());
162 cir::StoreOp store =
163 builder.createFlagStore(loc, false, active.getPointer());
164 outermostConditional->advanceInsertPoint(store);
165 }
166
167 // Set to true at the current location (inside the conditional branch).
168 builder.createFlagStore(loc, true, active.getPointer());
169
170 return active;
171}
172
176
178 EHCleanupScope &cleanup = cast<EHCleanupScope>(*ehStack.begin());
179 assert(!cleanup.hasActiveFlag() && "cleanup already has active flag?");
180 cleanup.setActiveFlag(activeFlag);
181
182 cleanup.setTestFlagInNormalCleanup(cleanup.isNormalCleanup());
183 cleanup.setTestFlagInEHCleanup(cleanup.isEHCleanup());
184}
185
187 const Stmt *s)
188 : cgf(cgf), cleanups(cgf), scope(nullptr),
189 oldFullExprCleanupScope(cgf.currentFullExprCleanupScope),
190 oldOutermostConditional(cgf.outermostConditional),
191 oldHiddenOutermostConditional(cgf.hiddenOutermostConditional),
192 deferredCleanupStackSize(cgf.deferredConditionalCleanupStack.size()),
193 conditionalScopeDepth(cgf.conditionalCleanupScopes.size()) {
194
195 assert(s && "expected an AST root for a cleanup boundary");
196 if (ConditionalEvaluationFinder::needsCleanupScope(s)) {
197 mlir::Location loc = cgf.builder.getUnknownLoc();
198 cir::CleanupKind cleanupKind = cgf.getLangOpts().Exceptions
199 ? cir::CleanupKind::All
200 : cir::CleanupKind::Normal;
201 scope = cir::CleanupScopeOp::create(
202 cgf.builder, loc, cleanupKind,
203 /*bodyBuilder=*/
204 [&](mlir::OpBuilder &b, mlir::Location loc) {},
205 /*cleanupBuilder=*/
206 [&](mlir::OpBuilder &b, mlir::Location loc) {});
207 cgf.builder.setInsertionPointToEnd(&scope.getBodyRegion().front());
208 }
209
210 // Conditional cleanup scopes are only opened while a full-expression scope
211 // is active to close them.
212 cgf.currentFullExprCleanupScope = scope;
213
214 // This full-expression owns its cleanups, even inside a conditional branch.
215 // Hide the outer conditional to keep those cleanups here, but remember it
216 // for lifetime-extended temporaries whose cleanups belong to an outer scope.
217 if (cgf.outermostConditional)
218 cgf.hiddenOutermostConditional = cgf.outermostConditional;
219 cgf.outermostConditional = nullptr;
220}
221
222/// If the alloca that backs \p addr is currently nested inside the body
223/// region of \p scope, hoist it, and any cast chain leading to it, out of the
224// scope so the alloca dominates the scope's sibling cleanup region.
226 cir::CleanupScopeOp scope) {
227 cir::AllocaOp alloca = addr.getUnderlyingAllocaOp();
228 if (!alloca)
229 return;
230
231 // If the alloca is not contained within the cleanup scope we're currently
232 // proccessing we don't need to hoist it.
233 auto cur = alloca->getParentOfType<cir::CleanupScopeOp>();
234 while (cur && cur != scope)
235 cur = cur->getParentOfType<cir::CleanupScopeOp>();
236 if (cur != scope)
237 return;
238
239 // Place the alloca at the canonical alloca insertion point of the block
240 // containing the cleanup scope op, so it groups with any preceding
241 // allocas / labels and dominates both the body and cleanup regions.
242 mlir::Block *parentBlock = scope->getBlock();
243 mlir::OpBuilder::InsertPoint ip =
245 alloca->moveBefore(parentBlock, ip.getPoint());
246
247 // Move any cast chain that consumes the alloca's result to immediately after
248 // the alloca, so the address used by the deferred cleanup also dominates the
249 // cleanup region. We walk down the chain starting from the alloca's user
250 // that the Address was built from. This is very conservative. In practice,
251 // we should only ever see alloca or address_space(alloca) operations here.
252 mlir::Value ptr = addr.getPointer();
254 for (mlir::Operation *cur = ptr.getDefiningOp(); cur && cur != alloca;) {
255 auto cast = mlir::dyn_cast<cir::CastOp>(cur);
256 if (!cast)
257 break;
258 casts.push_back(cast);
259 cur = cast.getSrc().getDefiningOp();
260 }
261 // Move casts in source order (closest to the alloca first).
262 mlir::Operation *prev = alloca;
263 for (cir::CastOp cast : llvm::reverse(casts)) {
264 cast->moveAfter(prev);
265 prev = cast;
266 }
267}
268
269/// Close a cleanup scope opened by a ConditionalEvaluation. Terminate its body
270/// region and emit this scope's slice of deferredConditionalCleanupStack into
271/// its cleanup region, in reverse of push order. An insertion point that was
272/// inside the scope's body is moved to immediately after the scope op.
274 CIRGenFunction &cgf,
276 CIRGenBuilderTy &builder = cgf.getBuilder();
277 cir::CleanupScopeOp scope = condScope.scope;
278 size_t base = condScope.deferredCleanupStackSize;
279 auto &stack = cgf.deferredConditionalCleanupStack;
280 bool hasDeferredCleanups = stack.size() > base;
281
282 // Make sure the body region has a terminator.
283 {
284 mlir::OpBuilder::InsertionGuard guard(builder);
285 mlir::Block &lastBodyBlock = scope.getBodyRegion().back();
286 builder.setInsertionPointToEnd(&lastBodyBlock);
287 if (lastBodyBlock.empty() ||
288 !lastBodyBlock.back().hasTrait<mlir::OpTrait::IsTerminator>())
289 builder.createYield(scope.getLoc());
290 }
291
292 // Each deferred cleanup references its addr from the sibling cleanup region
293 // we are about to fill. If the alloca that backs that addr was created
294 // inside this scope's body region, hoist it out so it dominates the cleanup
295 // region.
296 if (hasDeferredCleanups) {
297 for (const CIRGenFunction::PendingCleanupEntry &entry :
298 llvm::make_range(stack.begin() + base, stack.end()))
299 hoistAllocaOutOfCleanupScope(cgf, entry.addr, scope);
300 }
301
302 {
303 mlir::OpBuilder::InsertionGuard guard(builder);
304 mlir::Block &cleanupBlock = scope.getCleanupRegion().front();
305 builder.setInsertionPointToEnd(&cleanupBlock);
306
307 for (const CIRGenFunction::PendingCleanupEntry &entry :
308 llvm::reverse(llvm::make_range(stack.begin() + base, stack.end()))) {
309 if (entry.activeFlag.isValid()) {
310 // We may have hoisted this alloca out of the cleanup scope. If so, we
311 // will have also hoisted any casts between it and the address that we
312 // stored in the deferredConditionalCleanupStack. While I can't find a
313 // case where this actually happens, there is a theoretical
314 // possibility that we could have a second address that uses an alloca
315 // that has already been hoisted but a different cast chain. This
316 // assert guards against that possibility.
317 assert(entry.addr.getUnderlyingAllocaOp() &&
318 (entry.addr.getUnderlyingAllocaOp()->getBlock() ==
319 entry.addr.getPointer().getDefiningOp()->getBlock()) &&
320 "alloca and cast are in different blocks");
321 mlir::Value flag = builder.createLoad(scope.getLoc(), entry.activeFlag);
322 cir::IfOp::create(builder, scope.getLoc(), flag,
323 /*withElseRegion=*/false,
324 [&](mlir::OpBuilder &b, mlir::Location loc) {
325 cgf.emitDestroy(entry.addr, entry.type,
326 entry.destroyer);
327 builder.createYield(loc);
328 });
329 } else {
330 cgf.emitDestroy(entry.addr, entry.type, entry.destroyer);
331 }
332 }
333 builder.createYield(scope.getLoc());
334 }
335
336 stack.truncate(base);
337
338 // Move the insertion point out of the scope just closed, but only if it was
339 // inside it. Moving it unconditionally can strand it outside the region it
340 // belonged to, leaving an enclosing scope unterminated. popCleanup guards
341 // this the same way.
342 mlir::Block *insertBlock = builder.getInsertionBlock();
343 if (insertBlock &&
344 scope.getBodyRegion().findAncestorBlockInRegion(*insertBlock))
345 builder.setInsertionPointAfter(scope);
346}
347
349 ArrayRef<mlir::Value *> valuesToReload) {
350 assert(!exited && "FullExprCleanupScope::exit called twice");
351 exited = true;
352
353 cgf.currentFullExprCleanupScope = oldFullExprCleanupScope;
354 cgf.outermostConditional = oldOutermostConditional;
355 cgf.hiddenOutermostConditional = oldHiddenOutermostConditional;
356
357 size_t oldSize = deferredCleanupStackSize;
358
359 if (!scope) {
360 assert(cgf.conditionalCleanupScopes.size() == conditionalScopeDepth &&
361 "conditional cleanup scope opened without one to close it");
362 cgf.deferredConditionalCleanupStack.truncate(oldSize);
363 cleanups.forceCleanup(valuesToReload);
364 return;
365 }
366
367 // Spill any values that callers need after the scope is closed.
368 SmallVector<Address> tempAllocas;
369 for (mlir::Value *valPtr : valuesToReload) {
370 mlir::Value val = *valPtr;
371 if (!val) {
372 tempAllocas.push_back(Address::invalid());
373 continue;
374 }
375 Address temp = cgf.createDefaultAlignTempAlloca(val.getType(), val.getLoc(),
376 "tmp.exprcleanup");
377 tempAllocas.push_back(temp);
378 cgf.builder.createStore(val.getLoc(), val, temp);
379 }
380
381 // Close the cleanup scopes opened by conditionals in this full expression,
382 // innermost first. The EH cleanups popped below own the scopes enclosing
383 // these, so they are closed after.
384 while (cgf.conditionalCleanupScopes.size() > conditionalScopeDepth)
386 cgf.conditionalCleanupScopes.pop_back_val());
387
388 // Pop any EH cleanups that were pushed during the expression but leave
389 // any lifetime-extended cleanups so that they can be promoted to the EH
390 // stack after we've finished emitting any deferred cleanups.
391 cleanups.forceCleanupExceptLifetimeExtended();
392
393 // Make sure the cleanup scope body region has a terminator.
394 {
395 mlir::OpBuilder::InsertionGuard guard(cgf.builder);
396 mlir::Block &lastBodyBlock = scope.getBodyRegion().back();
397 cgf.builder.setInsertionPointToEnd(&lastBodyBlock);
398 if (lastBodyBlock.empty() ||
399 !lastBodyBlock.back().hasTrait<mlir::OpTrait::IsTerminator>())
400 cgf.builder.createYield(scope.getLoc());
401 }
402
403 // Deferred cleanups are emitted into the conditional scopes closed above,
404 // so this scope's cleanup region is empty and canonicalization will inline
405 // the scope away.
406 {
407 mlir::OpBuilder::InsertionGuard guard(cgf.builder);
408 mlir::Block &cleanupBlock = scope.getCleanupRegion().front();
409 cgf.builder.setInsertionPointToEnd(&cleanupBlock);
410 cgf.builder.createYield(scope.getLoc());
411 }
412
413 assert(cgf.deferredConditionalCleanupStack.size() == oldSize &&
414 "deferred cleanups were not consumed by a conditional scope");
415 cgf.builder.setInsertionPointAfter(scope);
416
417 // Promote any lifetime-extended cleanups onto the EH scope stack. The new
418 // cir.cleanup.scope ops created here will wrap any code in the enclosing
419 // scope, including reloads of any spilled values below, so the
420 // lifetime-extended destructors run at the correct point.
421 cleanups.forceLifetimeExtendedCleanups();
422
423 // Reload spilled values now that the builder is after the closed scope.
424 for (auto [addr, valPtr] : llvm::zip(tempAllocas, valuesToReload)) {
425 if (!addr.isValid())
426 continue;
427 *valPtr = cgf.builder.createLoad(valPtr->getLoc(), addr);
428 }
429}
430
431//===----------------------------------------------------------------------===//
432// EHScopeStack
433//===----------------------------------------------------------------------===//
434
435void EHScopeStack::Cleanup::anchor() {}
436
439 stable_iterator si = getInnermostNormalCleanup();
440 stable_iterator se = stable_end();
441 while (si != se) {
442 EHCleanupScope &cleanup = llvm::cast<EHCleanupScope>(*find(si));
443 if (cleanup.isActive())
444 return si;
445 si = cleanup.getEnclosingNormalCleanup();
446 }
447 return stable_end();
448}
449
450/// Push an entry of the given size onto this protected-scope stack.
451char *EHScopeStack::allocate(size_t size) {
452 size = llvm::alignTo(size, ScopeStackAlignment);
453 if (!startOfBuffer) {
454 unsigned capacity = llvm::PowerOf2Ceil(std::max<size_t>(size, 1024ul));
455 startOfBuffer = std::make_unique<char[]>(capacity);
456 startOfData = endOfBuffer = startOfBuffer.get() + capacity;
457 } else if (static_cast<size_t>(startOfData - startOfBuffer.get()) < size) {
458 unsigned currentCapacity = endOfBuffer - startOfBuffer.get();
459 unsigned usedCapacity =
460 currentCapacity - (startOfData - startOfBuffer.get());
461 unsigned requiredCapacity = usedCapacity + size;
462 // We know from the 'else if' condition that requiredCapacity is greater
463 // than currentCapacity.
464 unsigned newCapacity = llvm::PowerOf2Ceil(requiredCapacity);
465
466 std::unique_ptr<char[]> newStartOfBuffer =
467 std::make_unique<char[]>(newCapacity);
468 char *newEndOfBuffer = newStartOfBuffer.get() + newCapacity;
469 char *newStartOfData = newEndOfBuffer - usedCapacity;
470 memcpy(newStartOfData, startOfData, usedCapacity);
471 startOfBuffer.swap(newStartOfBuffer);
472 endOfBuffer = newEndOfBuffer;
473 startOfData = newStartOfData;
474 }
475
476 assert(startOfBuffer.get() + size <= startOfData);
477 startOfData -= size;
478 return startOfData;
479}
480
481void EHScopeStack::deallocate(size_t size) {
482 startOfData += llvm::alignTo(size, ScopeStackAlignment);
483}
484
485void *EHScopeStack::pushCleanup(CleanupKind kind, size_t size) {
486 char *buffer = allocate(EHCleanupScope::getSizeForCleanupSize(size));
487 bool isNormalCleanup = kind & NormalCleanup;
488 bool isEHCleanup = kind & EHCleanup;
489 bool isLifetimeMarker = kind & LifetimeMarker;
490 bool skipCleanupScope = false;
491
492 cir::CleanupKind cleanupKind = cir::CleanupKind::All;
493 if (isEHCleanup && cgf->getLangOpts().Exceptions) {
494 cleanupKind =
495 isNormalCleanup ? cir::CleanupKind::All : cir::CleanupKind::EH;
496 } else {
497 // Exceptions are disabled (or no EH flag was requested). Drop the EH
498 // flag so the scope entry stays consistent with the op's cleanup kind.
499 isEHCleanup = false;
500 if (isNormalCleanup)
501 cleanupKind = cir::CleanupKind::Normal;
502 else
503 skipCleanupScope = true;
504 }
505
506 // While emitting a variable with captured cleanups, suppress
507 // cir.cleanup.scope creation. The variable's cleanups are captured on the EH
508 // stack and later emitted into the cleanup region of the op that owns them.
509 if (capturingCleanups)
510 skipCleanupScope = true;
511
512 cir::CleanupScopeOp cleanupScope = nullptr;
513 if (!skipCleanupScope) {
514 CIRGenBuilderTy &builder = cgf->getBuilder();
515 mlir::Location loc = builder.getUnknownLoc();
516 cleanupScope = cir::CleanupScopeOp::create(
517 builder, loc, cleanupKind,
518 /*bodyBuilder=*/
519 [&](mlir::OpBuilder &b, mlir::Location loc) {
520 // Terminations will be handled in popCleanup
521 },
522 /*cleanupBuilder=*/
523 [&](mlir::OpBuilder &b, mlir::Location loc) {
524 // Terminations will be handled after emiting cleanup
525 });
526
527 builder.setInsertionPointToEnd(&cleanupScope.getBodyRegion().back());
528 }
529
530 // Per C++ [except.terminate], it is implementation-defined whether none,
531 // some, or all cleanups are called before std::terminate. Thus, when
532 // terminate is the current EH scope, we may skip adding any EH cleanup
533 // scopes.
534 if (innermostEHScope != stable_end() &&
535 find(innermostEHScope)->getKind() == EHScope::Terminate)
536 isEHCleanup = false;
537
538 EHCleanupScope *scope = new (buffer)
539 EHCleanupScope(isNormalCleanup, isEHCleanup, size, cleanupScope,
540 innermostNormalCleanup, innermostEHScope);
541
542 if (isNormalCleanup)
543 innermostNormalCleanup = stable_begin();
544
545 if (isEHCleanup)
546 innermostEHScope = stable_begin();
547
548 if (isLifetimeMarker)
549 scope->setLifetimeMarker();
550
551 // With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup
552 if (cgf->getLangOpts().EHAsynch && isEHCleanup && !isLifetimeMarker &&
553 cgf->getTarget().getCXXABI().isMicrosoft())
554 cgf->cgm.errorNYI("push seh cleanup");
555
556 return scope->getCleanupBuffer();
557}
558
560 assert(!empty() && "popping exception stack when not empty");
561
562 assert(isa<EHCleanupScope>(*begin()));
563 EHCleanupScope &cleanup = cast<EHCleanupScope>(*begin());
564 innermostNormalCleanup = cleanup.getEnclosingNormalCleanup();
565 innermostEHScope = cleanup.getEnclosingEHScope();
566 deallocate(cleanup.getAllocatedSize());
567
568 cir::CleanupScopeOp cleanupScope = cleanup.getCleanupScopeOp();
569 if (cleanupScope) {
570 auto *block = &cleanupScope.getBodyRegion().back();
571 if (!block->mightHaveTerminator()) {
572 mlir::OpBuilder::InsertionGuard guard(cgf->getBuilder());
573 cgf->getBuilder().setInsertionPointToEnd(block);
574 cir::YieldOp::create(cgf->getBuilder(),
575 cgf->getBuilder().getUnknownLoc());
576 }
577 // If the insertion point was inside the cleanup scope we just closed, move
578 // it to immediate after the scope.
579 mlir::Block *insertBlock = cgf->getBuilder().getInsertionBlock();
580 if (insertBlock &&
581 cleanupScope.getBodyRegion().findAncestorBlockInRegion(*insertBlock))
582 cgf->getBuilder().setInsertionPointAfter(cleanupScope);
583 }
584
585 // Destroy the cleanup.
586 cleanup.destroy();
587}
588
590 for (stable_iterator si = getInnermostEHScope(); si != stable_end();) {
591 if (auto *cleanup = dyn_cast<EHCleanupScope>(&*find(si))) {
592 if (cleanup->isLifetimeMarker()) {
593 // Skip lifetime markers and continue from the enclosing EH scope
594 si = cleanup->getEnclosingEHScope();
595 continue;
596 }
597 }
598 return true;
599 }
600 return false;
601}
602
603/// The given cleanup block is being deactivated. Configure a cleanup variable
604/// if necessary.
607 mlir::Operation *dominatingIP) {
609
610 // An EH-only cleanup has both flags clear when pushCleanup dropped
611 // the EH flag. That happens when exceptions are disabled, and when
612 // the innermost EH scope is a terminate scope. The cleanup never
613 // runs, so there is no active flag to clear.
614 if (!scope.isNormalCleanup() && !scope.isEHCleanup())
615 return;
616
618 scope.setTestFlagInEHCleanup(scope.isEHCleanup());
619
620 CIRGenBuilderTy &builder = cgf.getBuilder();
621
622 // If the cleanup block doesn't exist yet, create it and set its initial
623 // value to `true`. If we are inside a conditional branch, the value must be
624 // initialized before the conditional branch begins.
625 Address var = scope.getActiveFlag();
626 if (!var.isValid()) {
627 mlir::Location loc = builder.getUnknownLoc();
628
630 loc, "cleanup.isactive");
631 scope.setActiveFlag(var);
632
633 assert(dominatingIP && "no existing variable and no dominating IP!");
634
635 if (cgf.isInConditionalBranch()) {
636 mlir::Value val = builder.getBool(true, loc);
637 cgf.setBeforeOutermostConditional(val, var);
638 } else {
639 mlir::OpBuilder::InsertionGuard guard(builder);
640 builder.setInsertionPoint(dominatingIP);
641 builder.createFlagStore(loc, true, var.getPointer());
642 }
643 }
644
645 // The code above sets the `isActive` flag to `true` as its initial state
646 // at the point where the variable is created. The code below sets it to
647 // `false` at the point where the cleanup is deactivated.
648 mlir::Location loc = builder.getUnknownLoc();
649 builder.createFlagStore(loc, false, var.getPointer());
650}
651
653 EHCleanupScope &scope) {
654 cir::CleanupScopeOp op = scope.getCleanupScopeOp();
655 return op &&
656 llvm::any_of(cgf.conditionalCleanupScopes, [&](const auto &condScope) {
657 return op->isProperAncestor(condScope.scope);
658 });
659}
660
661/// Deactivate a cleanup that was created in an active state.
663 mlir::Operation *dominatingIP) {
664 assert(c != ehStack.stable_end() && "deactivating bottom of stack?");
666 assert(scope.isActive() && "double deactivation");
667
668 // If it's the top of the stack, just pop it, but do so only if it belongs
669 // to the current RunCleanupsScope. If a conditional opened a cleanup scope
670 // keep it open till the end of the full-expression.
671 if (c == ehStack.stable_begin() &&
672 currentCleanupStackDepth.strictlyEncloses(c) &&
674 popCleanupBlock(/*forDeactivation=*/true);
675 return;
676 }
677
678 // Otherwise, follow the general case.
679 setupCleanupBlockDeactivation(*this, c, dominatingIP);
680
681 scope.setActive(false);
682}
683
684static void emitCleanupBody(CIRGenFunction &cgf, EHScopeStack::Cleanup *cleanup,
686 Address activeFlag, mlir::Location loc) {
687 CIRGenBuilderTy &builder = cgf.getBuilder();
688
689 // Ask the cleanup to emit itself.
690 assert(cgf.haveInsertPoint() && "expected insertion point");
691
692 if (activeFlag.isValid()) {
693 mlir::Value isActive = builder.createFlagLoad(loc, activeFlag.getPointer());
694 cir::IfOp::create(builder, loc, isActive,
695 /*withElseRegion=*/false,
696 /*thenBuilder=*/
697 [&](mlir::OpBuilder &, mlir::Location) {
698 cleanup->emit(cgf, flags);
699 assert(cgf.haveInsertPoint() &&
700 "cleanup ended with no insertion point?");
701 builder.createYield(loc);
702 });
703 } else {
704 cleanup->emit(cgf, flags);
705 assert(cgf.haveInsertPoint() && "cleanup ended with no insertion point?");
706 }
707}
708
709static void emitCleanup(CIRGenFunction &cgf, cir::CleanupScopeOp cleanupScope,
710 EHScopeStack::Cleanup *cleanup,
712 Address activeFlag) {
713 CIRGenBuilderTy &builder = cgf.getBuilder();
714 mlir::Block &block = cleanupScope.getCleanupRegion().back();
715
716 mlir::OpBuilder::InsertionGuard guard(builder);
717 builder.setInsertionPointToStart(&block);
718
719 emitCleanupBody(cgf, cleanup, flags, activeFlag, cleanupScope.getLoc());
720
721 mlir::Block &cleanupRegionLastBlock = cleanupScope.getCleanupRegion().back();
722 if (cleanupRegionLastBlock.empty() ||
723 !cleanupRegionLastBlock.back().hasTrait<mlir::OpTrait::IsTerminator>()) {
724 mlir::OpBuilder::InsertionGuard guardCase(builder);
725 builder.setInsertionPointToEnd(&cleanupRegionLastBlock);
726 builder.createYield(cleanupScope.getLoc());
727 }
728}
729
730/// Check whether a cleanup scope body contains any non-yield exits that branch
731/// through the cleanup. These exits branch through the cleanup and require
732/// the normal cleanup to be executed even when the cleanup has been
733/// deactivated.
734static bool bodyHasBranchThroughExits(mlir::Region &bodyRegion) {
735 return bodyRegion
736 .walk([&](mlir::Operation *op) {
738 return mlir::WalkResult::interrupt();
739 return mlir::WalkResult::advance();
740 })
741 .wasInterrupted();
742}
743
744/// Pop a cleanup block from the stack.
745///
746/// \param forDeactivation - When true, this indicates that the cleanup block
747/// is being popped because it was deactivated while at the top of the stack.
748void CIRGenFunction::popCleanupBlock(bool forDeactivation) {
749 assert(!ehStack.empty() && "cleanup stack is empty!");
750 assert(isa<EHCleanupScope>(*ehStack.begin()) && "top not a cleanup!");
752
753 // If we pushed an EH-only cleanup but exceptions are disabled, it will leave
754 // an effectively empty cleanup on the EH stack. In that case, there is
755 // nothing to do here except pop the cleanup.
756 cir::CleanupScopeOp cleanupScope = scope.getCleanupScopeOp();
757 if (!cleanupScope) {
758 assert(!scope.isNormalCleanup() && !scope.isEHCleanup() &&
759 "missing cir.cleanup.scope for active cleanup");
760 ehStack.popCleanup();
761 return;
762 }
763
764 bool requiresNormalCleanup = scope.isNormalCleanup();
765 bool requiresEHCleanup = scope.isEHCleanup();
766
767 // When we're popping a cleanup to deactivate it, we need to know if anything
768 // in the cleanup scope body region branches through the cleanup handler
769 // before the entire cleanup scope body has executed. If the cleanup scope
770 // body falls through, we don't want to emit normal cleanup code. However,
771 // if the cleanup body region contains early exits (return or goto), we do
772 // need to execute the normal cleanup when the early exit is taken. To handle
773 // that case, we guard the cleanup with an "active" flag so that it executes
774 // conditionally and set the flag to false when the cleanup body falls
775 // through. Classic codegen tracks this state with "hasBranches" and
776 // "getFixupDepth" on the cleanup scope, but because CIR uses structured
777 // control flow, we need to check for early exits and insert the active
778 // flag handling here. Note that when a cleanup is deactivated while not at
779 // the top of the stack, the active flag gets created in
780 // setupCleanupBlockDeactivation.
781 if (forDeactivation && requiresNormalCleanup) {
782 if (bodyHasBranchThroughExits(cleanupScope.getBodyRegion())) {
783 // The active flag shouldn't exist if the scope was at the top of the
784 // stack when it was deactivated.
785 assert(!scope.getActiveFlag().isValid() && "active flag already set");
786
787 // Create the flag.
788 mlir::Location loc = builder.getUnknownLoc();
790 builder.getBoolTy(), CharUnits::One(), loc, "cleanup.isactive");
791
792 // Initialize the flag to true before the cleanup scope (the point where
793 // the cleanup becomes active).
794 {
795 mlir::OpBuilder::InsertionGuard guard(builder);
796 builder.setInsertionPoint(cleanupScope);
797 builder.createFlagStore(loc, true, activeFlag.getPointer());
798 }
799
800 // Set the flag to false at the end of the cleanup scope body region.
801 assert(builder.getInsertionBlock() ==
802 &cleanupScope.getBodyRegion().back() &&
803 "expected insertion point in cleanup body");
804 builder.createFlagStore(loc, false, activeFlag.getPointer());
805
806 scope.setActiveFlag(activeFlag);
807 scope.setTestFlagInNormalCleanup(true);
808 } else {
809 // If the cleanup was pushed on the stack as normal+eh, downgrade it to
810 // eh-only.
811 if (requiresEHCleanup)
812 cleanupScope.setCleanupKind(cir::CleanupKind::EH);
813 requiresNormalCleanup = false;
814 }
815 }
816
817 Address normalActiveFlag = scope.shouldTestFlagInNormalCleanup()
818 ? scope.getActiveFlag()
820 Address ehActiveFlag = scope.shouldTestFlagInEHCleanup()
821 ? scope.getActiveFlag()
823
824 // If we don't need the cleanup at all, we're done.
825 if (!requiresNormalCleanup && !requiresEHCleanup) {
826 // If we get here, the cleanup scope isn't needed. Rather than try to move
827 // the contents of its body region out of the cleanup and erase it, we just
828 // add a yield to the cleanup region to make it valid but no-op. It will be
829 // erased during canonicalization.
830 mlir::Block &cleanupBlock = cleanupScope.getCleanupRegion().back();
831 if (!cleanupBlock.mightHaveTerminator()) {
832 mlir::OpBuilder::InsertionGuard guard(builder);
833 builder.setInsertionPointToEnd(&cleanupBlock);
834 cir::YieldOp::create(builder, builder.getUnknownLoc());
835 }
836 ehStack.popCleanup();
837 return;
838 }
839
840 // Copy the cleanup emission data out. This uses either a stack
841 // array or malloc'd memory, depending on the size, which is
842 // behavior that SmallVector would provide, if we could use it
843 // here. Unfortunately, if you ask for a SmallVector<char>, the
844 // alignment isn't sufficient.
845 auto *cleanupSource = reinterpret_cast<char *>(scope.getCleanupBuffer());
847 cleanupBufferStack[8 * sizeof(void *)];
848 std::unique_ptr<char[]> cleanupBufferHeap;
849 size_t cleanupSize = scope.getCleanupSize();
850 EHScopeStack::Cleanup *cleanup;
851
852 // This is necessary because we are going to deallocate the cleanup
853 // (in popCleanup) before we emit it.
854 if (cleanupSize <= sizeof(cleanupBufferStack)) {
855 memcpy(cleanupBufferStack, cleanupSource, cleanupSize);
856 cleanup = reinterpret_cast<EHScopeStack::Cleanup *>(cleanupBufferStack);
857 } else {
858 cleanupBufferHeap.reset(new char[cleanupSize]);
859 memcpy(cleanupBufferHeap.get(), cleanupSource, cleanupSize);
860 cleanup =
861 reinterpret_cast<EHScopeStack::Cleanup *>(cleanupBufferHeap.get());
862 }
863
864 EHScopeStack::Cleanup::Flags cleanupFlags;
865 if (scope.isNormalCleanup())
866 cleanupFlags.setIsNormalCleanupKind();
867 if (scope.isEHCleanup())
868 cleanupFlags.setIsEHCleanupKind();
869
870 // Determine the active flag for the cleanup handler.
871 Address cleanupActiveFlag = normalActiveFlag.isValid() ? normalActiveFlag
872 : ehActiveFlag.isValid() ? ehActiveFlag
874
875 // In CIR, the cleanup code is emitted into the cleanup region of the
876 // cir.cleanup.scope op. There is no CFG threading needed — the FlattenCFG
877 // pass handles lowering the structured cleanup scope.
878 ehStack.popCleanup();
879 scope.markEmitted();
880 emitCleanup(*this, cleanupScope, cleanup, cleanupFlags, cleanupActiveFlag);
881}
882
884 mlir::Location loc) {
885 // The captured cleanups were pushed while EHScopeStack was capturing
886 // cleanups, so they own no cir.cleanup.scope. Emit them directly into the
887 // owning op's cleanup region (the current insertion point), popping each off
888 // the EH stack.
889 while (ehStack.stable_begin() != depth) {
890 assert(isa<EHCleanupScope>(*ehStack.begin()) && "top not a cleanup!");
892 assert(!scope.getCleanupScopeOp() &&
893 "captured cleanup should not own a cleanup scope");
894
895 EHScopeStack::Cleanup::Flags cleanupFlags;
896 if (scope.isNormalCleanup())
897 cleanupFlags.setIsNormalCleanupKind();
898 if (scope.isEHCleanup())
899 cleanupFlags.setIsEHCleanupKind();
900
901 // A captured cleanup may be guarded by an active flag. The destructor
902 // cleanups of a loop condition variable and of the temporaries that its
903 // initializer lifetime-extends have one that is false while its
904 // initializer runs, so a throwing initializer does not destroy the
905 // not-yet-constructed variables. The lifetime-end cleanup has no flag
906 // because its lifetime starts before initialization. For a loop, each
907 // emission serves both the normal per-iteration exit and the EH unwind
908 // path.
909 Address activeFlag = scope.getActiveFlag();
910
911 // Copy the cleanup emission data out before popping, since popCleanup
912 // deallocates the entry. This mirrors popCleanupBlock.
913 auto *cleanupSource = reinterpret_cast<char *>(scope.getCleanupBuffer());
915 cleanupBufferStack[8 * sizeof(void *)];
916 std::unique_ptr<char[]> cleanupBufferHeap;
917 size_t cleanupSize = scope.getCleanupSize();
918 EHScopeStack::Cleanup *cleanup;
919 if (cleanupSize <= sizeof(cleanupBufferStack)) {
920 memcpy(cleanupBufferStack, cleanupSource, cleanupSize);
921 cleanup = reinterpret_cast<EHScopeStack::Cleanup *>(cleanupBufferStack);
922 } else {
923 cleanupBufferHeap.reset(new char[cleanupSize]);
924 memcpy(cleanupBufferHeap.get(), cleanupSource, cleanupSize);
925 cleanup =
926 reinterpret_cast<EHScopeStack::Cleanup *>(cleanupBufferHeap.get());
927 }
928
929 ehStack.popCleanup();
930 emitCleanupBody(*this, cleanup, cleanupFlags, activeFlag, loc);
931 }
932}
933
934/// Pops cleanup blocks until the given savepoint is reached.
936 EHScopeStack::stable_iterator oldCleanupStackDepth,
937 ArrayRef<mlir::Value *> valuesToReload) {
938 // If the current stack depth is the same as the cleanup stack depth,
939 // we won't be exiting any cleanup scopes, so we don't need to reload
940 // any values.
941 bool requiresCleanup = false;
942 for (auto it = ehStack.begin(), ie = ehStack.find(oldCleanupStackDepth);
943 it != ie; ++it) {
944 if (isa<EHCleanupScope>(&*it)) {
945 requiresCleanup = true;
946 break;
947 }
948 }
949
950 // If there are values that we need to keep live, spill them now before
951 // we pop the cleanup blocks. These are passed as pointers to mlir::Value
952 // because we're going to replace them with the reloaded value.
953 SmallVector<Address> tempAllocas;
954 if (requiresCleanup) {
955 for (mlir::Value *valPtr : valuesToReload) {
956 mlir::Value val = *valPtr;
957 if (!val)
958 continue;
959
960 // TODO(cir): Check for static allocas.
961
962 Address temp = createDefaultAlignTempAlloca(val.getType(), val.getLoc(),
963 "tmp.exprcleanup");
964 tempAllocas.push_back(temp);
965 builder.createStore(val.getLoc(), val, temp);
966 }
967 }
968
969 // Pop cleanup blocks until we reach the base stack depth for the
970 // current scope.
971 while (ehStack.stable_begin() != oldCleanupStackDepth)
973
974 // Reload the values that we spilled, if necessary.
975 if (requiresCleanup) {
976 for (auto [addr, valPtr] : llvm::zip(tempAllocas, valuesToReload)) {
977 mlir::Location loc = valPtr->getLoc();
978 *valPtr = builder.createLoad(loc, addr);
979 }
980 }
981}
982
983/// Pops cleanup blocks until the given savepoint is reached, then add the
984/// cleanups from the given savepoint in the lifetime-extended cleanups stack.
986 EHScopeStack::stable_iterator oldCleanupStackDepth,
987 size_t oldLifetimeExtendedSize, ArrayRef<mlir::Value *> valuesToReload) {
988 popCleanupBlocks(oldCleanupStackDepth, valuesToReload);
989
990 // Promote deferred lifetime-extended cleanups onto the EH scope stack.
991 for (const PendingCleanupEntry &cleanup : llvm::make_range(
992 lifetimeExtendedCleanupStack.begin() + oldLifetimeExtendedSize,
995 lifetimeExtendedCleanupStack.truncate(oldLifetimeExtendedSize);
996}
static void setupCleanupBlockDeactivation(CIRGenFunction &cgf, EHScopeStack::stable_iterator c, mlir::Operation *dominatingIP)
The given cleanup block is being deactivated.
static bool bodyHasBranchThroughExits(mlir::Region &bodyRegion)
Check whether a cleanup scope body contains any non-yield exits that branch through the cleanup.
static void closeConditionalCleanupScope(CIRGenFunction &cgf, const CIRGenFunction::ConditionalCleanupScope &condScope)
Close a cleanup scope opened by a ConditionalEvaluation.
static bool enclosesOpenConditionalCleanupScope(CIRGenFunction &cgf, EHCleanupScope &scope)
static void hoistAllocaOutOfCleanupScope(CIRGenFunction &cgf, Address addr, cir::CleanupScopeOp scope)
If the alloca that backs addr is currently nested inside the body region of scope,...
static void emitCleanupBody(CIRGenFunction &cgf, EHScopeStack::Cleanup *cleanup, EHScopeStack::Cleanup::Flags flags, Address activeFlag, mlir::Location loc)
static void emitCleanup(CIRGenFunction &cgf, cir::CleanupScopeOp cleanupScope, EHScopeStack::Cleanup *cleanup, EHScopeStack::Cleanup::Flags flags, Address activeFlag)
static Decl::Kind getKind(const Decl *D)
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
__DEVICE__ void * memcpy(void *__a, const void *__b, size_t __c)
cir::ConstantOp getBool(bool state, mlir::Location loc)
cir::StoreOp createFlagStore(mlir::Location loc, bool val, mlir::Value dst)
static OpBuilder::InsertPoint getBestAllocaInsertPoint(mlir::Block *block)
cir::YieldOp createYield(mlir::Location loc, mlir::ValueRange value={})
Create a yield operation.
cir::LoadOp createFlagLoad(mlir::Location loc, mlir::Value addr)
Emit a load from an boolean flag variable.
cir::BoolType getBoolTy()
child_range children()
Definition Stmt.h:3449
static bool isLogicalOp(Opcode Opc)
Definition Expr.h:4240
mlir::Value getPointer() const
Definition Address.h:98
static Address invalid()
Definition Address.h:76
bool isValid() const
Definition Address.h:77
cir::AllocaOp getUnderlyingAllocaOp() const
Return the underlying alloca for this address, if any.
Definition Address.h:152
cir::LoadOp createLoad(mlir::Location loc, Address addr, bool isVolatile=false, bool isNontemporal=false)
FullExprCleanupScope(CIRGenFunction &cgf, const Stmt *s)
s is the AST root for one cleanup boundary: a full-expression, an asm statement, or the construction ...
void exit(ArrayRef< mlir::Value * > valuesToReload={})
llvm::SmallVector< PendingCleanupEntry > lifetimeExtendedCleanupStack
ConditionalEvaluation * hiddenOutermostConditional
void initFullExprCleanup()
Set up the last cleanup that was pushed as a conditional full-expression cleanup.
void emitCapturedCleanups(EHScopeStack::stable_iterator depth, mlir::Location loc)
Emit the captured cleanups (those pushed above depth while EHScopeStack was capturing cleanups) at th...
mlir::Block * getCurFunctionEntryBlock()
void setBeforeOutermostConditional(mlir::Value value, Address addr)
ConditionalEvaluation * outermostConditional
static bool resumeExprDefersCleanups(const CoroutineSuspendExpr &s)
Whether the resume expression of s contains a construct that defers cleanups to the end of the enclos...
EHScopeStack ehStack
Tracks function scope overall cleanup handling.
llvm::SmallVector< PendingCleanupEntry > deferredConditionalCleanupStack
Cleanups for temporaries constructed inside a conditional.
llvm::SmallVector< ConditionalCleanupScope > conditionalCleanupScopes
void pushDestroy(QualType::DestructionKind dtorKind, Address addr, QualType type)
Push the standard destructor for the given type as at least a normal cleanup.
void initFullExprCleanupWithFlag(Address activeFlag)
Address createCleanupActiveFlag()
Create an active flag variable for use with conditional cleanups.
void deactivateCleanupBlock(EHScopeStack::stable_iterator cleanup, mlir::Operation *dominatingIP)
Deactivates the given cleanup block.
bool haveInsertPoint() const
True if an insertion point is defined.
void emitCXXTemporary(const CXXTemporary *temporary, QualType tempType, Address ptr)
Emits all the code to cause the given temporary to be cleaned up.
void popCleanupBlocks(EHScopeStack::stable_iterator oldCleanupStackDepth, ArrayRef< mlir::Value * > valuesToReload={})
Takes the old cleanup stack size and emits the cleanup blocks that have been added.
CIRGenBuilderTy & getBuilder()
void emitDestroy(Address addr, QualType type, Destroyer *destroyer)
Immediately perform the destruction of the given object.
void pushPendingCleanupToEHStack(const PendingCleanupEntry &entry)
Promote a single pending cleanup entry onto the EH scope stack.
void popCleanupBlock(bool forDeactivation=false)
Pop a cleanup block from the stack.
cir::CleanupScopeOp currentFullExprCleanupScope
The cir.cleanup.scope of the innermost FullExprCleanupScope that materialized one,...
EHScopeStack::stable_iterator currentCleanupStackDepth
CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder, bool suppressNewContext=false)
Address createTempAllocaWithoutCast(mlir::Type ty, CharUnits align, mlir::Location loc, const Twine &name="tmp", mlir::Value arraySize=nullptr, mlir::OpBuilder::InsertPoint ip={})
This creates a alloca and inserts it into the entry block of the current region.
Address createDefaultAlignTempAlloca(mlir::Type ty, mlir::Location loc, const Twine &name)
CreateDefaultAlignTempAlloca - This creates an alloca with the default alignment of the corresponding...
A cleanup scope which generates the cleanup blocks lazily.
void setTestFlagInEHCleanup(bool value)
void setTestFlagInNormalCleanup(bool value)
cir::CleanupScopeOp getCleanupScopeOp()
static size_t getSizeForCleanupSize(size_t size)
Gets the size required for a lazy cleanup scope with the given cleanup-data requirements.
void setActiveFlag(Address var)
bool shouldTestFlagInNormalCleanup() const
void setActive(bool isActive)
Information for lazily generating a cleanup.
A saved depth on the scope stack.
void popCleanup()
Pops a cleanup scope off the stack. This is private to CIRGenCleanup.cpp.
iterator find(stable_iterator savePoint) const
Turn a stable reference to a scope depth into a unstable pointer to the EH stack.
bool requiresCatchOrCleanup() const
stable_iterator getInnermostActiveNormalCleanup() const
bool hasInitializer() const
Whether this new-expression has any initializer at all.
Definition ExprCXX.h:2529
bool shouldNullCheckAllocation() const
True if the allocation result needs to be null-checked.
Definition ExprCXX.cpp:333
Represents a C++ temporary.
Definition ExprCXX.h:1464
static CharUnits One()
Construct a CharUnits quantity of one.
Definition CharUnits.h:55
Represents an expression that might suspend coroutine execution; either a co_await or co_yield expres...
Definition ExprCXX.h:5309
Expr * getResumeExpr() const
Definition ExprCXX.h:5373
Expr * getCommonExpr() const
Definition ExprCXX.h:5358
QualType getType() const
Definition Expr.h:146
llvm::iterator_range< capture_init_iterator > capture_inits()
Retrieve the initialization expressions for this lambda's captures.
Definition ExprCXX.h:2088
A (possibly-)qualified type.
Definition TypeBase.h:938
DestructionKind isDestructedType() const
Returns a nonzero value if objects of this type require non-trivial work to clean up after.
Definition TypeBase.h:1561
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Stmt - This represents one statement.
Definition Stmt.h:85
@ EHCleanup
Denotes a cleanup that should run when a scope is exited using exceptional control flow (a throw stat...
@ NormalCleanup
Denotes a cleanup that should run when a scope is exited using normal control flow (falling off the e...
tooling::Replacements cleanup(const FormatStyle &Style, StringRef Code, ArrayRef< tooling::Range > Ranges, StringRef FileName="<stdin>")
Clean up any erroneous/redundant code in the given Ranges in Code.
Definition Format.cpp:4563
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
U cast(CodeGen::Address addr)
Definition Address.h:327
One record per cir.cleanup.scope opened by a ConditionalEvaluation, as described above.
size_t deferredCleanupStackSize
The size of deferredConditionalCleanupStack when this scope was opened.
A cleanup whose destructor call is not emitted where the cleanup is registered.