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
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
35public:
36 // Default arguments and default member initializers can contain conditional
37 // temporaries whose cleanups belong to the enclosing full-expression.
38 bool shouldVisitImplicitCode() const { return true; }
39
40 bool found() const { return foundConditional; }
41
42 bool VisitAbstractConditionalOperator(AbstractConditionalOperator *) {
43 foundConditional = true;
44 return false;
45 }
46
47 bool VisitBinaryOperator(BinaryOperator *e) {
48 if (e->isLogicalOp()) {
49 foundConditional = true;
50 return false;
51 }
52 return true;
53 }
54
55 bool VisitCXXNewExpr(CXXNewExpr *e) {
56 // If the new expression has an initializer, the initializer may contain a
57 // a temporary expression that requires deferred cleanup. If we're emitting
58 // a null check, we need to make this cleanup conditional.
60 foundConditional = true;
61 return false;
62 }
63 return true;
64 }
65
66 // Don't cross evaluation-context boundaries. Only the initializers of a
67 // lambda's captures are part of the enclosing full-expression.
68 bool TraverseLambdaExpr(LambdaExpr *e) {
69 for (Expr *init : e->capture_inits())
70 if (init && !TraverseStmt(init))
71 return false;
72 return true;
73 }
74 bool TraverseBlockExpr(BlockExpr *) { return true; }
75 bool TraverseStmtExpr(StmtExpr *) { return true; }
76
77 // Skip over the implicit call to await_resume(). This requires cleanup
78 // scopes for await full-expressions which don't exist yet.
79 bool TraverseCoawaitExpr(CoawaitExpr *e) {
81 return TraverseStmt(e->getOperand());
82 }
83 bool TraverseDependentCoawaitExpr(DependentCoawaitExpr *e) {
85 return TraverseStmt(e->getOperand());
86 }
87 bool TraverseCoyieldExpr(CoyieldExpr *e) {
89 return TraverseStmt(e->getOperand());
90 }
91};
92} // namespace
93
94//===----------------------------------------------------------------------===//
95// CIRGenFunction cleanup related
96//===----------------------------------------------------------------------===//
97
98/// Emits all the code to cause the given temporary to be cleaned up.
100 QualType tempType, Address ptr) {
102}
103
105 assert(isInConditionalBranch());
106 mlir::Location loc = builder.getUnknownLoc();
107
108 // Place the alloca in the function entry block so it dominates everything,
109 // including both regions of any enclosing cir.cleanup.scope. We can't rely
110 // on the default curLexScope path because we may be inside a ternary branch
111 // whose LexicalScope would capture the alloca.
113 builder.getBoolTy(), CharUnits::One(), loc, "cleanup.cond",
114 /*arraySize=*/nullptr,
115 builder.getBestAllocaInsertPoint(getCurFunctionEntryBlock()));
116
117 // Initialize to false before the outermost conditional.
118 {
119 mlir::OpBuilder::InsertionGuard guard(builder);
120 builder.restoreInsertionPoint(outermostConditional->getInsertPoint());
121 cir::StoreOp store =
122 builder.createFlagStore(loc, false, active.getPointer());
123 outermostConditional->advanceInsertPoint(store);
124 }
125
126 // Set to true at the current location (inside the conditional branch).
127 builder.createFlagStore(loc, true, active.getPointer());
128
129 return active;
130}
131
135
137 EHCleanupScope &cleanup = cast<EHCleanupScope>(*ehStack.begin());
138 assert(!cleanup.hasActiveFlag() && "cleanup already has active flag?");
139 cleanup.setActiveFlag(activeFlag);
140
141 cleanup.setTestFlagInNormalCleanup(cleanup.isNormalCleanup());
142 cleanup.setTestFlagInEHCleanup(cleanup.isEHCleanup());
143}
144
146 const Expr *subExpr)
147 : cgf(cgf), cleanups(cgf), scope(nullptr),
148 oldFullExprCleanupScope(cgf.currentFullExprCleanupScope),
149 deferredCleanupStackSize(cgf.deferredConditionalCleanupStack.size()),
150 conditionalScopeDepth(cgf.conditionalCleanupScopes.size()) {
151
152 assert(subExpr && "ExprWithCleanups always has a sub-expression");
153 ConditionalEvaluationFinder finder;
154 finder.TraverseStmt(const_cast<Expr *>(subExpr));
155 if (finder.found()) {
156 mlir::Location loc = cgf.builder.getUnknownLoc();
157 cir::CleanupKind cleanupKind = cgf.getLangOpts().Exceptions
158 ? cir::CleanupKind::All
159 : cir::CleanupKind::Normal;
160 scope = cir::CleanupScopeOp::create(
161 cgf.builder, loc, cleanupKind,
162 /*bodyBuilder=*/
163 [&](mlir::OpBuilder &b, mlir::Location loc) {},
164 /*cleanupBuilder=*/
165 [&](mlir::OpBuilder &b, mlir::Location loc) {});
166 cgf.builder.setInsertionPointToEnd(&scope.getBodyRegion().front());
167 }
168
169 // Conditional cleanup scopes are only opened while a full-expression scope
170 // is active to close them.
171 cgf.currentFullExprCleanupScope = scope;
172}
173
174/// If the alloca that backs \p addr is currently nested inside the body
175/// region of \p scope, hoist it, and any cast chain leading to it, out of the
176// scope so the alloca dominates the scope's sibling cleanup region.
178 cir::CleanupScopeOp scope) {
179 cir::AllocaOp alloca = addr.getUnderlyingAllocaOp();
180 if (!alloca)
181 return;
182
183 // If the alloca is not contained within the cleanup scope we're currently
184 // proccessing we don't need to hoist it.
185 auto cur = alloca->getParentOfType<cir::CleanupScopeOp>();
186 while (cur && cur != scope)
187 cur = cur->getParentOfType<cir::CleanupScopeOp>();
188 if (cur != scope)
189 return;
190
191 // Place the alloca at the canonical alloca insertion point of the block
192 // containing the cleanup scope op, so it groups with any preceding
193 // allocas / labels and dominates both the body and cleanup regions.
194 mlir::Block *parentBlock = scope->getBlock();
195 mlir::OpBuilder::InsertPoint ip =
197 alloca->moveBefore(parentBlock, ip.getPoint());
198
199 // Move any cast chain that consumes the alloca's result to immediately after
200 // the alloca, so the address used by the deferred cleanup also dominates the
201 // cleanup region. We walk down the chain starting from the alloca's user
202 // that the Address was built from. This is very conservative. In practice,
203 // we should only ever see alloca or address_space(alloca) operations here.
204 mlir::Value ptr = addr.getPointer();
206 for (mlir::Operation *cur = ptr.getDefiningOp(); cur && cur != alloca;) {
207 auto cast = mlir::dyn_cast<cir::CastOp>(cur);
208 if (!cast)
209 break;
210 casts.push_back(cast);
211 cur = cast.getSrc().getDefiningOp();
212 }
213 // Move casts in source order (closest to the alloca first).
214 mlir::Operation *prev = alloca;
215 for (cir::CastOp cast : llvm::reverse(casts)) {
216 cast->moveAfter(prev);
217 prev = cast;
218 }
219}
220
221/// Close a cleanup scope opened by a ConditionalEvaluation. Terminate its body
222/// region and emit this scope's slice of deferredConditionalCleanupStack into
223/// its cleanup region, in reverse of push order. An insertion point that was
224/// inside the scope's body is moved to immediately after the scope op.
226 CIRGenFunction &cgf,
228 CIRGenBuilderTy &builder = cgf.getBuilder();
229 cir::CleanupScopeOp scope = condScope.scope;
230 size_t base = condScope.deferredCleanupStackSize;
231 auto &stack = cgf.deferredConditionalCleanupStack;
232 bool hasDeferredCleanups = stack.size() > base;
233
234 // Make sure the body region has a terminator.
235 {
236 mlir::OpBuilder::InsertionGuard guard(builder);
237 mlir::Block &lastBodyBlock = scope.getBodyRegion().back();
238 builder.setInsertionPointToEnd(&lastBodyBlock);
239 if (lastBodyBlock.empty() ||
240 !lastBodyBlock.back().hasTrait<mlir::OpTrait::IsTerminator>())
241 builder.createYield(scope.getLoc());
242 }
243
244 // Each deferred cleanup references its addr from the sibling cleanup region
245 // we are about to fill. If the alloca that backs that addr was created
246 // inside this scope's body region, hoist it out so it dominates the cleanup
247 // region.
248 if (hasDeferredCleanups) {
249 for (const CIRGenFunction::PendingCleanupEntry &entry :
250 llvm::make_range(stack.begin() + base, stack.end()))
251 hoistAllocaOutOfCleanupScope(cgf, entry.addr, scope);
252 }
253
254 {
255 mlir::OpBuilder::InsertionGuard guard(builder);
256 mlir::Block &cleanupBlock = scope.getCleanupRegion().front();
257 builder.setInsertionPointToEnd(&cleanupBlock);
258
259 for (const CIRGenFunction::PendingCleanupEntry &entry :
260 llvm::reverse(llvm::make_range(stack.begin() + base, stack.end()))) {
261 if (entry.activeFlag.isValid()) {
262 // We may have hoisted this alloca out of the cleanup scope. If so, we
263 // will have also hoisted any casts between it and the address that we
264 // stored in the deferredConditionalCleanupStack. While I can't find a
265 // case where this actually happens, there is a theoretical
266 // possibility that we could have a second address that uses an alloca
267 // that has already been hoisted but a different cast chain. This
268 // assert guards against that possibility.
269 assert(entry.addr.getUnderlyingAllocaOp() &&
270 (entry.addr.getUnderlyingAllocaOp()->getBlock() ==
271 entry.addr.getPointer().getDefiningOp()->getBlock()) &&
272 "alloca and cast are in different blocks");
273 mlir::Value flag = builder.createLoad(scope.getLoc(), entry.activeFlag);
274 cir::IfOp::create(builder, scope.getLoc(), flag,
275 /*withElseRegion=*/false,
276 [&](mlir::OpBuilder &b, mlir::Location loc) {
277 cgf.emitDestroy(entry.addr, entry.type,
278 entry.destroyer);
279 builder.createYield(loc);
280 });
281 } else {
282 cgf.emitDestroy(entry.addr, entry.type, entry.destroyer);
283 }
284 }
285 builder.createYield(scope.getLoc());
286 }
287
288 stack.truncate(base);
289
290 // Move the insertion point out of the scope just closed, but only if it was
291 // inside it. Moving it unconditionally can strand it outside the region it
292 // belonged to, leaving an enclosing scope unterminated. popCleanup guards
293 // this the same way.
294 mlir::Block *insertBlock = builder.getInsertionBlock();
295 if (insertBlock &&
296 scope.getBodyRegion().findAncestorBlockInRegion(*insertBlock))
297 builder.setInsertionPointAfter(scope);
298}
299
301 ArrayRef<mlir::Value *> valuesToReload) {
302 assert(!exited && "FullExprCleanupScope::exit called twice");
303 exited = true;
304
305 cgf.currentFullExprCleanupScope = oldFullExprCleanupScope;
306
307 size_t oldSize = deferredCleanupStackSize;
308
309 if (!scope) {
310 assert(cgf.conditionalCleanupScopes.size() == conditionalScopeDepth &&
311 "conditional cleanup scope opened without one to close it");
312 cgf.deferredConditionalCleanupStack.truncate(oldSize);
313 cleanups.forceCleanup(valuesToReload);
314 return;
315 }
316
317 // Spill any values that callers need after the scope is closed.
318 SmallVector<Address> tempAllocas;
319 for (mlir::Value *valPtr : valuesToReload) {
320 mlir::Value val = *valPtr;
321 if (!val) {
322 tempAllocas.push_back(Address::invalid());
323 continue;
324 }
325 Address temp = cgf.createDefaultAlignTempAlloca(val.getType(), val.getLoc(),
326 "tmp.exprcleanup");
327 tempAllocas.push_back(temp);
328 cgf.builder.createStore(val.getLoc(), val, temp);
329 }
330
331 // Close the cleanup scopes opened by conditionals in this full expression,
332 // innermost first. The EH cleanups popped below own the scopes enclosing
333 // these, so they are closed after.
334 while (cgf.conditionalCleanupScopes.size() > conditionalScopeDepth)
336 cgf.conditionalCleanupScopes.pop_back_val());
337
338 // Pop any EH cleanups that were pushed during the expression but leave
339 // any lifetime-extended cleanups so that they can be promoted to the EH
340 // stack after we've finished emitting any deferred cleanups.
341 cleanups.forceCleanupExceptLifetimeExtended();
342
343 // Make sure the cleanup scope body region has a terminator.
344 {
345 mlir::OpBuilder::InsertionGuard guard(cgf.builder);
346 mlir::Block &lastBodyBlock = scope.getBodyRegion().back();
347 cgf.builder.setInsertionPointToEnd(&lastBodyBlock);
348 if (lastBodyBlock.empty() ||
349 !lastBodyBlock.back().hasTrait<mlir::OpTrait::IsTerminator>())
350 cgf.builder.createYield(scope.getLoc());
351 }
352
353 // Deferred cleanups are emitted into the conditional scopes closed above,
354 // so this scope's cleanup region is empty and canonicalization will inline
355 // the scope away.
356 {
357 mlir::OpBuilder::InsertionGuard guard(cgf.builder);
358 mlir::Block &cleanupBlock = scope.getCleanupRegion().front();
359 cgf.builder.setInsertionPointToEnd(&cleanupBlock);
360 cgf.builder.createYield(scope.getLoc());
361 }
362
363 assert(cgf.deferredConditionalCleanupStack.size() == oldSize &&
364 "deferred cleanups were not consumed by a conditional scope");
365 cgf.builder.setInsertionPointAfter(scope);
366
367 // Promote any lifetime-extended cleanups onto the EH scope stack. The new
368 // cir.cleanup.scope ops created here will wrap any code in the enclosing
369 // scope, including reloads of any spilled values below, so the
370 // lifetime-extended destructors run at the correct point.
371 cleanups.forceLifetimeExtendedCleanups();
372
373 // Reload spilled values now that the builder is after the closed scope.
374 for (auto [addr, valPtr] : llvm::zip(tempAllocas, valuesToReload)) {
375 if (!addr.isValid())
376 continue;
377 *valPtr = cgf.builder.createLoad(valPtr->getLoc(), addr);
378 }
379}
380
381//===----------------------------------------------------------------------===//
382// EHScopeStack
383//===----------------------------------------------------------------------===//
384
385void EHScopeStack::Cleanup::anchor() {}
386
389 stable_iterator si = getInnermostNormalCleanup();
390 stable_iterator se = stable_end();
391 while (si != se) {
392 EHCleanupScope &cleanup = llvm::cast<EHCleanupScope>(*find(si));
393 if (cleanup.isActive())
394 return si;
395 si = cleanup.getEnclosingNormalCleanup();
396 }
397 return stable_end();
398}
399
400/// Push an entry of the given size onto this protected-scope stack.
401char *EHScopeStack::allocate(size_t size) {
402 size = llvm::alignTo(size, ScopeStackAlignment);
403 if (!startOfBuffer) {
404 unsigned capacity = llvm::PowerOf2Ceil(std::max<size_t>(size, 1024ul));
405 startOfBuffer = std::make_unique<char[]>(capacity);
406 startOfData = endOfBuffer = startOfBuffer.get() + capacity;
407 } else if (static_cast<size_t>(startOfData - startOfBuffer.get()) < size) {
408 unsigned currentCapacity = endOfBuffer - startOfBuffer.get();
409 unsigned usedCapacity =
410 currentCapacity - (startOfData - startOfBuffer.get());
411 unsigned requiredCapacity = usedCapacity + size;
412 // We know from the 'else if' condition that requiredCapacity is greater
413 // than currentCapacity.
414 unsigned newCapacity = llvm::PowerOf2Ceil(requiredCapacity);
415
416 std::unique_ptr<char[]> newStartOfBuffer =
417 std::make_unique<char[]>(newCapacity);
418 char *newEndOfBuffer = newStartOfBuffer.get() + newCapacity;
419 char *newStartOfData = newEndOfBuffer - usedCapacity;
420 memcpy(newStartOfData, startOfData, usedCapacity);
421 startOfBuffer.swap(newStartOfBuffer);
422 endOfBuffer = newEndOfBuffer;
423 startOfData = newStartOfData;
424 }
425
426 assert(startOfBuffer.get() + size <= startOfData);
427 startOfData -= size;
428 return startOfData;
429}
430
431void EHScopeStack::deallocate(size_t size) {
432 startOfData += llvm::alignTo(size, ScopeStackAlignment);
433}
434
435void *EHScopeStack::pushCleanup(CleanupKind kind, size_t size) {
436 char *buffer = allocate(EHCleanupScope::getSizeForCleanupSize(size));
437 bool isNormalCleanup = kind & NormalCleanup;
438 bool isEHCleanup = kind & EHCleanup;
439 bool isLifetimeMarker = kind & LifetimeMarker;
440 bool skipCleanupScope = false;
441
442 cir::CleanupKind cleanupKind = cir::CleanupKind::All;
443 if (isEHCleanup && cgf->getLangOpts().Exceptions) {
444 cleanupKind =
445 isNormalCleanup ? cir::CleanupKind::All : cir::CleanupKind::EH;
446 } else {
447 // Exceptions are disabled (or no EH flag was requested). Drop the EH
448 // flag so the scope entry stays consistent with the op's cleanup kind.
449 isEHCleanup = false;
450 if (isNormalCleanup)
451 cleanupKind = cir::CleanupKind::Normal;
452 else
453 skipCleanupScope = true;
454 }
455
456 // While emitting a variable with captured cleanups, suppress
457 // cir.cleanup.scope creation. The variable's cleanups are captured on the EH
458 // stack and later emitted into the cleanup region of the op that owns them.
459 if (capturingCleanups)
460 skipCleanupScope = true;
461
462 cir::CleanupScopeOp cleanupScope = nullptr;
463 if (!skipCleanupScope) {
464 CIRGenBuilderTy &builder = cgf->getBuilder();
465 mlir::Location loc = builder.getUnknownLoc();
466 cleanupScope = cir::CleanupScopeOp::create(
467 builder, loc, cleanupKind,
468 /*bodyBuilder=*/
469 [&](mlir::OpBuilder &b, mlir::Location loc) {
470 // Terminations will be handled in popCleanup
471 },
472 /*cleanupBuilder=*/
473 [&](mlir::OpBuilder &b, mlir::Location loc) {
474 // Terminations will be handled after emiting cleanup
475 });
476
477 builder.setInsertionPointToEnd(&cleanupScope.getBodyRegion().back());
478 }
479
480 // Per C++ [except.terminate], it is implementation-defined whether none,
481 // some, or all cleanups are called before std::terminate. Thus, when
482 // terminate is the current EH scope, we may skip adding any EH cleanup
483 // scopes.
484 if (innermostEHScope != stable_end() &&
485 find(innermostEHScope)->getKind() == EHScope::Terminate)
486 isEHCleanup = false;
487
488 EHCleanupScope *scope = new (buffer)
489 EHCleanupScope(isNormalCleanup, isEHCleanup, size, cleanupScope,
490 innermostNormalCleanup, innermostEHScope);
491
492 if (isNormalCleanup)
493 innermostNormalCleanup = stable_begin();
494
495 if (isEHCleanup)
496 innermostEHScope = stable_begin();
497
498 if (isLifetimeMarker)
499 scope->setLifetimeMarker();
500
501 // With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup
502 if (cgf->getLangOpts().EHAsynch && isEHCleanup && !isLifetimeMarker &&
503 cgf->getTarget().getCXXABI().isMicrosoft())
504 cgf->cgm.errorNYI("push seh cleanup");
505
506 return scope->getCleanupBuffer();
507}
508
510 assert(!empty() && "popping exception stack when not empty");
511
512 assert(isa<EHCleanupScope>(*begin()));
513 EHCleanupScope &cleanup = cast<EHCleanupScope>(*begin());
514 innermostNormalCleanup = cleanup.getEnclosingNormalCleanup();
515 innermostEHScope = cleanup.getEnclosingEHScope();
516 deallocate(cleanup.getAllocatedSize());
517
518 cir::CleanupScopeOp cleanupScope = cleanup.getCleanupScopeOp();
519 if (cleanupScope) {
520 auto *block = &cleanupScope.getBodyRegion().back();
521 if (!block->mightHaveTerminator()) {
522 mlir::OpBuilder::InsertionGuard guard(cgf->getBuilder());
523 cgf->getBuilder().setInsertionPointToEnd(block);
524 cir::YieldOp::create(cgf->getBuilder(),
525 cgf->getBuilder().getUnknownLoc());
526 }
527 // If the insertion point was inside the cleanup scope we just closed, move
528 // it to immediate after the scope.
529 mlir::Block *insertBlock = cgf->getBuilder().getInsertionBlock();
530 if (insertBlock &&
531 cleanupScope.getBodyRegion().findAncestorBlockInRegion(*insertBlock))
532 cgf->getBuilder().setInsertionPointAfter(cleanupScope);
533 }
534
535 // Destroy the cleanup.
536 cleanup.destroy();
537}
538
540 for (stable_iterator si = getInnermostEHScope(); si != stable_end();) {
541 if (auto *cleanup = dyn_cast<EHCleanupScope>(&*find(si))) {
542 if (cleanup->isLifetimeMarker()) {
543 // Skip lifetime markers and continue from the enclosing EH scope
544 si = cleanup->getEnclosingEHScope();
545 continue;
546 }
547 }
548 return true;
549 }
550 return false;
551}
552
553/// The given cleanup block is being deactivated. Configure a cleanup variable
554/// if necessary.
557 mlir::Operation *dominatingIP) {
559
560 // An EH-only cleanup has both flags clear when pushCleanup dropped
561 // the EH flag. That happens when exceptions are disabled, and when
562 // the innermost EH scope is a terminate scope. The cleanup never
563 // runs, so there is no active flag to clear.
564 if (!scope.isNormalCleanup() && !scope.isEHCleanup())
565 return;
566
568 scope.setTestFlagInEHCleanup(scope.isEHCleanup());
569
570 CIRGenBuilderTy &builder = cgf.getBuilder();
571
572 // If the cleanup block doesn't exist yet, create it and set its initial
573 // value to `true`. If we are inside a conditional branch, the value must be
574 // initialized before the conditional branch begins.
575 Address var = scope.getActiveFlag();
576 if (!var.isValid()) {
577 mlir::Location loc = builder.getUnknownLoc();
578
580 loc, "cleanup.isactive");
581 scope.setActiveFlag(var);
582
583 assert(dominatingIP && "no existing variable and no dominating IP!");
584
585 if (cgf.isInConditionalBranch()) {
586 mlir::Value val = builder.getBool(true, loc);
587 cgf.setBeforeOutermostConditional(val, var);
588 } else {
589 mlir::OpBuilder::InsertionGuard guard(builder);
590 builder.setInsertionPoint(dominatingIP);
591 builder.createFlagStore(loc, true, var.getPointer());
592 }
593 }
594
595 // The code above sets the `isActive` flag to `true` as its initial state
596 // at the point where the variable is created. The code below sets it to
597 // `false` at the point where the cleanup is deactivated.
598 mlir::Location loc = builder.getUnknownLoc();
599 builder.createFlagStore(loc, false, var.getPointer());
600}
601
602/// Deactive a cleanup that was created in an active state.
604 mlir::Operation *dominatingIP) {
605 assert(c != ehStack.stable_end() && "deactivating bottom of stack?");
607 assert(scope.isActive() && "double deactivation");
608
609 // If it's the top of the stack, just pop it, but do so only if it belongs
610 // to the current RunCleanupsScope.
611 if (c == ehStack.stable_begin() &&
612 currentCleanupStackDepth.strictlyEncloses(c)) {
613 popCleanupBlock(/*forDeactivation=*/true);
614 return;
615 }
616
617 // Otherwise, follow the general case.
618 setupCleanupBlockDeactivation(*this, c, dominatingIP);
619
620 scope.setActive(false);
621}
622
623static void emitCleanupBody(CIRGenFunction &cgf, EHScopeStack::Cleanup *cleanup,
625 Address activeFlag, mlir::Location loc) {
626 CIRGenBuilderTy &builder = cgf.getBuilder();
627
628 // Ask the cleanup to emit itself.
629 assert(cgf.haveInsertPoint() && "expected insertion point");
630
631 if (activeFlag.isValid()) {
632 mlir::Value isActive = builder.createFlagLoad(loc, activeFlag.getPointer());
633 cir::IfOp::create(builder, loc, isActive,
634 /*withElseRegion=*/false,
635 /*thenBuilder=*/
636 [&](mlir::OpBuilder &, mlir::Location) {
637 cleanup->emit(cgf, flags);
638 assert(cgf.haveInsertPoint() &&
639 "cleanup ended with no insertion point?");
640 builder.createYield(loc);
641 });
642 } else {
643 cleanup->emit(cgf, flags);
644 assert(cgf.haveInsertPoint() && "cleanup ended with no insertion point?");
645 }
646}
647
648static void emitCleanup(CIRGenFunction &cgf, cir::CleanupScopeOp cleanupScope,
649 EHScopeStack::Cleanup *cleanup,
651 Address activeFlag) {
652 CIRGenBuilderTy &builder = cgf.getBuilder();
653 mlir::Block &block = cleanupScope.getCleanupRegion().back();
654
655 mlir::OpBuilder::InsertionGuard guard(builder);
656 builder.setInsertionPointToStart(&block);
657
658 emitCleanupBody(cgf, cleanup, flags, activeFlag, cleanupScope.getLoc());
659
660 mlir::Block &cleanupRegionLastBlock = cleanupScope.getCleanupRegion().back();
661 if (cleanupRegionLastBlock.empty() ||
662 !cleanupRegionLastBlock.back().hasTrait<mlir::OpTrait::IsTerminator>()) {
663 mlir::OpBuilder::InsertionGuard guardCase(builder);
664 builder.setInsertionPointToEnd(&cleanupRegionLastBlock);
665 builder.createYield(cleanupScope.getLoc());
666 }
667}
668
669/// Check whether a cleanup scope body contains any non-yield exits that branch
670/// through the cleanup. These exits branch through the cleanup and require
671/// the normal cleanup to be executed even when the cleanup has been
672/// deactivated.
673static bool bodyHasBranchThroughExits(mlir::Region &bodyRegion) {
674 return bodyRegion
675 .walk([&](mlir::Operation *op) {
677 return mlir::WalkResult::interrupt();
678 return mlir::WalkResult::advance();
679 })
680 .wasInterrupted();
681}
682
683/// Pop a cleanup block from the stack.
684///
685/// \param forDeactivation - When true, this indicates that the cleanup block
686/// is being popped because it was deactivated while at the top of the stack.
687void CIRGenFunction::popCleanupBlock(bool forDeactivation) {
688 assert(!ehStack.empty() && "cleanup stack is empty!");
689 assert(isa<EHCleanupScope>(*ehStack.begin()) && "top not a cleanup!");
691
692 // If we pushed an EH-only cleanup but exceptions are disabled, it will leave
693 // an effectively empty cleanup on the EH stack. In that case, there is
694 // nothing to do here except pop the cleanup.
695 cir::CleanupScopeOp cleanupScope = scope.getCleanupScopeOp();
696 if (!cleanupScope) {
697 assert(!scope.isNormalCleanup() && !scope.isEHCleanup() &&
698 "missing cir.cleanup.scope for active cleanup");
699 ehStack.popCleanup();
700 return;
701 }
702
703 bool requiresNormalCleanup = scope.isNormalCleanup();
704 bool requiresEHCleanup = scope.isEHCleanup();
705
706 // When we're popping a cleanup to deactivate it, we need to know if anything
707 // in the cleanup scope body region branches through the cleanup handler
708 // before the entire cleanup scope body has executed. If the cleanup scope
709 // body falls through, we don't want to emit normal cleanup code. However,
710 // if the cleanup body region contains early exits (return or goto), we do
711 // need to execute the normal cleanup when the early exit is taken. To handle
712 // that case, we guard the cleanup with an "active" flag so that it executes
713 // conditionally and set the flag to false when the cleanup body falls
714 // through. Classic codegen tracks this state with "hasBranches" and
715 // "getFixupDepth" on the cleanup scope, but because CIR uses structured
716 // control flow, we need to check for early exits and insert the active
717 // flag handling here. Note that when a cleanup is deactivated while not at
718 // the top of the stack, the active flag gets created in
719 // setupCleanupBlockDeactivation.
720 if (forDeactivation && requiresNormalCleanup) {
721 if (bodyHasBranchThroughExits(cleanupScope.getBodyRegion())) {
722 // The active flag shouldn't exist if the scope was at the top of the
723 // stack when it was deactivated.
724 assert(!scope.getActiveFlag().isValid() && "active flag already set");
725
726 // Create the flag.
727 mlir::Location loc = builder.getUnknownLoc();
729 builder.getBoolTy(), CharUnits::One(), loc, "cleanup.isactive");
730
731 // Initialize the flag to true before the cleanup scope (the point where
732 // the cleanup becomes active).
733 {
734 mlir::OpBuilder::InsertionGuard guard(builder);
735 builder.setInsertionPoint(cleanupScope);
736 builder.createFlagStore(loc, true, activeFlag.getPointer());
737 }
738
739 // Set the flag to false at the end of the cleanup scope body region.
740 assert(builder.getInsertionBlock() ==
741 &cleanupScope.getBodyRegion().back() &&
742 "expected insertion point in cleanup body");
743 builder.createFlagStore(loc, false, activeFlag.getPointer());
744
745 scope.setActiveFlag(activeFlag);
746 scope.setTestFlagInNormalCleanup(true);
747 } else {
748 // If the cleanup was pushed on the stack as normal+eh, downgrade it to
749 // eh-only.
750 if (requiresEHCleanup)
751 cleanupScope.setCleanupKind(cir::CleanupKind::EH);
752 requiresNormalCleanup = false;
753 }
754 }
755
756 Address normalActiveFlag = scope.shouldTestFlagInNormalCleanup()
757 ? scope.getActiveFlag()
759 Address ehActiveFlag = scope.shouldTestFlagInEHCleanup()
760 ? scope.getActiveFlag()
762
763 // If we don't need the cleanup at all, we're done.
764 if (!requiresNormalCleanup && !requiresEHCleanup) {
765 // If we get here, the cleanup scope isn't needed. Rather than try to move
766 // the contents of its body region out of the cleanup and erase it, we just
767 // add a yield to the cleanup region to make it valid but no-op. It will be
768 // erased during canonicalization.
769 mlir::Block &cleanupBlock = cleanupScope.getCleanupRegion().back();
770 if (!cleanupBlock.mightHaveTerminator()) {
771 mlir::OpBuilder::InsertionGuard guard(builder);
772 builder.setInsertionPointToEnd(&cleanupBlock);
773 cir::YieldOp::create(builder, builder.getUnknownLoc());
774 }
775 ehStack.popCleanup();
776 return;
777 }
778
779 // Copy the cleanup emission data out. This uses either a stack
780 // array or malloc'd memory, depending on the size, which is
781 // behavior that SmallVector would provide, if we could use it
782 // here. Unfortunately, if you ask for a SmallVector<char>, the
783 // alignment isn't sufficient.
784 auto *cleanupSource = reinterpret_cast<char *>(scope.getCleanupBuffer());
786 cleanupBufferStack[8 * sizeof(void *)];
787 std::unique_ptr<char[]> cleanupBufferHeap;
788 size_t cleanupSize = scope.getCleanupSize();
789 EHScopeStack::Cleanup *cleanup;
790
791 // This is necessary because we are going to deallocate the cleanup
792 // (in popCleanup) before we emit it.
793 if (cleanupSize <= sizeof(cleanupBufferStack)) {
794 memcpy(cleanupBufferStack, cleanupSource, cleanupSize);
795 cleanup = reinterpret_cast<EHScopeStack::Cleanup *>(cleanupBufferStack);
796 } else {
797 cleanupBufferHeap.reset(new char[cleanupSize]);
798 memcpy(cleanupBufferHeap.get(), cleanupSource, cleanupSize);
799 cleanup =
800 reinterpret_cast<EHScopeStack::Cleanup *>(cleanupBufferHeap.get());
801 }
802
803 EHScopeStack::Cleanup::Flags cleanupFlags;
804 if (scope.isNormalCleanup())
805 cleanupFlags.setIsNormalCleanupKind();
806 if (scope.isEHCleanup())
807 cleanupFlags.setIsEHCleanupKind();
808
809 // Determine the active flag for the cleanup handler.
810 Address cleanupActiveFlag = normalActiveFlag.isValid() ? normalActiveFlag
811 : ehActiveFlag.isValid() ? ehActiveFlag
813
814 // In CIR, the cleanup code is emitted into the cleanup region of the
815 // cir.cleanup.scope op. There is no CFG threading needed — the FlattenCFG
816 // pass handles lowering the structured cleanup scope.
817 ehStack.popCleanup();
818 scope.markEmitted();
819 emitCleanup(*this, cleanupScope, cleanup, cleanupFlags, cleanupActiveFlag);
820}
821
823 mlir::Location loc) {
824 // The captured cleanups were pushed while EHScopeStack was capturing
825 // cleanups, so they own no cir.cleanup.scope. Emit them directly into the
826 // owning op's cleanup region (the current insertion point), popping each off
827 // the EH stack.
828 while (ehStack.stable_begin() != depth) {
829 assert(isa<EHCleanupScope>(*ehStack.begin()) && "top not a cleanup!");
831 assert(!scope.getCleanupScopeOp() &&
832 "captured cleanup should not own a cleanup scope");
833
834 EHScopeStack::Cleanup::Flags cleanupFlags;
835 if (scope.isNormalCleanup())
836 cleanupFlags.setIsNormalCleanupKind();
837 if (scope.isEHCleanup())
838 cleanupFlags.setIsEHCleanupKind();
839
840 // A captured cleanup may be guarded by an active flag. The destructor
841 // cleanups of a loop condition variable and of the temporaries that its
842 // initializer lifetime-extends have one that is false while its
843 // initializer runs, so a throwing initializer does not destroy the
844 // not-yet-constructed variables. The lifetime-end cleanup has no flag
845 // because its lifetime starts before initialization. For a loop, each
846 // emission serves both the normal per-iteration exit and the EH unwind
847 // path.
848 Address activeFlag = scope.getActiveFlag();
849
850 // Copy the cleanup emission data out before popping, since popCleanup
851 // deallocates the entry. This mirrors popCleanupBlock.
852 auto *cleanupSource = reinterpret_cast<char *>(scope.getCleanupBuffer());
854 cleanupBufferStack[8 * sizeof(void *)];
855 std::unique_ptr<char[]> cleanupBufferHeap;
856 size_t cleanupSize = scope.getCleanupSize();
857 EHScopeStack::Cleanup *cleanup;
858 if (cleanupSize <= sizeof(cleanupBufferStack)) {
859 memcpy(cleanupBufferStack, cleanupSource, cleanupSize);
860 cleanup = reinterpret_cast<EHScopeStack::Cleanup *>(cleanupBufferStack);
861 } else {
862 cleanupBufferHeap.reset(new char[cleanupSize]);
863 memcpy(cleanupBufferHeap.get(), cleanupSource, cleanupSize);
864 cleanup =
865 reinterpret_cast<EHScopeStack::Cleanup *>(cleanupBufferHeap.get());
866 }
867
868 ehStack.popCleanup();
869 emitCleanupBody(*this, cleanup, cleanupFlags, activeFlag, loc);
870 }
871}
872
873/// Pops cleanup blocks until the given savepoint is reached.
875 EHScopeStack::stable_iterator oldCleanupStackDepth,
876 ArrayRef<mlir::Value *> valuesToReload) {
877 // If the current stack depth is the same as the cleanup stack depth,
878 // we won't be exiting any cleanup scopes, so we don't need to reload
879 // any values.
880 bool requiresCleanup = false;
881 for (auto it = ehStack.begin(), ie = ehStack.find(oldCleanupStackDepth);
882 it != ie; ++it) {
883 if (isa<EHCleanupScope>(&*it)) {
884 requiresCleanup = true;
885 break;
886 }
887 }
888
889 // If there are values that we need to keep live, spill them now before
890 // we pop the cleanup blocks. These are passed as pointers to mlir::Value
891 // because we're going to replace them with the reloaded value.
892 SmallVector<Address> tempAllocas;
893 if (requiresCleanup) {
894 for (mlir::Value *valPtr : valuesToReload) {
895 mlir::Value val = *valPtr;
896 if (!val)
897 continue;
898
899 // TODO(cir): Check for static allocas.
900
901 Address temp = createDefaultAlignTempAlloca(val.getType(), val.getLoc(),
902 "tmp.exprcleanup");
903 tempAllocas.push_back(temp);
904 builder.createStore(val.getLoc(), val, temp);
905 }
906 }
907
908 // Pop cleanup blocks until we reach the base stack depth for the
909 // current scope.
910 while (ehStack.stable_begin() != oldCleanupStackDepth)
912
913 // Reload the values that we spilled, if necessary.
914 if (requiresCleanup) {
915 for (auto [addr, valPtr] : llvm::zip(tempAllocas, valuesToReload)) {
916 mlir::Location loc = valPtr->getLoc();
917 *valPtr = builder.createLoad(loc, addr);
918 }
919 }
920}
921
922/// Pops cleanup blocks until the given savepoint is reached, then add the
923/// cleanups from the given savepoint in the lifetime-extended cleanups stack.
925 EHScopeStack::stable_iterator oldCleanupStackDepth,
926 size_t oldLifetimeExtendedSize, ArrayRef<mlir::Value *> valuesToReload) {
927 popCleanupBlocks(oldCleanupStackDepth, valuesToReload);
928
929 // Promote deferred lifetime-extended cleanups onto the EH scope stack.
930 for (const PendingCleanupEntry &cleanup : llvm::make_range(
931 lifetimeExtendedCleanupStack.begin() + oldLifetimeExtendedSize,
934 lifetimeExtendedCleanupStack.truncate(oldLifetimeExtendedSize);
935}
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 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()
static bool isLogicalOp(Opcode Opc)
Definition Expr.h:4219
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 Expr *subExpr)
void exit(ArrayRef< mlir::Value * > valuesToReload={})
llvm::SmallVector< PendingCleanupEntry > lifetimeExtendedCleanupStack
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
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
Expr * getOperand() const
Definition ExprCXX.h:5378
Expr * getOperand() const
Definition ExprCXX.h:5478
This represents one expression.
Definition Expr.h:113
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
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
@ 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:4534
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
static bool coroAwaitFullExprCleanups()
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.