Skip to content

Commit d72146f

Browse files
authored
Re-apply "Emit missing cleanups for stmt-expr" and other commits (llvm#89154)
Latest diff: https://github.com/llvm/llvm-project/pull/89154/files/f1ab4c2677394bbfc985d9680d5eecd7b2e6a882..adf9bc902baddb156c83ce0f8ec03c142e806d45 We address two additional bugs here: ### Problem 1: Deactivated normal cleanup still runs, leading to double-free Consider the following: ```cpp struct A { }; struct B { B(const A&); }; struct S { A a; B b; }; int AcceptS(S s); void Accept2(int x, int y); void Test() { Accept2(AcceptS({.a = A{}, .b = A{}}), ({ return; 0; })); } ``` We add cleanups as follows: 1. push dtor for field `S::a` 2. push dtor for temp `A{}` (used by ` B(const A&)` in `.b = A{}`) 3. push dtor for field `S::b` 4. Deactivate 3 `S::b`-> This pops the cleanup. 5. Deactivate 1 `S::a` -> Does not pop the cleanup as *2* is top. Should create _active flag_!! 6. push dtor for `~S()`. 7. ... It is important to deactivate **5** using active flags. Without the active flags, the `return` will fallthrough it and would run both `~S()` and dtor `S::a` leading to **double free** of `~A()`. In this patch, we unconditionally emit active flags while deactivating normal cleanups. These flags are deleted later by the `AllocaTracker` if the cleanup is not emitted. ### Problem 2: Missing cleanup for conditional lifetime extension We push 2 cleanups for lifetime-extended cleanup. The first cleanup is useful if we exit from the middle of the expression (stmt-expr/coro suspensions). This is deactivated after full-expr, and a new cleanup is pushed, extending the lifetime of the temporaries (to the scope of the reference being initialized). If this lifetime extension happens to be conditional, then we use active flags to remember whether the branch was taken and if the object was initialized. Previously, we used a **single** active flag, which was used by both cleanups. This is wrong because the first cleanup will be forced to deactivate after the full-expr and therefore this **active** flag will always be **inactive**. The dtor for the lifetime extended entity would not run as it always sees an **inactive** flag. In this patch, we solve this using two separate active flags for both cleanups. Both of them are activated if the conditional branch is taken, but only one of them is deactivated after the full-expr. --- Fixes llvm#63818 Fixes llvm#88478 --- Previous PR logs: 1. llvm#85398 2. llvm#88670 3. llvm#88751 4. llvm#88884
1 parent df762a1 commit d72146f

File tree

15 files changed

+949
-168
lines changed

15 files changed

+949
-168
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,10 @@ Bug Fixes in This Version
460460

461461
- Fixed an assertion failure on invalid InitListExpr in C89 mode (#GH88008).
462462

463+
- Fixed missing destructor calls when we branch from middle of an expression.
464+
This could happen through a branch in stmt-expr or in an expression containing a coroutine
465+
suspension. Fixes (#GH63818) (#GH88478).
466+
463467
- Clang will no longer diagnose an erroneous non-dependent ``switch`` condition
464468
during instantiation, and instead will only diagnose it once, during checking
465469
of the function template.

clang/lib/CodeGen/CGCall.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4698,11 +4698,11 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
46984698
AggValueSlot Slot = args.isUsingInAlloca()
46994699
? createPlaceholderSlot(*this, type) : CreateAggTemp(type, "agg.tmp");
47004700

4701-
bool DestroyedInCallee = true, NeedsEHCleanup = true;
4701+
bool DestroyedInCallee = true, NeedsCleanup = true;
47024702
if (const auto *RD = type->getAsCXXRecordDecl())
47034703
DestroyedInCallee = RD->hasNonTrivialDestructor();
47044704
else
4705-
NeedsEHCleanup = needsEHCleanup(type.isDestructedType());
4705+
NeedsCleanup = type.isDestructedType();
47064706

47074707
if (DestroyedInCallee)
47084708
Slot.setExternallyDestructed();
@@ -4711,14 +4711,15 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
47114711
RValue RV = Slot.asRValue();
47124712
args.add(RV, type);
47134713

4714-
if (DestroyedInCallee && NeedsEHCleanup) {
4714+
if (DestroyedInCallee && NeedsCleanup) {
47154715
// Create a no-op GEP between the placeholder and the cleanup so we can
47164716
// RAUW it successfully. It also serves as a marker of the first
47174717
// instruction where the cleanup is active.
4718-
pushFullExprCleanup<DestroyUnpassedArg>(EHCleanup, Slot.getAddress(),
4719-
type);
4718+
pushFullExprCleanup<DestroyUnpassedArg>(NormalAndEHCleanup,
4719+
Slot.getAddress(), type);
47204720
// This unreachable is a temporary marker which will be removed later.
4721-
llvm::Instruction *IsActive = Builder.CreateUnreachable();
4721+
llvm::Instruction *IsActive =
4722+
Builder.CreateFlagLoad(llvm::Constant::getNullValue(Int8PtrTy));
47224723
args.addArgCleanupDeactivation(EHStack.stable_begin(), IsActive);
47234724
}
47244725
return;

clang/lib/CodeGen/CGCleanup.cpp

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -634,12 +634,19 @@ static void destroyOptimisticNormalEntry(CodeGenFunction &CGF,
634634
/// Pops a cleanup block. If the block includes a normal cleanup, the
635635
/// current insertion point is threaded through the cleanup, as are
636636
/// any branch fixups on the cleanup.
637-
void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
637+
void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough,
638+
bool ForDeactivation) {
638639
assert(!EHStack.empty() && "cleanup stack is empty!");
639640
assert(isa<EHCleanupScope>(*EHStack.begin()) && "top not a cleanup!");
640641
EHCleanupScope &Scope = cast<EHCleanupScope>(*EHStack.begin());
641642
assert(Scope.getFixupDepth() <= EHStack.getNumBranchFixups());
642643

644+
// If we are deactivating a normal cleanup, we need to pretend that the
645+
// fallthrough is unreachable. We restore this IP before returning.
646+
CGBuilderTy::InsertPoint NormalDeactivateOrigIP;
647+
if (ForDeactivation && (Scope.isNormalCleanup() || !getLangOpts().EHAsynch)) {
648+
NormalDeactivateOrigIP = Builder.saveAndClearIP();
649+
}
643650
// Remember activation information.
644651
bool IsActive = Scope.isActive();
645652
Address NormalActiveFlag =
@@ -667,7 +674,8 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
667674

668675
// - whether there's a fallthrough
669676
llvm::BasicBlock *FallthroughSource = Builder.GetInsertBlock();
670-
bool HasFallthrough = (FallthroughSource != nullptr && IsActive);
677+
bool HasFallthrough =
678+
FallthroughSource != nullptr && (IsActive || HasExistingBranches);
671679

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

693701
// If we have a prebranched fallthrough into an inactive normal
694702
// cleanup, rewrite it so that it leads to the appropriate place.
695-
if (Scope.isNormalCleanup() && HasPrebranchedFallthrough && !IsActive) {
703+
if (Scope.isNormalCleanup() && HasPrebranchedFallthrough &&
704+
!RequiresNormalCleanup) {
705+
// FIXME: Come up with a program which would need forwarding prebranched
706+
// fallthrough and add tests. Otherwise delete this and assert against it.
707+
assert(!IsActive);
696708
llvm::BasicBlock *prebranchDest;
697709

698710
// If the prebranch is semantically branching through the next
@@ -724,6 +736,8 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
724736
EHStack.popCleanup(); // safe because there are no fixups
725737
assert(EHStack.getNumBranchFixups() == 0 ||
726738
EHStack.hasNormalCleanups());
739+
if (NormalDeactivateOrigIP.isSet())
740+
Builder.restoreIP(NormalDeactivateOrigIP);
727741
return;
728742
}
729743

@@ -760,11 +774,19 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
760774
if (!RequiresNormalCleanup) {
761775
// Mark CPP scope end for passed-by-value Arg temp
762776
// per Windows ABI which is "normally" Cleanup in callee
763-
if (IsEHa && getInvokeDest() && Builder.GetInsertBlock()) {
764-
if (Personality.isMSVCXXPersonality())
777+
if (IsEHa && getInvokeDest()) {
778+
// If we are deactivating a normal cleanup then we don't have a
779+
// fallthrough. Restore original IP to emit CPP scope ends in the correct
780+
// block.
781+
if (NormalDeactivateOrigIP.isSet())
782+
Builder.restoreIP(NormalDeactivateOrigIP);
783+
if (Personality.isMSVCXXPersonality() && Builder.GetInsertBlock())
765784
EmitSehCppScopeEnd();
785+
if (NormalDeactivateOrigIP.isSet())
786+
NormalDeactivateOrigIP = Builder.saveAndClearIP();
766787
}
767788
destroyOptimisticNormalEntry(*this, Scope);
789+
Scope.MarkEmitted();
768790
EHStack.popCleanup();
769791
} else {
770792
// If we have a fallthrough and no other need for the cleanup,
@@ -781,6 +803,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
781803
}
782804

783805
destroyOptimisticNormalEntry(*this, Scope);
806+
Scope.MarkEmitted();
784807
EHStack.popCleanup();
785808

786809
EmitCleanup(*this, Fn, cleanupFlags, NormalActiveFlag);
@@ -916,6 +939,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
916939
}
917940

918941
// IV. Pop the cleanup and emit it.
942+
Scope.MarkEmitted();
919943
EHStack.popCleanup();
920944
assert(EHStack.hasNormalCleanups() == HasEnclosingCleanups);
921945

@@ -984,6 +1008,8 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
9841008
}
9851009
}
9861010

1011+
if (NormalDeactivateOrigIP.isSet())
1012+
Builder.restoreIP(NormalDeactivateOrigIP);
9871013
assert(EHStack.hasNormalCleanups() || EHStack.getNumBranchFixups() == 0);
9881014

9891015
// Emit the EH cleanup if required.
@@ -1143,25 +1169,6 @@ void CodeGenFunction::EmitBranchThroughCleanup(JumpDest Dest) {
11431169
Builder.ClearInsertionPoint();
11441170
}
11451171

1146-
static bool IsUsedAsNormalCleanup(EHScopeStack &EHStack,
1147-
EHScopeStack::stable_iterator C) {
1148-
// If we needed a normal block for any reason, that counts.
1149-
if (cast<EHCleanupScope>(*EHStack.find(C)).getNormalBlock())
1150-
return true;
1151-
1152-
// Check whether any enclosed cleanups were needed.
1153-
for (EHScopeStack::stable_iterator
1154-
I = EHStack.getInnermostNormalCleanup();
1155-
I != C; ) {
1156-
assert(C.strictlyEncloses(I));
1157-
EHCleanupScope &S = cast<EHCleanupScope>(*EHStack.find(I));
1158-
if (S.getNormalBlock()) return true;
1159-
I = S.getEnclosingNormalCleanup();
1160-
}
1161-
1162-
return false;
1163-
}
1164-
11651172
static bool IsUsedAsEHCleanup(EHScopeStack &EHStack,
11661173
EHScopeStack::stable_iterator cleanup) {
11671174
// If we needed an EH block for any reason, that counts.
@@ -1210,8 +1217,7 @@ static void SetupCleanupBlockActivation(CodeGenFunction &CGF,
12101217
// Calculate whether the cleanup was used:
12111218

12121219
// - as a normal cleanup
1213-
if (Scope.isNormalCleanup() &&
1214-
(isActivatedInConditional || IsUsedAsNormalCleanup(CGF.EHStack, C))) {
1220+
if (Scope.isNormalCleanup()) {
12151221
Scope.setTestFlagInNormalCleanup();
12161222
needFlag = true;
12171223
}
@@ -1224,13 +1230,16 @@ static void SetupCleanupBlockActivation(CodeGenFunction &CGF,
12241230
}
12251231

12261232
// If it hasn't yet been used as either, we're done.
1227-
if (!needFlag) return;
1233+
if (!needFlag)
1234+
return;
12281235

12291236
Address var = Scope.getActiveFlag();
12301237
if (!var.isValid()) {
1238+
CodeGenFunction::AllocaTrackerRAII AllocaTracker(CGF);
12311239
var = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), CharUnits::One(),
12321240
"cleanup.isactive");
12331241
Scope.setActiveFlag(var);
1242+
Scope.AddAuxAllocas(AllocaTracker.Take());
12341243

12351244
assert(dominatingIP && "no existing variable and no dominating IP!");
12361245

@@ -1273,17 +1282,8 @@ void CodeGenFunction::DeactivateCleanupBlock(EHScopeStack::stable_iterator C,
12731282
// to the current RunCleanupsScope.
12741283
if (C == EHStack.stable_begin() &&
12751284
CurrentCleanupScopeDepth.strictlyEncloses(C)) {
1276-
// Per comment below, checking EHAsynch is not really necessary
1277-
// it's there to assure zero-impact w/o EHAsynch option
1278-
if (!Scope.isNormalCleanup() && getLangOpts().EHAsynch) {
1279-
PopCleanupBlock();
1280-
} else {
1281-
// If it's a normal cleanup, we need to pretend that the
1282-
// fallthrough is unreachable.
1283-
CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP();
1284-
PopCleanupBlock();
1285-
Builder.restoreIP(SavedIP);
1286-
}
1285+
PopCleanupBlock(/*FallthroughIsBranchThrough=*/false,
1286+
/*ForDeactivation=*/true);
12871287
return;
12881288
}
12891289

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

0 commit comments

Comments
 (0)