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