Skip to content

Commit 7d8646e

Browse files
committed
[Clang] Add fake use emission to Clang with -fextend-lifetimes
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.
1 parent 22829f7 commit 7d8646e

21 files changed

+503
-10
lines changed

clang/lib/CodeGen/CGCall.cpp

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

3569-
// Look at directly preceding instruction, skipping bitcasts and lifetime
3570-
// markers.
3569+
// Look at directly preceding instruction, skipping bitcasts, lifetime
3570+
// markers, and fake uses and their operands.
3571+
const llvm::Instruction *LoadIntoFakeUse = nullptr;
35713572
for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) {
3573+
// Ignore instructions that are just loads for fake uses; the load should
3574+
// immediately precede the fake use, so we only need to remember the
3575+
// operand for the last fake use seen.
3576+
if (LoadIntoFakeUse == &I)
3577+
continue;
35723578
if (isa<llvm::BitCastInst>(&I))
35733579
continue;
3574-
if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I))
3580+
if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I)) {
35753581
if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end)
35763582
continue;
35773583

3584+
if (II->getIntrinsicID() == llvm::Intrinsic::fake_use) {
3585+
LoadIntoFakeUse = dyn_cast<llvm::Instruction>(II->getArgOperand(0));
3586+
continue;
3587+
}
3588+
}
35783589
return GetStoreIfValid(&I);
35793590
}
35803591
return nullptr;

clang/lib/CodeGen/CGCleanup.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,11 @@ 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 (!cleanup || !(cleanup->isLifetimeMarker() || cleanup->isFakeUse()))
120120
return false;
121121
}
122122

@@ -154,6 +154,7 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) {
154154
bool IsNormalCleanup = Kind & NormalCleanup;
155155
bool IsEHCleanup = Kind & EHCleanup;
156156
bool IsLifetimeMarker = Kind & LifetimeMarker;
157+
bool IsFakeUse = Kind & FakeUse;
157158

158159
// Per C++ [except.terminate], it is implementation-defined whether none,
159160
// some, or all cleanups are called before std::terminate. Thus, when
@@ -176,6 +177,8 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) {
176177
InnermostEHScope = stable_begin();
177178
if (IsLifetimeMarker)
178179
Scope->setLifetimeMarker();
180+
if (IsFakeUse)
181+
Scope->setFakeUse();
179182

180183
// With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup
181184
// 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: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,6 +1353,14 @@ void CodeGenFunction::EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr) {
13531353
C->setDoesNotThrow();
13541354
}
13551355

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

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

1708+
// Analogous to lifetime markers, we use a 'cleanup' to emit fake.use
1709+
// calls for local variables. We are exempting volatile variables and
1710+
// non-scalars larger than 4 times the size of an unsigned int (32 bytes).
1711+
// Larger non-scalars are often allocated in memory and may create unnecessary
1712+
// overhead.
1713+
if (CGM.getCodeGenOpts().ExtendLifetimes) {
1714+
if (extendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl))
1715+
EHStack.pushCleanup<FakeUse>(NormalFakeUse,
1716+
emission.getAllocatedAddress());
1717+
}
1718+
16671719
return emission;
16681720
}
16691721

@@ -2523,6 +2575,14 @@ llvm::Function *CodeGenModule::getLLVMLifetimeEndFn() {
25232575
return LifetimeEndFn;
25242576
}
25252577

2578+
/// Lazily declare the @llvm.fake.use intrinsic.
2579+
llvm::Function *CodeGenModule::getLLVMFakeUseFn() {
2580+
if (!FakeUseFn)
2581+
FakeUseFn = llvm::Intrinsic::getDeclaration(&getModule(),
2582+
llvm::Intrinsic::fake_use);
2583+
return FakeUseFn;
2584+
}
2585+
25262586
namespace {
25272587
/// A cleanup to perform a release of an object at the end of a
25282588
/// function. This is used to balance out the incoming +1 of a
@@ -2716,6 +2776,15 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg,
27162776

27172777
setAddrOfLocalVar(&D, DeclPtr);
27182778

2779+
// Push a FakeUse 'cleanup' object onto the EHStack for the parameter,
2780+
// which may be the 'this' pointer. This causes the emission of a fake.use
2781+
// call with the parameter as argument at the end of the function.
2782+
if (CGM.getCodeGenOpts().ExtendLifetimes ||
2783+
(CGM.getCodeGenOpts().ExtendThisPtr && &D == CXXABIThisDecl)) {
2784+
if (extendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl))
2785+
EHStack.pushCleanup<FakeUse>(NormalFakeUse, DeclPtr);
2786+
}
2787+
27192788
// Emit debug info for param declarations in non-thunk functions.
27202789
if (CGDebugInfo *DI = getDebugInfo()) {
27212790
if (CGM.getCodeGenOpts().hasReducedDebugInfo() && !CurFuncIsThunk &&

clang/lib/CodeGen/CodeGenFunction.cpp

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

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

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,20 @@ class CodeGenFunction : public CodeGenTypeCache {
718718
}
719719
};
720720

721+
// We are using objects of this 'cleanup' class to emit fake.use calls
722+
// for -fextend-lifetimes and -fextend-this-ptr. They are placed at the end of
723+
// a variable's scope analogous to lifetime markers.
724+
class FakeUse final : public EHScopeStack::Cleanup {
725+
Address Addr;
726+
727+
public:
728+
FakeUse(Address addr) : Addr(addr) {}
729+
730+
void Emit(CodeGenFunction &CGF, Flags flags) override {
731+
CGF.EmitFakeUse(Addr);
732+
}
733+
};
734+
721735
/// Header for data within LifetimeExtendedCleanupStack.
722736
struct LifetimeExtendedCleanupHeader {
723737
/// The size of the following cleanup object.
@@ -4975,6 +4989,8 @@ class CodeGenFunction : public CodeGenTypeCache {
49754989

49764990
RValue EmitAtomicExpr(AtomicExpr *E);
49774991

4992+
void EmitFakeUse(Address Addr);
4993+
49784994
//===--------------------------------------------------------------------===//
49794995
// Annotations Emission
49804996
//===--------------------------------------------------------------------===//

clang/lib/CodeGen/CodeGenModule.h

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

604+
/// void @llvm.fake.use(...)
605+
llvm::Function *FakeUseFn = nullptr;
606+
604607
std::unique_ptr<SanitizerMetadata> SanitizerMD;
605608

606609
llvm::MapVector<const Decl *, bool> DeferredEmptyCoverageMappingDecls;
@@ -1268,6 +1271,7 @@ class CodeGenModule : public CodeGenTypeCache {
12681271

12691272
llvm::Function *getLLVMLifetimeStartFn();
12701273
llvm::Function *getLLVMLifetimeEndFn();
1274+
llvm::Function *getLLVMFakeUseFn();
12711275

12721276
// Make sure that this type is translated.
12731277
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; }

clang/test/CodeGen/extend-liveness1.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
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+
extern int use(int);
6+
int glob1;
7+
int glob2;
8+
float globf;
9+
10+
int foo(int i) {
11+
// CHECK: define{{.*}}foo
12+
if (i < 4) {
13+
int j = i * 3;
14+
if (glob1 > 3) {
15+
float f = globf;
16+
// CHECK: [[SSAVAL:%[a-z0-9]*]] = load float{{.*}}globf
17+
j = f;
18+
glob2 = j;
19+
// CHECK: store{{.*}}glob2
20+
// CHECK-NEXT: call void (...) @llvm.fake.use(float [[SSAVAL]])
21+
}
22+
glob1 = j;
23+
// CHECK: store{{.*}}glob1
24+
// CHECK-NEXT: call void (...) @llvm.fake.use(i32 %j.
25+
}
26+
// CHECK: call void (...) @llvm.fake.use(i32 %i)
27+
// CHECK-NEXT: ret
28+
return 4;
29+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -fcxx-exceptions -fexceptions -o - | FileCheck %s
2+
// REQUIRES: x86-registered-target
3+
// This test checks that the fake_use concept works with exception handling and that we
4+
// can handle the __int128 data type.
5+
6+
class A {
7+
public:
8+
A(int i) : m_i(i) {}
9+
void func(__int128 i128);
10+
11+
int m_i;
12+
};
13+
14+
extern int bar();
15+
extern void foo();
16+
int glob;
17+
18+
void A::func(__int128 i128) {
19+
int j = 4;
20+
try {
21+
int k = bar();
22+
foo();
23+
// CHECK: [[SSAVAL:%[a-z0-9]*]] = invoke{{.*}}bar
24+
glob = 0;
25+
// CHECK: store{{.*}}glob
26+
// CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[SSAVAL]])
27+
} catch (...) {
28+
foo();
29+
}
30+
// CHECK-LABEL: try.cont:
31+
// CHECK-DAG: call void (...) @llvm.fake.use({{.*%this}})
32+
// CHECK-DAG: call void (...) @llvm.fake.use(i128 %i128.sroa.0.0.insert.insert)
33+
// CHECK: ret void
34+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %clang -S -O2 -emit-llvm -fextend-lifetimes %s -o - | FileCheck %s
2+
// REQUIRES: asserts
3+
//
4+
// We are checking that the fake.use calls for i, j and k appear
5+
// in a particular order. It is not the order itself that is important
6+
// but that it remains the same between different test runs.
7+
8+
// CHECK: call {{.*}}void (...) @llvm.fake.use(i32 %k)
9+
// CHECK-NEXT: call {{.*}}void (...) @llvm.fake.use(i32 %j)
10+
// CHECK-NEXT: call {{.*}}void (...) @llvm.fake.use(i32 %i)
11+
12+
extern void bar();
13+
void foo(int i, int j, int k)
14+
{
15+
for (int l = 0; l < i; l++) {
16+
bar();
17+
}
18+
}

0 commit comments

Comments
 (0)