Skip to content

Commit 89ba7e1

Browse files
authored
[codegen] Emit missing cleanups for stmt-expr and coro suspensions [take-2] (#85398)
Fixes #63818 for control flow out of an expressions. #### Background A control flow could happen in the middle of an expression due to stmt-expr and coroutine suspensions. Due to branch-in-expr, we missed running cleanups for the temporaries constructed in the expression before the branch. Previously, these cleanups were only added as `EHCleanup` during the expression and as normal expression after the full expression. Examples of such deferred cleanups include: `ParenList/InitList`: Cleanups for fields are performed by the destructor of the object being constructed. `Array init`: Cleanup for elements of an array is included in the array cleanup. `Lifetime-extended temporaries`: reference-binding temporaries in braced-init are lifetime extended to the parent scope. `Lambda capture init`: init in the lambda capture list is destroyed by the lambda object. --- #### In this PR In this PR, we change some of the `EHCleanups` cleanups to `NormalAndEHCleanups` to make sure these are emitted when we see a branch inside an expression (through statement expressions or coroutine suspensions). These are supposed to be deactivated after full expression and destroyed later as part of the destructor of the aggregate or array being constructed. To simplify deactivating cleanups, we add two utilities as well: * `DeferredDeactivationCleanupStack`: A stack to remember cleanups with deferred deactivation. * `CleanupDeactivationScope`: RAII for deactivating cleanups added to the above stack. --- #### Deactivating normal cleanups These were previously `EHCleanups` and not `Normal` and **deactivation** of **required** `Normal` cleanups had some bugs. These specifically include deactivating `Normal` cleanups which are not the top of `EHStack` [source1](https://github.com/llvm/llvm-project/blob/92b56011e6b61e7dc1628c0431ece432f282b3cb/clang/lib/CodeGen/CGCleanup.cpp#L1319), [2](https://github.com/llvm/llvm-project/blob/92b56011e6b61e7dc1628c0431ece432f282b3cb/clang/lib/CodeGen/CGCleanup.cpp#L722-L746). This has not been part of our test suite (maybe it was never required before statement expressions). In this PR, we also fix the emission of required-deactivated-normal cleanups.
1 parent 1709eac commit 89ba7e1

File tree

10 files changed

+716
-107
lines changed

10 files changed

+716
-107
lines changed

clang/lib/CodeGen/CGCleanup.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,8 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
667667

668668
// - whether there's a fallthrough
669669
llvm::BasicBlock *FallthroughSource = Builder.GetInsertBlock();
670-
bool HasFallthrough = (FallthroughSource != nullptr && IsActive);
670+
bool HasFallthrough =
671+
FallthroughSource != nullptr && (IsActive || HasExistingBranches);
671672

672673
// Branch-through fall-throughs leave the insertion point set to the
673674
// end of the last cleanup, which points to the current scope. The
@@ -692,7 +693,11 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
692693

693694
// If we have a prebranched fallthrough into an inactive normal
694695
// cleanup, rewrite it so that it leads to the appropriate place.
695-
if (Scope.isNormalCleanup() && HasPrebranchedFallthrough && !IsActive) {
696+
if (Scope.isNormalCleanup() && HasPrebranchedFallthrough &&
697+
!RequiresNormalCleanup) {
698+
// FIXME: Come up with a program which would need forwarding prebranched
699+
// fallthrough and add tests. Otherwise delete this and assert against it.
700+
assert(!IsActive);
696701
llvm::BasicBlock *prebranchDest;
697702

698703
// If the prebranch is semantically branching through the next
@@ -765,6 +770,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
765770
EmitSehCppScopeEnd();
766771
}
767772
destroyOptimisticNormalEntry(*this, Scope);
773+
Scope.MarkEmitted();
768774
EHStack.popCleanup();
769775
} else {
770776
// If we have a fallthrough and no other need for the cleanup,
@@ -781,6 +787,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
781787
}
782788

783789
destroyOptimisticNormalEntry(*this, Scope);
790+
Scope.MarkEmitted();
784791
EHStack.popCleanup();
785792

786793
EmitCleanup(*this, Fn, cleanupFlags, NormalActiveFlag);
@@ -916,6 +923,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
916923
}
917924

918925
// IV. Pop the cleanup and emit it.
926+
Scope.MarkEmitted();
919927
EHStack.popCleanup();
920928
assert(EHStack.hasNormalCleanups() == HasEnclosingCleanups);
921929

clang/lib/CodeGen/CGCleanup.h

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
#include "EHScopeStack.h"
1717

1818
#include "Address.h"
19+
#include "llvm/ADT/STLExtras.h"
20+
#include "llvm/ADT/SetVector.h"
1921
#include "llvm/ADT/SmallPtrSet.h"
2022
#include "llvm/ADT/SmallVector.h"
23+
#include "llvm/IR/Instruction.h"
2124

2225
namespace llvm {
2326
class BasicBlock;
@@ -266,6 +269,51 @@ class alignas(8) EHCleanupScope : public EHScope {
266269
};
267270
mutable struct ExtInfo *ExtInfo;
268271

272+
/// Erases auxillary allocas and their usages for an unused cleanup.
273+
/// Cleanups should mark these allocas as 'used' if the cleanup is
274+
/// emitted, otherwise these instructions would be erased.
275+
struct AuxillaryAllocas {
276+
SmallVector<llvm::Instruction *, 1> AuxAllocas;
277+
bool used = false;
278+
279+
// Records a potentially unused instruction to be erased later.
280+
void Add(llvm::AllocaInst *Alloca) { AuxAllocas.push_back(Alloca); }
281+
282+
// Mark all recorded instructions as used. These will not be erased later.
283+
void MarkUsed() {
284+
used = true;
285+
AuxAllocas.clear();
286+
}
287+
288+
~AuxillaryAllocas() {
289+
if (used)
290+
return;
291+
llvm::SetVector<llvm::Instruction *> Uses;
292+
for (auto *Inst : llvm::reverse(AuxAllocas))
293+
CollectUses(Inst, Uses);
294+
// Delete uses in the reverse order of insertion.
295+
for (auto *I : llvm::reverse(Uses))
296+
I->eraseFromParent();
297+
}
298+
299+
private:
300+
void CollectUses(llvm::Instruction *I,
301+
llvm::SetVector<llvm::Instruction *> &Uses) {
302+
if (!I || !Uses.insert(I))
303+
return;
304+
for (auto *User : I->users())
305+
CollectUses(cast<llvm::Instruction>(User), Uses);
306+
}
307+
};
308+
mutable struct AuxillaryAllocas *AuxAllocas;
309+
310+
AuxillaryAllocas &getAuxillaryAllocas() {
311+
if (!AuxAllocas) {
312+
AuxAllocas = new struct AuxillaryAllocas();
313+
}
314+
return *AuxAllocas;
315+
}
316+
269317
/// The number of fixups required by enclosing scopes (not including
270318
/// this one). If this is the top cleanup scope, all the fixups
271319
/// from this index onwards belong to this scope.
@@ -298,7 +346,7 @@ class alignas(8) EHCleanupScope : public EHScope {
298346
EHScopeStack::stable_iterator enclosingEH)
299347
: EHScope(EHScope::Cleanup, enclosingEH),
300348
EnclosingNormal(enclosingNormal), NormalBlock(nullptr),
301-
ActiveFlag(Address::invalid()), ExtInfo(nullptr),
349+
ActiveFlag(Address::invalid()), ExtInfo(nullptr), AuxAllocas(nullptr),
302350
FixupDepth(fixupDepth) {
303351
CleanupBits.IsNormalCleanup = isNormal;
304352
CleanupBits.IsEHCleanup = isEH;
@@ -312,8 +360,15 @@ class alignas(8) EHCleanupScope : public EHScope {
312360
}
313361

314362
void Destroy() {
363+
if (AuxAllocas)
364+
delete AuxAllocas;
315365
delete ExtInfo;
316366
}
367+
void AddAuxAllocas(llvm::SmallVector<llvm::AllocaInst *> Allocas) {
368+
for (auto *Alloca : Allocas)
369+
getAuxillaryAllocas().Add(Alloca);
370+
}
371+
void MarkEmitted() { getAuxillaryAllocas().MarkUsed(); }
317372
// Objects of EHCleanupScope are not destructed. Use Destroy().
318373
~EHCleanupScope() = delete;
319374

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "CodeGenFunction.h"
2020
#include "CodeGenModule.h"
2121
#include "ConstantEmitter.h"
22+
#include "EHScopeStack.h"
2223
#include "PatternInit.h"
2324
#include "TargetInfo.h"
2425
#include "clang/AST/ASTContext.h"
@@ -2201,6 +2202,24 @@ void CodeGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
22012202
destroyer, useEHCleanupForArray);
22022203
}
22032204

2205+
// Pushes a destroy and defers its deactivation until its
2206+
// CleanupDeactivationScope is exited.
2207+
void CodeGenFunction::pushDestroyAndDeferDeactivation(
2208+
QualType::DestructionKind dtorKind, Address addr, QualType type) {
2209+
assert(dtorKind && "cannot push destructor for trivial type");
2210+
2211+
CleanupKind cleanupKind = getCleanupKind(dtorKind);
2212+
pushDestroyAndDeferDeactivation(
2213+
cleanupKind, addr, type, getDestroyer(dtorKind), cleanupKind & EHCleanup);
2214+
}
2215+
2216+
void CodeGenFunction::pushDestroyAndDeferDeactivation(
2217+
CleanupKind cleanupKind, Address addr, QualType type, Destroyer *destroyer,
2218+
bool useEHCleanupForArray) {
2219+
pushCleanupAndDeferDeactivation<DestroyObject>(
2220+
cleanupKind, addr, type, destroyer, useEHCleanupForArray);
2221+
}
2222+
22042223
void CodeGenFunction::pushStackRestore(CleanupKind Kind, Address SPMem) {
22052224
EHStack.pushCleanup<CallStackRestore>(Kind, SPMem);
22062225
}
@@ -2217,16 +2236,19 @@ void CodeGenFunction::pushLifetimeExtendedDestroy(CleanupKind cleanupKind,
22172236
// If we're not in a conditional branch, we don't need to bother generating a
22182237
// conditional cleanup.
22192238
if (!isInConditionalBranch()) {
2220-
// Push an EH-only cleanup for the object now.
22212239
// FIXME: When popping normal cleanups, we need to keep this EH cleanup
22222240
// around in case a temporary's destructor throws an exception.
2223-
if (cleanupKind & EHCleanup)
2224-
EHStack.pushCleanup<DestroyObject>(
2225-
static_cast<CleanupKind>(cleanupKind & ~NormalCleanup), addr, type,
2226-
destroyer, useEHCleanupForArray);
22272241

2242+
// Add the cleanup to the EHStack. After the full-expr, this would be
2243+
// deactivated before being popped from the stack.
2244+
pushDestroyAndDeferDeactivation(cleanupKind, addr, type, destroyer,
2245+
useEHCleanupForArray);
2246+
2247+
// Since this is lifetime-extended, push it once again to the EHStack after
2248+
// the full expression.
22282249
return pushCleanupAfterFullExprWithActiveFlag<DestroyObject>(
2229-
cleanupKind, Address::invalid(), addr, type, destroyer, useEHCleanupForArray);
2250+
cleanupKind, Address::invalid(), addr, type, destroyer,
2251+
useEHCleanupForArray);
22302252
}
22312253

22322254
// Otherwise, we should only destroy the object if it's been initialized.
@@ -2241,13 +2263,12 @@ void CodeGenFunction::pushLifetimeExtendedDestroy(CleanupKind cleanupKind,
22412263
Address ActiveFlag = createCleanupActiveFlag();
22422264
SavedType SavedAddr = saveValueInCond(addr);
22432265

2244-
if (cleanupKind & EHCleanup) {
2245-
EHStack.pushCleanup<ConditionalCleanupType>(
2246-
static_cast<CleanupKind>(cleanupKind & ~NormalCleanup), SavedAddr, type,
2247-
destroyer, useEHCleanupForArray);
2248-
initFullExprCleanupWithFlag(ActiveFlag);
2249-
}
2266+
pushCleanupAndDeferDeactivation<ConditionalCleanupType>(
2267+
cleanupKind, SavedAddr, type, destroyer, useEHCleanupForArray);
2268+
initFullExprCleanupWithFlag(ActiveFlag);
22502269

2270+
// Since this is lifetime-extended, push it once again to the EHStack after
2271+
// the full expression.
22512272
pushCleanupAfterFullExprWithActiveFlag<ConditionalCleanupType>(
22522273
cleanupKind, ActiveFlag, SavedAddr, type, destroyer,
22532274
useEHCleanupForArray);
@@ -2442,9 +2463,9 @@ namespace {
24422463
};
24432464
} // end anonymous namespace
24442465

2445-
/// pushIrregularPartialArrayCleanup - Push an EH cleanup to destroy
2446-
/// already-constructed elements of the given array. The cleanup
2447-
/// may be popped with DeactivateCleanupBlock or PopCleanupBlock.
2466+
/// pushIrregularPartialArrayCleanup - Push a NormalAndEHCleanup to
2467+
/// destroy already-constructed elements of the given array. The cleanup may be
2468+
/// popped with DeactivateCleanupBlock or PopCleanupBlock.
24482469
///
24492470
/// \param elementType - the immediate element type of the array;
24502471
/// possibly still an array type
@@ -2453,10 +2474,9 @@ void CodeGenFunction::pushIrregularPartialArrayCleanup(llvm::Value *arrayBegin,
24532474
QualType elementType,
24542475
CharUnits elementAlign,
24552476
Destroyer *destroyer) {
2456-
pushFullExprCleanup<IrregularPartialArrayDestroy>(EHCleanup,
2457-
arrayBegin, arrayEndPointer,
2458-
elementType, elementAlign,
2459-
destroyer);
2477+
pushFullExprCleanup<IrregularPartialArrayDestroy>(
2478+
NormalAndEHCleanup, arrayBegin, arrayEndPointer, elementType,
2479+
elementAlign, destroyer);
24602480
}
24612481

24622482
/// pushRegularPartialArrayCleanup - Push an EH cleanup to destroy

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,16 @@ RawAddress CodeGenFunction::CreateTempAlloca(llvm::Type *Ty, CharUnits Align,
115115
llvm::AllocaInst *CodeGenFunction::CreateTempAlloca(llvm::Type *Ty,
116116
const Twine &Name,
117117
llvm::Value *ArraySize) {
118+
llvm::AllocaInst *Alloca;
118119
if (ArraySize)
119-
return Builder.CreateAlloca(Ty, ArraySize, Name);
120-
return new llvm::AllocaInst(Ty, CGM.getDataLayout().getAllocaAddrSpace(),
121-
ArraySize, Name, AllocaInsertPt);
120+
Alloca = Builder.CreateAlloca(Ty, ArraySize, Name);
121+
else
122+
Alloca = new llvm::AllocaInst(Ty, CGM.getDataLayout().getAllocaAddrSpace(),
123+
ArraySize, Name, AllocaInsertPt);
124+
if (Allocas) {
125+
Allocas->Add(Alloca);
126+
}
127+
return Alloca;
122128
}
123129

124130
/// CreateDefaultAlignTempAlloca - This creates an alloca with the

0 commit comments

Comments
 (0)