Skip to content

Commit 4424c44

Browse files
wolfy1961SLTozer
andcommitted
[Clang] Add fake use emission to Clang with -fextend-lifetimes (#110102)
Following the previous patch which adds the "extend lifetimes" flag without (almost) any functionality, this patch adds the real feature by allowing Clang to emit fake uses. These are emitted as a new form of cleanup, set for variable addresses, which just emits a fake use intrinsic when the variable falls out of scope. The code for achieving this is simple, with most of the logic centered on determining whether to emit a fake use for a given address, and on ensuring that fake uses are ignored in a few cases. Co-authored-by: Stephen Tozer <[email protected]>
1 parent 500a183 commit 4424c44

20 files changed

+483
-10
lines changed

clang/lib/CodeGen/CGCall.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3581,15 +3581,26 @@ static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) {
35813581
llvm::BasicBlock *IP = CGF.Builder.GetInsertBlock();
35823582
if (IP->empty()) return nullptr;
35833583

3584-
// Look at directly preceding instruction, skipping bitcasts and lifetime
3585-
// markers.
3584+
// Look at directly preceding instruction, skipping bitcasts, lifetime
3585+
// markers, and fake uses and their operands.
3586+
const llvm::Instruction *LoadIntoFakeUse = nullptr;
35863587
for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) {
3588+
// Ignore instructions that are just loads for fake uses; the load should
3589+
// immediately precede the fake use, so we only need to remember the
3590+
// operand for the last fake use seen.
3591+
if (LoadIntoFakeUse == &I)
3592+
continue;
35873593
if (isa<llvm::BitCastInst>(&I))
35883594
continue;
3589-
if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I))
3595+
if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I)) {
35903596
if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end)
35913597
continue;
35923598

3599+
if (II->getIntrinsicID() == llvm::Intrinsic::fake_use) {
3600+
LoadIntoFakeUse = dyn_cast<llvm::Instruction>(II->getArgOperand(0));
3601+
continue;
3602+
}
3603+
}
35933604
return GetStoreIfValid(&I);
35943605
}
35953606
return nullptr;

clang/lib/CodeGen/CGCleanup.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,15 @@ void EHScopeStack::deallocate(size_t Size) {
112112
StartOfData += llvm::alignTo(Size, ScopeStackAlignment);
113113
}
114114

115-
bool EHScopeStack::containsOnlyLifetimeMarkers(
115+
bool EHScopeStack::containsOnlyNoopCleanups(
116116
EHScopeStack::stable_iterator Old) const {
117117
for (EHScopeStack::iterator it = begin(); stabilize(it) != Old; it++) {
118118
EHCleanupScope *cleanup = dyn_cast<EHCleanupScope>(&*it);
119-
if (!cleanup || !cleanup->isLifetimeMarker())
119+
// If this is anything other than a lifetime marker or fake use cleanup,
120+
// then the scope stack does not contain only noop cleanups.
121+
if (!cleanup)
122+
return false;
123+
if (!cleanup->isLifetimeMarker() && !cleanup->isFakeUse())
120124
return false;
121125
}
122126

@@ -154,6 +158,7 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) {
154158
bool IsNormalCleanup = Kind & NormalCleanup;
155159
bool IsEHCleanup = Kind & EHCleanup;
156160
bool IsLifetimeMarker = Kind & LifetimeMarker;
161+
bool IsFakeUse = Kind & FakeUse;
157162

158163
// Per C++ [except.terminate], it is implementation-defined whether none,
159164
// some, or all cleanups are called before std::terminate. Thus, when
@@ -176,6 +181,8 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) {
176181
InnermostEHScope = stable_begin();
177182
if (IsLifetimeMarker)
178183
Scope->setLifetimeMarker();
184+
if (IsFakeUse)
185+
Scope->setFakeUse();
179186

180187
// With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup
181188
// If exceptions are disabled/ignored and SEH is not in use, then there is no

clang/lib/CodeGen/CGCleanup.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ class EHScope {
8787
LLVM_PREFERRED_TYPE(bool)
8888
unsigned IsLifetimeMarker : 1;
8989

90+
/// Whether this cleanup is a fake use
91+
LLVM_PREFERRED_TYPE(bool)
92+
unsigned IsFakeUse : 1;
93+
9094
/// Whether the normal cleanup should test the activation flag.
9195
LLVM_PREFERRED_TYPE(bool)
9296
unsigned TestFlagInNormalCleanup : 1;
@@ -352,6 +356,7 @@ class alignas(8) EHCleanupScope : public EHScope {
352356
CleanupBits.IsEHCleanup = isEH;
353357
CleanupBits.IsActive = true;
354358
CleanupBits.IsLifetimeMarker = false;
359+
CleanupBits.IsFakeUse = false;
355360
CleanupBits.TestFlagInNormalCleanup = false;
356361
CleanupBits.TestFlagInEHCleanup = false;
357362
CleanupBits.CleanupSize = cleanupSize;
@@ -384,6 +389,9 @@ class alignas(8) EHCleanupScope : public EHScope {
384389
bool isLifetimeMarker() const { return CleanupBits.IsLifetimeMarker; }
385390
void setLifetimeMarker() { CleanupBits.IsLifetimeMarker = true; }
386391

392+
bool isFakeUse() const { return CleanupBits.IsFakeUse; }
393+
void setFakeUse() { CleanupBits.IsFakeUse = true; }
394+
387395
bool hasActiveFlag() const { return ActiveFlag.isValid(); }
388396
Address getActiveFlag() const {
389397
return ActiveFlag;

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,14 @@ void CodeGenFunction::EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr) {
13551355
C->setDoesNotThrow();
13561356
}
13571357

1358+
void CodeGenFunction::EmitFakeUse(Address Addr) {
1359+
auto NL = ApplyDebugLocation::CreateEmpty(*this);
1360+
llvm::Value *V = Builder.CreateLoad(Addr, "fake.use");
1361+
llvm::CallInst *C = Builder.CreateCall(CGM.getLLVMFakeUseFn(), {V});
1362+
C->setDoesNotThrow();
1363+
C->setTailCallKind(llvm::CallInst::TCK_NoTail);
1364+
}
1365+
13581366
void CodeGenFunction::EmitAndRegisterVariableArrayDimensions(
13591367
CGDebugInfo *DI, const VarDecl &D, bool EmitDebugInfo) {
13601368
// For each dimension stores its QualType and corresponding
@@ -1414,6 +1422,39 @@ void CodeGenFunction::EmitAndRegisterVariableArrayDimensions(
14141422
}
14151423
}
14161424

1425+
/// Return the maximum size of an aggregate for which we generate a fake use
1426+
/// intrinsic when -fextend-lifetimes is in effect.
1427+
static uint64_t maxFakeUseAggregateSize(const ASTContext &C) {
1428+
return 4 * C.getTypeSize(C.UnsignedIntTy);
1429+
}
1430+
1431+
// Helper function to determine whether a variable's or parameter's lifetime
1432+
// should be extended.
1433+
static bool shouldExtendLifetime(const ASTContext &Context,
1434+
const Decl *FuncDecl, const VarDecl &D,
1435+
ImplicitParamDecl *CXXABIThisDecl) {
1436+
// When we're not inside a valid function it is unlikely that any
1437+
// lifetime extension is useful.
1438+
if (!FuncDecl)
1439+
return false;
1440+
if (FuncDecl->isImplicit())
1441+
return false;
1442+
// Do not extend compiler-created variables except for the this pointer.
1443+
if (D.isImplicit() && &D != CXXABIThisDecl)
1444+
return false;
1445+
QualType Ty = D.getType();
1446+
// No need to extend volatiles, they have a memory location.
1447+
if (Ty.isVolatileQualified())
1448+
return false;
1449+
// Don't extend variables that exceed a certain size.
1450+
if (Context.getTypeSize(Ty) > maxFakeUseAggregateSize(Context))
1451+
return false;
1452+
// Do not extend variables in nodebug or optnone functions.
1453+
if (FuncDecl->hasAttr<NoDebugAttr>() || FuncDecl->hasAttr<OptimizeNoneAttr>())
1454+
return false;
1455+
return true;
1456+
}
1457+
14171458
/// EmitAutoVarAlloca - Emit the alloca and debug information for a
14181459
/// local variable. Does not emit initialization or destruction.
14191460
CodeGenFunction::AutoVarEmission
@@ -1666,6 +1707,18 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
16661707
emission.getOriginalAllocatedAddress(),
16671708
emission.getSizeForLifetimeMarkers());
16681709

1710+
// Analogous to lifetime markers, we use a 'cleanup' to emit fake.use
1711+
// calls for local variables. We are exempting volatile variables and
1712+
// non-scalars larger than 4 times the size of an unsigned int. Larger
1713+
// non-scalars are often allocated in memory and may create unnecessary
1714+
// overhead.
1715+
if (CGM.getCodeGenOpts().getExtendVariableLiveness() ==
1716+
CodeGenOptions::ExtendVariableLivenessKind::All) {
1717+
if (shouldExtendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl))
1718+
EHStack.pushCleanup<FakeUse>(NormalFakeUse,
1719+
emission.getAllocatedAddress());
1720+
}
1721+
16691722
return emission;
16701723
}
16711724

@@ -2532,6 +2585,14 @@ llvm::Function *CodeGenModule::getLLVMLifetimeEndFn() {
25322585
return LifetimeEndFn;
25332586
}
25342587

2588+
/// Lazily declare the @llvm.fake.use intrinsic.
2589+
llvm::Function *CodeGenModule::getLLVMFakeUseFn() {
2590+
if (!FakeUseFn)
2591+
FakeUseFn = llvm::Intrinsic::getDeclaration(&getModule(),
2592+
llvm::Intrinsic::fake_use);
2593+
return FakeUseFn;
2594+
}
2595+
25352596
namespace {
25362597
/// A cleanup to perform a release of an object at the end of a
25372598
/// function. This is used to balance out the incoming +1 of a
@@ -2725,6 +2786,18 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg,
27252786

27262787
setAddrOfLocalVar(&D, DeclPtr);
27272788

2789+
// Push a FakeUse 'cleanup' object onto the EHStack for the parameter,
2790+
// which may be the 'this' pointer. This causes the emission of a fake.use
2791+
// call with the parameter as argument at the end of the function.
2792+
if (CGM.getCodeGenOpts().getExtendVariableLiveness() ==
2793+
CodeGenOptions::ExtendVariableLivenessKind::All ||
2794+
(CGM.getCodeGenOpts().getExtendVariableLiveness() ==
2795+
CodeGenOptions::ExtendVariableLivenessKind::This &&
2796+
&D == CXXABIThisDecl)) {
2797+
if (shouldExtendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl))
2798+
EHStack.pushCleanup<FakeUse>(NormalFakeUse, DeclPtr);
2799+
}
2800+
27282801
// Emit debug info for param declarations in non-thunk functions.
27292802
if (CGDebugInfo *DI = getDebugInfo()) {
27302803
if (CGM.getCodeGenOpts().hasReducedDebugInfo() && !CurFuncIsThunk &&

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,9 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) {
404404
// important to do this before we enter the return block or return
405405
// edges will be *really* confused.
406406
bool HasCleanups = EHStack.stable_begin() != PrologueCleanupDepth;
407-
bool HasOnlyLifetimeMarkers =
408-
HasCleanups && EHStack.containsOnlyLifetimeMarkers(PrologueCleanupDepth);
409-
bool EmitRetDbgLoc = !HasCleanups || HasOnlyLifetimeMarkers;
407+
bool HasOnlyNoopCleanups =
408+
HasCleanups && EHStack.containsOnlyNoopCleanups(PrologueCleanupDepth);
409+
bool EmitRetDbgLoc = !HasCleanups || HasOnlyNoopCleanups;
410410

411411
std::optional<ApplyDebugLocation> OAL;
412412
if (HasCleanups) {

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,20 @@ class CodeGenFunction : public CodeGenTypeCache {
723723
}
724724
};
725725

726+
// We are using objects of this 'cleanup' class to emit fake.use calls
727+
// for -fextend-lifetimes and -fextend-this-ptr. They are placed at the end of
728+
// a variable's scope analogous to lifetime markers.
729+
class FakeUse final : public EHScopeStack::Cleanup {
730+
Address Addr;
731+
732+
public:
733+
FakeUse(Address addr) : Addr(addr) {}
734+
735+
void Emit(CodeGenFunction &CGF, Flags flags) override {
736+
CGF.EmitFakeUse(Addr);
737+
}
738+
};
739+
726740
/// Header for data within LifetimeExtendedCleanupStack.
727741
struct LifetimeExtendedCleanupHeader {
728742
/// The size of the following cleanup object.
@@ -5075,6 +5089,8 @@ class CodeGenFunction : public CodeGenTypeCache {
50755089

50765090
RValue EmitAtomicExpr(AtomicExpr *E);
50775091

5092+
void EmitFakeUse(Address Addr);
5093+
50785094
//===--------------------------------------------------------------------===//
50795095
// Annotations Emission
50805096
//===--------------------------------------------------------------------===//

clang/lib/CodeGen/CodeGenModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,9 @@ class CodeGenModule : public CodeGenTypeCache {
647647
/// void @llvm.lifetime.end(i64 %size, i8* nocapture <ptr>)
648648
llvm::Function *LifetimeEndFn = nullptr;
649649

650+
/// void @llvm.fake.use(...)
651+
llvm::Function *FakeUseFn = nullptr;
652+
650653
std::unique_ptr<SanitizerMetadata> SanitizerMD;
651654

652655
llvm::MapVector<const Decl *, bool> DeferredEmptyCoverageMappingDecls;
@@ -1326,6 +1329,7 @@ class CodeGenModule : public CodeGenTypeCache {
13261329

13271330
llvm::Function *getLLVMLifetimeStartFn();
13281331
llvm::Function *getLLVMLifetimeEndFn();
1332+
llvm::Function *getLLVMFakeUseFn();
13291333

13301334
// Make sure that this type is translated.
13311335
void UpdateCompletedType(const TagDecl *TD);

clang/lib/CodeGen/EHScopeStack.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ enum CleanupKind : unsigned {
8787

8888
LifetimeMarker = 0x8,
8989
NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup,
90+
91+
// FakeUse needs to be recognized as a special cleanup similar to lifetime
92+
// markers chiefly to be ignored in most contexts.
93+
FakeUse = 0x10,
94+
NormalFakeUse = FakeUse | NormalCleanup,
9095
};
9196

9297
/// A stack of scopes which respond to exceptions, including cleanups
@@ -352,8 +357,8 @@ class EHScopeStack {
352357
void popTerminate();
353358

354359
// Returns true iff the current scope is either empty or contains only
355-
// lifetime markers, i.e. no real cleanup code
356-
bool containsOnlyLifetimeMarkers(stable_iterator Old) const;
360+
// noop cleanups, i.e. lifetime markers and fake uses.
361+
bool containsOnlyNoopCleanups(stable_iterator Old) const;
357362

358363
/// Determines whether the exception-scopes stack is empty.
359364
bool empty() const { return StartOfData == EndOfBuffer; }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -fcxx-exceptions -fexceptions -o - | FileCheck %s
2+
// This test checks that the fake uses can be generated in exception handling
3+
// blocks and that we can emit fake uses for the __int128 data type.
4+
5+
extern int bar();
6+
7+
/// Try block: fake use ends at try-block scope.
8+
// [[BAR_VAL::%[a-zA-Z0-9\.]+]] = invoke{{.*}} i32 @_Z3barv()
9+
// store i32 %[[BAR_VAL]], ptr [[K_ALLOC_VAL:%[a-zA-Z0-9\.]+]], align 4
10+
// [[K_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr [[K_ALLOC_VAL]], align 4
11+
// call void (...) @llvm.fake.use(i32 [[K_FAKE_USE]]) #2
12+
// br label
13+
14+
/// Catch block: fetching the caught value...
15+
// CHECK: [[CATCH_PTR:%[a-zA-Z0-9\.]+]] = call ptr @__cxa_begin_catch(
16+
// CHECK: [[L_VAL:%[a-zA-Z0-9\.]+]] = load i32, ptr [[CATCH_PTR]], align 4
17+
18+
/// Storing to allocas...
19+
// CHECK-DAG: store i32 8, ptr [[M_ALLOC_VAL:%[a-zA-Z0-9\.]+]]
20+
// CHECK-DAG: store i32 [[L_VAL]], ptr [[L_ALLOC_VAL:%[a-zA-Z0-9\.]+]], align 4
21+
22+
/// Load into fake uses - expect M to precede L.
23+
// CHECK: [[M_FAKE_VAL:%[a-zA-Z0-9\.]+]] = load i32, ptr [[M_ALLOC_VAL]]
24+
// CHECK: call void (...) @llvm.fake.use(i32 [[M_FAKE_VAL]])
25+
// CHECK: [[L_FAKE_VAL:%[a-zA-Z0-9\.]+]] = load i32, ptr [[L_ALLOC_VAL]]
26+
// CHECK: call void (...) @llvm.fake.use(i32 [[L_FAKE_VAL]])
27+
void foo() {
28+
try {
29+
int k = bar();
30+
} catch (int l) {
31+
/// The catch block contains a fake use for the local within its scope.
32+
int m = 8;
33+
}
34+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -triple x86_64-unknown-linux -o - | FileCheck %s
2+
// REQUIRES: x86-registered-target
3+
// This test checks that the fake uses can be generated in exception handling
4+
// blocks and that we can emit fake uses for the __int128 data type.
5+
6+
void bar();
7+
8+
// CHECK: call void (...) @llvm.fake.use(i128 %
9+
void foo(__int128 wide_int) {
10+
bar();
11+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -o - | FileCheck %s --implicit-check-not=llvm.fake.use
2+
// Check that fake use calls are emitted at the correct locations, i.e.
3+
// at the end of lexical blocks and at the end of the function.
4+
5+
int glob_i;
6+
char glob_c;
7+
float glob_f;
8+
9+
int foo(int i) {
10+
// CHECK-LABEL: define{{.*}}foo
11+
if (i < 4) {
12+
char j = i * 3;
13+
if (glob_i > 3) {
14+
float f = glob_f;
15+
j = f;
16+
glob_c = j;
17+
// CHECK: call void (...) @llvm.fake.use(float %
18+
// CHECK-NEXT: br label %
19+
}
20+
glob_i = j;
21+
// CHECK: call void (...) @llvm.fake.use(i8 %
22+
// CHECK-NEXT: br label %
23+
}
24+
// CHECK: call void (...) @llvm.fake.use(i32 %
25+
// CHECK-NEXT: ret
26+
return 4;
27+
}
28+
29+
// CHECK: declare void @llvm.fake.use(...)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %clang_cc1 -emit-llvm -fextend-variable-liveness %s -o - | FileCheck %s
2+
//
3+
// We are checking that the fake.use calls for i, j and k appear
4+
// in a particular order. It is not the order itself that is important
5+
// but that it remains the same between different test runs.
6+
7+
// CHECK: [[K_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %k.addr
8+
// CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[K_FAKE_USE]]) #2
9+
// CHECK-NEXT: [[J_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %j.addr
10+
// CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[J_FAKE_USE]]) #2
11+
// CHECK-NEXT: [[I_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %i.addr
12+
// CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[I_FAKE_USE]]) #2
13+
14+
void bar();
15+
void foo(int i, int j, int k)
16+
{
17+
for (int l = 0; l < i; l++) {
18+
bar();
19+
}
20+
}

0 commit comments

Comments
 (0)