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