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