Skip to content

Commit fdfa695

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 c5cd1e9 commit fdfa695

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
@@ -3595,15 +3595,26 @@ static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) {
35953595
llvm::BasicBlock *IP = CGF.Builder.GetInsertBlock();
35963596
if (IP->empty()) return nullptr;
35973597

3598-
// Look at directly preceding instruction, skipping bitcasts and lifetime
3599-
// markers.
3598+
// Look at directly preceding instruction, skipping bitcasts, lifetime
3599+
// markers, and fake uses and their operands.
3600+
const llvm::Instruction *LoadIntoFakeUse = nullptr;
36003601
for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) {
3602+
// Ignore instructions that are just loads for fake uses; the load should
3603+
// immediately precede the fake use, so we only need to remember the
3604+
// operand for the last fake use seen.
3605+
if (LoadIntoFakeUse == &I)
3606+
continue;
36013607
if (isa<llvm::BitCastInst>(&I))
36023608
continue;
3603-
if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I))
3609+
if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I)) {
36043610
if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end)
36053611
continue;
36063612

3613+
if (II->getIntrinsicID() == llvm::Intrinsic::fake_use) {
3614+
LoadIntoFakeUse = dyn_cast<llvm::Instruction>(II->getArgOperand(0));
3615+
continue;
3616+
}
3617+
}
36073618
return GetStoreIfValid(&I);
36083619
}
36093620
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
@@ -1352,6 +1352,14 @@ void CodeGenFunction::EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr) {
13521352
C->setDoesNotThrow();
13531353
}
13541354

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

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

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

@@ -2524,6 +2576,14 @@ llvm::Function *CodeGenModule::getLLVMLifetimeEndFn() {
25242576
return LifetimeEndFn;
25252577
}
25262578

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

27182778
setAddrOfLocalVar(&D, DeclPtr);
27192779

2780+
// Push a FakeUse 'cleanup' object onto the EHStack for the parameter,
2781+
// which may be the 'this' pointer. This causes the emission of a fake.use
2782+
// call with the parameter as argument at the end of the function.
2783+
if (CGM.getCodeGenOpts().ExtendLifetimes ||
2784+
(CGM.getCodeGenOpts().ExtendThisPtr && &D == CXXABIThisDecl)) {
2785+
if (extendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl))
2786+
EHStack.pushCleanup<FakeUse>(NormalFakeUse, DeclPtr);
2787+
}
2788+
27202789
// Emit debug info for param declarations in non-thunk functions.
27212790
if (CGDebugInfo *DI = getDebugInfo()) {
27222791
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.
@@ -4991,6 +5005,8 @@ class CodeGenFunction : public CodeGenTypeCache {
49915005

49925006
RValue EmitAtomicExpr(AtomicExpr *E);
49935007

5008+
void EmitFakeUse(Address Addr);
5009+
49945010
//===--------------------------------------------------------------------===//
49955011
// Annotations Emission
49965012
//===--------------------------------------------------------------------===//

clang/lib/CodeGen/CodeGenModule.h

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

606+
/// void @llvm.fake.use(...)
607+
llvm::Function *FakeUseFn = nullptr;
608+
606609
std::unique_ptr<SanitizerMetadata> SanitizerMD;
607610

608611
llvm::MapVector<const Decl *, bool> DeferredEmptyCoverageMappingDecls;
@@ -1280,6 +1283,7 @@ class CodeGenModule : public CodeGenTypeCache {
12801283

12811284
llvm::Function *getLLVMLifetimeStartFn();
12821285
llvm::Function *getLLVMLifetimeEndFn();
1286+
llvm::Function *getLLVMFakeUseFn();
12831287

12841288
// Make sure that this type is translated.
12851289
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: 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+
}

0 commit comments

Comments
 (0)