[clang] [ExtendLifetimes] Add extend lifetimes to emit fake uses from clang (PR #106724)
Stephen Tozer via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 30 05:54:06 PDT 2024
https://github.com/SLTozer created https://github.com/llvm/llvm-project/pull/106724
This patch adds flags to clang to emit fake use intrinsics into IR, preserving the value of variables through codegen to improve the debugging experience. The two flags added are `-fextend-lifetimes`, which extends the lifetime of all variables, and `-fextend-this-ptr`, which extends the lifetime of `this` only. Both of these flags are incompatible with -O0, since without optimizations there is no purpose to extended variable lifetimes; the `-fextend-lifetimes` flag is intended to be set by default at -Og in a later patch.
Using either of these flags adds the `optdebug` attribute to generated functions, which currently only has the purpose of disabling post-RA machine scheduling, due to its negative effect on variable lifetimes that would have been preserved by fake uses (this behaviour is added in a separate patch).
>From 2b529d443d5e8512ffea6566a3d4d1e04e9542e3 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Fri, 30 Aug 2024 12:49:26 +0100
Subject: [PATCH] [ExtendLifetimes] Add extend lifetimes to emit fake uses from
clang
This patch adds flags to clang to emit fake use intrinsics into IR,
preserving the value of variables through codegen to improve the
debugging experience. The two flags added are `-fextend-lifetimes`,
which extends the lifetime of all variables, and `-fextend-this-ptr`,
which extends the lifetime of `this` only. Both of these flags are
incompatible with -O0, since without optimizations there is no purpose
to extended variable lifetimes.
Using either of these flags adds the `optdebug` attribute to generated
functions, which currently only has the purpose of disabling post-RA
machine scheduling, due to its negative effect on variable lifetimes that
would have been preserved by fake uses.
---
clang/include/clang/Basic/CodeGenOptions.def | 6 ++
clang/include/clang/Driver/Options.td | 9 +++
clang/lib/CodeGen/CGCall.cpp | 21 +++++-
clang/lib/CodeGen/CGCleanup.cpp | 7 +-
clang/lib/CodeGen/CGCleanup.h | 7 ++
clang/lib/CodeGen/CGDecl.cpp | 70 +++++++++++++++++++
clang/lib/CodeGen/CodeGenFunction.cpp | 6 +-
clang/lib/CodeGen/CodeGenFunction.h | 16 +++++
clang/lib/CodeGen/CodeGenModule.h | 4 ++
clang/lib/CodeGen/EHScopeStack.h | 9 ++-
clang/lib/Driver/ToolChains/Clang.cpp | 5 ++
clang/lib/Frontend/CompilerInvocation.cpp | 5 ++
.../test/CodeGen/extend-lifetimes-optdebug.c | 8 +++
clang/test/CodeGen/extend-liveness1.c | 29 ++++++++
clang/test/CodeGen/extend-liveness2.cpp | 34 +++++++++
clang/test/CodeGen/fake-use-determinism.c | 18 +++++
clang/test/CodeGen/fake-use-lambda.cpp | 43 ++++++++++++
clang/test/CodeGen/fake-use-landingpad.c | 15 ++++
clang/test/CodeGen/fake-use-noreturn.c | 13 ++++
clang/test/CodeGen/fake-use-return-line.c | 10 +++
clang/test/CodeGen/fake-use-sanitizer.cpp | 37 ++++++++++
clang/test/CodeGen/fake-use-scalar.c | 22 ++++++
clang/test/CodeGen/fake-use-small-aggs.c | 24 +++++++
clang/test/CodeGen/fake-use-while.c | 18 +++++
clang/test/CodeGen/fake-use.cpp | 44 ++++++++++++
clang/test/CodeGen/no-fake-use-O0.cpp | 50 +++++++++++++
26 files changed, 520 insertions(+), 10 deletions(-)
create mode 100644 clang/test/CodeGen/extend-lifetimes-optdebug.c
create mode 100644 clang/test/CodeGen/extend-liveness1.c
create mode 100644 clang/test/CodeGen/extend-liveness2.cpp
create mode 100644 clang/test/CodeGen/fake-use-determinism.c
create mode 100644 clang/test/CodeGen/fake-use-lambda.cpp
create mode 100644 clang/test/CodeGen/fake-use-landingpad.c
create mode 100644 clang/test/CodeGen/fake-use-noreturn.c
create mode 100644 clang/test/CodeGen/fake-use-return-line.c
create mode 100644 clang/test/CodeGen/fake-use-sanitizer.cpp
create mode 100644 clang/test/CodeGen/fake-use-scalar.c
create mode 100644 clang/test/CodeGen/fake-use-small-aggs.c
create mode 100644 clang/test/CodeGen/fake-use-while.c
create mode 100644 clang/test/CodeGen/fake-use.cpp
create mode 100644 clang/test/CodeGen/no-fake-use-O0.cpp
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index b600198998d85b..5bf5c664b46d5a 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -387,6 +387,12 @@ CODEGENOPT(EnableTLSDESC, 1, 0)
/// Bit size of immediate TLS offsets (0 == use the default).
VALUE_CODEGENOPT(TLSSize, 8, 0)
+/// Whether to extend the live range of the `this` pointer.
+CODEGENOPT(ExtendThisPtr, 1, 0)
+
+/// Whether to extend the live ranges of all local variables.
+CODEGENOPT(ExtendLifetimes, 1, 0)
+
/// The default stack protector guard offset to use.
VALUE_CODEGENOPT(StackProtectorGuardOffset, 32, INT_MAX)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 83cf753e824845..281bdffec4c6ef 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4243,6 +4243,15 @@ def stack_usage_file : Separate<["-"], "stack-usage-file">,
Visibility<[CC1Option]>,
HelpText<"Filename (or -) to write stack usage output to">,
MarshallingInfoString<CodeGenOpts<"StackUsageOutput">>;
+def fextend_this_ptr : Flag <["-"], "fextend-this-ptr">, Group<f_Group>,
+ MarshallingInfoFlag<CodeGenOpts<"ExtendThisPtr">>,
+ HelpText<"Extend the lifetime of the 'this' pointer to improve visibility "
+ "in optimized debugging">, Visibility<[ClangOption, CC1Option]>;
+def fextend_lifetimes : Flag <["-"], "fextend-lifetimes">, Group<f_Group>,
+ MarshallingInfoFlag<CodeGenOpts<"ExtendLifetimes">>,
+ HelpText<"Extend the lifetimes of local variables and parameters to improve "
+ "visibility in optimized debugging">,
+ Visibility<[ClangOption, CC1Option]>;
defm unique_basic_block_section_names : BoolFOption<"unique-basic-block-section-names",
CodeGenOpts<"UniqueBasicBlockSectionNames">, DefaultFalse,
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index ca2c79b51ac96b..f4703e6dc11a0e 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -2543,6 +2543,10 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
if (shouldDisableTailCalls())
FuncAttrs.addAttribute("disable-tail-calls", "true");
+ // Suppress the machine instruction scheduler when -fextend-lifetimes is on.
+ if (CodeGenOpts.ExtendLifetimes)
+ FuncAttrs.addAttribute(llvm::Attribute::OptimizeForDebugging);
+
// CPU/feature overrides. addDefaultFunctionDefinitionAttributes
// handles these separately to set them based on the global defaults.
GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs);
@@ -3558,15 +3562,26 @@ static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) {
llvm::BasicBlock *IP = CGF.Builder.GetInsertBlock();
if (IP->empty()) return nullptr;
- // Look at directly preceding instruction, skipping bitcasts and lifetime
- // markers.
+ // Look at directly preceding instruction, skipping bitcasts, lifetime
+ // markers, and fake uses and their operands.
+ const llvm::Instruction *LoadIntoFakeUse = nullptr;
for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) {
+ // Ignore instructions are just loads for fake uses; the load should
+ // immediately precede the fake use, so we only need to remember the
+ // operand for the last fake use seen.
+ if (LoadIntoFakeUse == &I)
+ continue;
if (isa<llvm::BitCastInst>(&I))
continue;
- if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I))
+ if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I)) {
if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end)
continue;
+ if (II->getIntrinsicID() == llvm::Intrinsic::fake_use) {
+ LoadIntoFakeUse = dyn_cast<llvm::Instruction>(II->getArgOperand(0));
+ continue;
+ }
+ }
return GetStoreIfValid(&I);
}
return nullptr;
diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp
index 5d253c92a38a81..82532e182bebbd 100644
--- a/clang/lib/CodeGen/CGCleanup.cpp
+++ b/clang/lib/CodeGen/CGCleanup.cpp
@@ -112,11 +112,11 @@ void EHScopeStack::deallocate(size_t Size) {
StartOfData += llvm::alignTo(Size, ScopeStackAlignment);
}
-bool EHScopeStack::containsOnlyLifetimeMarkers(
+bool EHScopeStack::containsOnlyNoopCleanups(
EHScopeStack::stable_iterator Old) const {
for (EHScopeStack::iterator it = begin(); stabilize(it) != Old; it++) {
EHCleanupScope *cleanup = dyn_cast<EHCleanupScope>(&*it);
- if (!cleanup || !cleanup->isLifetimeMarker())
+ if (!cleanup || !(cleanup->isLifetimeMarker() || cleanup->isFakeUse()))
return false;
}
@@ -154,6 +154,7 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) {
bool IsNormalCleanup = Kind & NormalCleanup;
bool IsEHCleanup = Kind & EHCleanup;
bool IsLifetimeMarker = Kind & LifetimeMarker;
+ bool IsFakeUse = Kind & FakeUse;
// Per C++ [except.terminate], it is implementation-defined whether none,
// some, or all cleanups are called before std::terminate. Thus, when
@@ -176,6 +177,8 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) {
InnermostEHScope = stable_begin();
if (IsLifetimeMarker)
Scope->setLifetimeMarker();
+ if (IsFakeUse)
+ Scope->setFakeUse();
// With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup
// If exceptions are disabled/ignored and SEH is not in use, then there is no
diff --git a/clang/lib/CodeGen/CGCleanup.h b/clang/lib/CodeGen/CGCleanup.h
index c73c97146abc4d..a2800442002a5c 100644
--- a/clang/lib/CodeGen/CGCleanup.h
+++ b/clang/lib/CodeGen/CGCleanup.h
@@ -87,6 +87,9 @@ class EHScope {
LLVM_PREFERRED_TYPE(bool)
unsigned IsLifetimeMarker : 1;
+ /// Whether this cleanup is a fake use
+ unsigned IsFakeUse : 1;
+
/// Whether the normal cleanup should test the activation flag.
LLVM_PREFERRED_TYPE(bool)
unsigned TestFlagInNormalCleanup : 1;
@@ -352,6 +355,7 @@ class alignas(8) EHCleanupScope : public EHScope {
CleanupBits.IsEHCleanup = isEH;
CleanupBits.IsActive = true;
CleanupBits.IsLifetimeMarker = false;
+ CleanupBits.IsFakeUse = false;
CleanupBits.TestFlagInNormalCleanup = false;
CleanupBits.TestFlagInEHCleanup = false;
CleanupBits.CleanupSize = cleanupSize;
@@ -384,6 +388,9 @@ class alignas(8) EHCleanupScope : public EHScope {
bool isLifetimeMarker() const { return CleanupBits.IsLifetimeMarker; }
void setLifetimeMarker() { CleanupBits.IsLifetimeMarker = true; }
+ bool isFakeUse() const { return CleanupBits.IsFakeUse; }
+ void setFakeUse() { CleanupBits.IsFakeUse = true; }
+
bool hasActiveFlag() const { return ActiveFlag.isValid(); }
Address getActiveFlag() const {
return ActiveFlag;
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 563f728e29d781..e464ef38a8fc4e 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1353,6 +1353,14 @@ void CodeGenFunction::EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr) {
C->setDoesNotThrow();
}
+void CodeGenFunction::EmitFakeUse(Address Addr) {
+ auto NL = ApplyDebugLocation::CreateEmpty(*this);
+ llvm::Value *V = Builder.CreateLoad(Addr, "fake.use");
+ llvm::CallInst *C = Builder.CreateCall(CGM.getLLVMFakeUseFn(), {V});
+ C->setDoesNotThrow();
+ C->setTailCallKind(llvm::CallInst::TCK_NoTail);
+}
+
void CodeGenFunction::EmitAndRegisterVariableArrayDimensions(
CGDebugInfo *DI, const VarDecl &D, bool EmitDebugInfo) {
// For each dimension stores its QualType and corresponding
@@ -1412,6 +1420,39 @@ void CodeGenFunction::EmitAndRegisterVariableArrayDimensions(
}
}
+/// Return the maximum size of an aggregate for which we generate a fake use
+/// intrinsic when -fextend-lifetimes is in effect.
+static uint64_t maxFakeUseAggregateSize(const ASTContext &C) {
+ return 4 * C.getTypeSize(C.UnsignedIntTy);
+}
+
+// Helper function to determine whether a variable's or parameter's lifetime
+// should be extended.
+static bool extendLifetime(const ASTContext &Context, const Decl *FuncDecl,
+ const VarDecl &D,
+ ImplicitParamDecl *CXXABIThisDecl) {
+ // When we're not inside a valid function it is unlikely that any
+ // lifetime extension is useful.
+ if (!FuncDecl)
+ return false;
+ if (FuncDecl->isImplicit())
+ return false;
+ // Do not extend compiler-created variables except for the this pointer.
+ if (D.isImplicit() && &D != CXXABIThisDecl)
+ return false;
+ QualType Ty = D.getType();
+ // No need to extend volatiles, they have a memory location.
+ if (Ty.isVolatileQualified())
+ return false;
+ // Don't extend variables that exceed a certain size.
+ if (Context.getTypeSize(Ty) > maxFakeUseAggregateSize(Context))
+ return false;
+ // Do not extend variables in nodebug functions.
+ if (FuncDecl->hasAttr<NoDebugAttr>())
+ return false;
+ return true;
+}
+
/// EmitAutoVarAlloca - Emit the alloca and debug information for a
/// local variable. Does not emit initialization or destruction.
CodeGenFunction::AutoVarEmission
@@ -1664,6 +1705,17 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
emission.getOriginalAllocatedAddress(),
emission.getSizeForLifetimeMarkers());
+ // Analogous to lifetime markers, we use a 'cleanup' to emit fake.use
+ // calls for local variables. We are exempting volatile variables and
+ // non-scalars larger than 4 times the size of an unsigned int (32 bytes).
+ // Larger non-scalars are often allocated in memory and may create unnecessary
+ // overhead.
+ if (CGM.getCodeGenOpts().ExtendLifetimes) {
+ if (extendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl))
+ EHStack.pushCleanup<FakeUse>(NormalFakeUse,
+ emission.getAllocatedAddress());
+ }
+
return emission;
}
@@ -2523,6 +2575,15 @@ llvm::Function *CodeGenModule::getLLVMLifetimeEndFn() {
return LifetimeEndFn;
}
+/// Lazily declare the @llvm.fake.use intrinsic.
+llvm::Function *CodeGenModule::getLLVMFakeUseFn() {
+ if (FakeUseFn)
+ return FakeUseFn;
+ FakeUseFn =
+ llvm::Intrinsic::getDeclaration(&getModule(), llvm::Intrinsic::fake_use);
+ return FakeUseFn;
+}
+
namespace {
/// A cleanup to perform a release of an object at the end of a
/// function. This is used to balance out the incoming +1 of a
@@ -2716,6 +2777,15 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg,
setAddrOfLocalVar(&D, DeclPtr);
+ // Push a FakeUse 'cleanup' object onto the EHStack for the parameter,
+ // which may be the 'this' pointer. This causes the emission of a fake.use
+ // call with the parameter as argument at the end of the function.
+ if (CGM.getCodeGenOpts().ExtendLifetimes ||
+ (CGM.getCodeGenOpts().ExtendThisPtr && &D == CXXABIThisDecl)) {
+ if (extendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl))
+ EHStack.pushCleanup<FakeUse>(NormalFakeUse, DeclPtr);
+ }
+
// Emit debug info for param declarations in non-thunk functions.
if (CGDebugInfo *DI = getDebugInfo()) {
if (CGM.getCodeGenOpts().hasReducedDebugInfo() && !CurFuncIsThunk &&
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index a5747283e98058..4769c0ab22af6d 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -403,9 +403,9 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) {
// important to do this before we enter the return block or return
// edges will be *really* confused.
bool HasCleanups = EHStack.stable_begin() != PrologueCleanupDepth;
- bool HasOnlyLifetimeMarkers =
- HasCleanups && EHStack.containsOnlyLifetimeMarkers(PrologueCleanupDepth);
- bool EmitRetDbgLoc = !HasCleanups || HasOnlyLifetimeMarkers;
+ bool HasOnlyNoopCleanups =
+ HasCleanups && EHStack.containsOnlyNoopCleanups(PrologueCleanupDepth);
+ bool EmitRetDbgLoc = !HasCleanups || HasOnlyNoopCleanups;
std::optional<ApplyDebugLocation> OAL;
if (HasCleanups) {
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 05f85f8b95bfa2..8bcd2027a39172 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -718,6 +718,20 @@ class CodeGenFunction : public CodeGenTypeCache {
}
};
+ // We are using objects of this 'cleanup' class to emit fake.use calls
+ // for -fextend-lifetimes and -fextend-this-ptr. They are placed at the end of
+ // a variable's scope analogous to lifetime markers.
+ class FakeUse final : public EHScopeStack::Cleanup {
+ Address Addr;
+
+ public:
+ FakeUse(Address addr) : Addr(addr) {}
+
+ void Emit(CodeGenFunction &CGF, Flags flags) override {
+ CGF.EmitFakeUse(Addr);
+ }
+ };
+
/// Header for data within LifetimeExtendedCleanupStack.
struct LifetimeExtendedCleanupHeader {
/// The size of the following cleanup object.
@@ -4966,6 +4980,8 @@ class CodeGenFunction : public CodeGenTypeCache {
RValue EmitAtomicExpr(AtomicExpr *E);
+ void EmitFakeUse(Address Addr);
+
//===--------------------------------------------------------------------===//
// Annotations Emission
//===--------------------------------------------------------------------===//
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index c58bb88035ca8a..77d83b1ba7d216 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -601,6 +601,9 @@ class CodeGenModule : public CodeGenTypeCache {
/// void @llvm.lifetime.end(i64 %size, i8* nocapture <ptr>)
llvm::Function *LifetimeEndFn = nullptr;
+ /// void @llvm.fake.use(i8* nocapture <ptr>)
+ llvm::Function *FakeUseFn = nullptr;
+
std::unique_ptr<SanitizerMetadata> SanitizerMD;
llvm::MapVector<const Decl *, bool> DeferredEmptyCoverageMappingDecls;
@@ -1268,6 +1271,7 @@ class CodeGenModule : public CodeGenTypeCache {
llvm::Function *getLLVMLifetimeStartFn();
llvm::Function *getLLVMLifetimeEndFn();
+ llvm::Function *getLLVMFakeUseFn();
// Make sure that this type is translated.
void UpdateCompletedType(const TagDecl *TD);
diff --git a/clang/lib/CodeGen/EHScopeStack.h b/clang/lib/CodeGen/EHScopeStack.h
index 0c667e80bb6d8c..ed11dc2bb05d73 100644
--- a/clang/lib/CodeGen/EHScopeStack.h
+++ b/clang/lib/CodeGen/EHScopeStack.h
@@ -87,6 +87,11 @@ enum CleanupKind : unsigned {
LifetimeMarker = 0x8,
NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup,
+
+ // FakeUse needs to be recognized as a special cleanup similar to lifetime
+ // markers chiefly to be ignored in most contexts.
+ FakeUse = 0x10,
+ NormalFakeUse = FakeUse | NormalCleanup,
};
/// A stack of scopes which respond to exceptions, including cleanups
@@ -352,8 +357,8 @@ class EHScopeStack {
void popTerminate();
// Returns true iff the current scope is either empty or contains only
- // lifetime markers, i.e. no real cleanup code
- bool containsOnlyLifetimeMarkers(stable_iterator Old) const;
+ // noop cleanups, i.e. lifetime markers and fake uses.
+ bool containsOnlyNoopCleanups(stable_iterator Old) const;
/// Determines whether the exception-scopes stack is empty.
bool empty() const { return StartOfData == EndOfBuffer; }
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index df86941950e46e..e5020271ba95dc 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7581,6 +7581,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (Args.hasArg(options::OPT_fretain_comments_from_system_headers))
CmdArgs.push_back("-fretain-comments-from-system-headers");
+ if (Args.hasArg(options::OPT_fextend_this_ptr))
+ CmdArgs.push_back("-fextend-this-ptr");
+ if (Args.hasArg(options::OPT_fextend_lifetimes))
+ CmdArgs.push_back("-fextend-lifetimes");
+
// Forward -fcomment-block-commands to -cc1.
Args.AddAllArgs(CmdArgs, options::OPT_fcomment_block_commands);
// Forward -fparse-all-comments to -cc1.
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 32628c5e84332d..c2925ce461dc8d 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2219,6 +2219,11 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
Args.getAllArgValues(OPT_fsanitize_trap_EQ), Diags,
Opts.SanitizeTrap);
+ Opts.ExtendThisPtr =
+ Opts.OptimizationLevel > 0 && Args.hasArg(OPT_fextend_this_ptr);
+ Opts.ExtendLifetimes =
+ Opts.OptimizationLevel > 0 && Args.hasArg(OPT_fextend_lifetimes);
+
Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
if (!LangOpts->CUDAIsDevice)
diff --git a/clang/test/CodeGen/extend-lifetimes-optdebug.c b/clang/test/CodeGen/extend-lifetimes-optdebug.c
new file mode 100644
index 00000000000000..74da738d3ed6fb
--- /dev/null
+++ b/clang/test/CodeGen/extend-lifetimes-optdebug.c
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 %s -emit-llvm -O2 -fextend-lifetimes -o - | FileCheck %s
+
+// Emit the function attribute disable-post-ra when
+// -fextend-lifetimes is on.
+
+// CHECK: attributes #0 = {{{.*}}optdebug
+
+void foo() {}
diff --git a/clang/test/CodeGen/extend-liveness1.c b/clang/test/CodeGen/extend-liveness1.c
new file mode 100644
index 00000000000000..ef2d00eb6be312
--- /dev/null
+++ b/clang/test/CodeGen/extend-liveness1.c
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
+// Check that fake use calls are emitted at the correct locations, i.e.
+// at the end of lexical blocks and at the end of the function.
+
+extern int use(int);
+int glob1;
+int glob2;
+float globf;
+
+int foo(int i) {
+ // CHECK: define{{.*}}foo
+ if (i < 4) {
+ int j = i * 3;
+ if (glob1 > 3) {
+ float f = globf;
+ // CHECK: [[SSAVAL:%[a-z0-9]*]] = load float{{.*}}globf
+ j = f;
+ glob2 = j;
+ // CHECK: store{{.*}}glob2
+ // CHECK-NEXT: call void (...) @llvm.fake.use(float [[SSAVAL]])
+ }
+ glob1 = j;
+ // CHECK: store{{.*}}glob1
+ // CHECK-NEXT: call void (...) @llvm.fake.use(i32 %j.
+ }
+ // CHECK: call void (...) @llvm.fake.use(i32 %i)
+ // CHECK-NEXT: ret
+ return 4;
+}
diff --git a/clang/test/CodeGen/extend-liveness2.cpp b/clang/test/CodeGen/extend-liveness2.cpp
new file mode 100644
index 00000000000000..119c783c634806
--- /dev/null
+++ b/clang/test/CodeGen/extend-liveness2.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -fcxx-exceptions -fexceptions -o - | FileCheck %s
+// REQUIRES: x86-registered-target
+// This test checks that the fake_use concept works with exception handling and that we
+// can handle the __int128 data type.
+
+class A {
+public:
+ A(int i) : m_i(i) {}
+ void func(__int128 i128);
+
+ int m_i;
+};
+
+extern int bar();
+extern void foo();
+int glob;
+
+void A::func(__int128 i128) {
+ int j = 4;
+ try {
+ int k = bar();
+ foo();
+ // CHECK: [[SSAVAL:%[a-z0-9]*]] = invoke{{.*}}bar
+ glob = 0;
+ // CHECK: store{{.*}}glob
+ // CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[SSAVAL]])
+ } catch (...) {
+ foo();
+ }
+ // CHECK-LABEL: try.cont:
+ // CHECK-DAG: call void (...) @llvm.fake.use({{.*%this}})
+ // CHECK-DAG: call void (...) @llvm.fake.use(i128 %i128.sroa.0.0.insert.insert)
+ // CHECK: ret void
+}
diff --git a/clang/test/CodeGen/fake-use-determinism.c b/clang/test/CodeGen/fake-use-determinism.c
new file mode 100644
index 00000000000000..d62efbb4efe7ee
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-determinism.c
@@ -0,0 +1,18 @@
+// RUN: %clang -S -O2 -emit-llvm -fextend-lifetimes %s -o - | FileCheck %s
+// REQUIRES: asserts
+//
+// We are checking that the fake.use calls for i, j and k appear
+// in a particular order. It is not the order itself that is important
+// but that it remains the same between different test runs.
+
+// CHECK: call {{.*}}void (...) @llvm.fake.use(i32 %k)
+// CHECK-NEXT: call {{.*}}void (...) @llvm.fake.use(i32 %j)
+// CHECK-NEXT: call {{.*}}void (...) @llvm.fake.use(i32 %i)
+
+extern void bar();
+void foo(int i, int j, int k)
+{
+ for (int l = 0; l < i; l++) {
+ bar();
+ }
+}
diff --git a/clang/test/CodeGen/fake-use-lambda.cpp b/clang/test/CodeGen/fake-use-lambda.cpp
new file mode 100644
index 00000000000000..fd4881b9bcd531
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-lambda.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 %s -triple=%itanium_abi_triple -O1 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
+// Make sure we don't crash compiling a lambda that is not nested in a function.
+// We also check that fake uses are properly issued in lambdas.
+
+int glob;
+
+extern int foo();
+
+struct S {
+ static const int a;
+};
+
+const int S::a = [](int b) __attribute__((noinline)) {
+ return b * foo();
+}
+(glob);
+
+int func(int param) {
+ return ([=](int lambdaparm) __attribute__((noinline))->int {
+ int lambdalocal = lambdaparm * 2;
+ return lambdalocal;
+ }(glob));
+}
+
+// We are looking for the first lambda's call operator, which should contain
+// 2 fake uses, one for 'b' and one for its 'this' pointer (in that order).
+// The mangled function name contains a $_0, followed by 'cl'.
+// This lambda is an orphaned lambda, i.e. one without lexical parent.
+//
+// CHECK: define internal {{.+\"_Z.+\$_0.*cl.*\"}}
+// CHECK-NOT: ret
+// CHECK: fake.use(i32
+// CHECK-NOT: ret
+// CHECK: fake.use(ptr
+
+// The second lambda. We are looking for 3 fake uses.
+// CHECK: define internal {{.+\"_Z.+\$_0.*cl.*\"}}
+// CHECK-NOT: ret
+// CHECK: fake.use(i32
+// CHECK-NOT: ret
+// CHECK: fake.use(i32
+// CHECK-NOT: ret
+// CHECK: fake.use(ptr
diff --git a/clang/test/CodeGen/fake-use-landingpad.c b/clang/test/CodeGen/fake-use-landingpad.c
new file mode 100644
index 00000000000000..7f49aab7d18656
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-landingpad.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 %s -O3 -emit-llvm -fextend-lifetimes -fexceptions -o - | FileCheck %s
+
+// Check that fake uses do not mistakenly cause a landing pad to be generated when
+// exceptions are enabled.
+
+extern void bar(int);
+void foo(int p) {
+ int a = 17;
+ bar(a);
+}
+
+// CHECK: define {{.*}} @foo
+// CHECK-NOT: personality
+// CHECK: entry:
+// CHECK-NOT: landingpad
diff --git a/clang/test/CodeGen/fake-use-noreturn.c b/clang/test/CodeGen/fake-use-noreturn.c
new file mode 100644
index 00000000000000..31166d26e5ee06
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-noreturn.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 %s -emit-llvm -fextend-lifetimes -o %t.ll
+//
+// Check we don't assert when we have a return in a nested conditional and
+// there is no code at the end of the function.
+
+// CHECK: define{{.*}}main
+// CHECK: call{{.*}}llvm.fake.use
+
+void foo(int i) {
+ while (0)
+ if (1)
+ return;
+}
diff --git a/clang/test/CodeGen/fake-use-return-line.c b/clang/test/CodeGen/fake-use-return-line.c
new file mode 100644
index 00000000000000..b14e0fa405dbdb
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-return-line.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -emit-llvm -O2 -debug-info-kind=limited -fextend-lifetimes -o - %s | FileCheck %s
+int main()
+{
+ volatile int a = 1;
+ int b = a + 2;
+ return b;
+}
+// CHECK: define{{.*}}@main
+// CHECK: ret i32{{.*}}!dbg ![[MDINDEX:[0-9]*]]
+// CHECK: ![[MDINDEX]] = !DILocation(line: 6
diff --git a/clang/test/CodeGen/fake-use-sanitizer.cpp b/clang/test/CodeGen/fake-use-sanitizer.cpp
new file mode 100644
index 00000000000000..915924a2b31025
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-sanitizer.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-lifetimes -fsanitize=null -fsanitize-trap=null -o - | FileCheck --check-prefix TRAP %s
+// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-lifetimes -o - | FileCheck --check-prefix FAKEUSE %s
+
+// With -fextend-lifetimes the compiler generated a fake.use of a
+// reference variable at the end of the function, in the cleanup block. This prompted the
+// address sanitizer to verify that the variable has been allocated properly, even when
+// the function returns early.
+// We check that sanitizers are disabled for code generated for the benefit of fake.use
+// intrinsics, as well as that the fake.use is no longer generated in the cleanup block.
+// It should be generated in the block preceding the cleanup block instead.
+
+struct A { short s1, s2; };
+extern A& getA();
+
+void foo()
+{
+ auto& va = getA();
+ short s = va.s1 & ~va.s2;
+ if (s == 0)
+ return;
+
+ auto& vb = getA();
+}
+
+// TRAP: define{{.*}}foo
+// TRAP: [[COMPARE:%[^\s]*]] = icmp eq
+// TRAP-NOT: br i1 [[COMPARE]]{{.*}}trap
+// TRAP: br i1 [[COMPARE]]{{.*}}%if.end
+// TRAP-NOT: trap:
+// TRAP: if.end:
+// TRAP-NOT: call{{.*}}llvm.trap
+
+// FAKEUSE: if.end:
+// FAKEUSE-NEXT: [[CALL:%[^\s]*]] = {{.*}}call{{.*}}getA
+// FAKEUSE-NOT: br{{.*}}cleanup
+// FAKEUSE: call{{.*}}fake.use({{.*}}[[CALL]])
+// FAKEUSE: cleanup:
diff --git a/clang/test/CodeGen/fake-use-scalar.c b/clang/test/CodeGen/fake-use-scalar.c
new file mode 100644
index 00000000000000..e42216c4a7feda
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-scalar.c
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
+// Make sure we don't generate fake.use for non-scalar variables.
+// Make sure we don't generate fake.use for volatile variables
+// and parameters even when they are scalar.
+
+struct A {
+ unsigned long t;
+ char c[1024];
+ unsigned char r[32];
+};
+
+
+int foo(volatile int param)
+{
+ struct A s;
+ volatile int vloc;
+ struct A v[128];
+ char c[33];
+ return 0;
+}
+
+// CHECK-NOT: fake.use
diff --git a/clang/test/CodeGen/fake-use-small-aggs.c b/clang/test/CodeGen/fake-use-small-aggs.c
new file mode 100644
index 00000000000000..236e3a08c533ef
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-small-aggs.c
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -disable-llvm-passes -o - | FileCheck %s
+// Check that we generate a fake_use call for small aggregate types.
+
+// CHECK-DAG: %[[FAKEUSE1:[^ ]+]] = load{{.*}} %loc,
+// CHECK-DAG: call{{.*}}llvm.fake.use({{.*}}%[[FAKEUSE1]])
+// CHECK-DAG: %[[FAKEUSE2:[^ ]+]] = load{{.*}} %arr,
+// CHECK-DAG: call{{.*}}llvm.fake.use({{.*}}%[[FAKEUSE2]])
+// CHECK-DAG: %[[FAKEUSE3:[^ ]+]] = load{{.*}} %S,
+// CHECK-DAG: call{{.*}}llvm.fake.use({{.*}}%[[FAKEUSE3]])
+
+struct s {
+ int i;
+ int j;
+};
+
+extern void inita(int *);
+extern struct s inits();
+void foo(struct s S)
+{
+ int arr[4];
+ inita (arr);
+ struct s loc = inits();
+}
+
diff --git a/clang/test/CodeGen/fake-use-while.c b/clang/test/CodeGen/fake-use-while.c
new file mode 100644
index 00000000000000..6055a73636f6ce
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-while.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
+//
+// Check we don't assert when there is no more code after a while statement
+// and the body of the while statement ends in a return, i.e. no insertion point
+// is available.
+
+// CHECK: define{{.*}}foo
+// CHECK: call{{.*}}llvm.fake.use
+
+void foo() {
+ {
+ while (1) {
+ int ret;
+ if (1)
+ return;
+ }
+ }
+}
diff --git a/clang/test/CodeGen/fake-use.cpp b/clang/test/CodeGen/fake-use.cpp
new file mode 100644
index 00000000000000..b58ba3ea60c4a2
--- /dev/null
+++ b/clang/test/CodeGen/fake-use.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-this-ptr -o - | FileCheck %s
+// Check that we generate a fake_use call with the 'this' pointer as argument.
+// The call should appear after the call to bar().
+
+extern void bar();
+
+class v
+{
+public:
+ int x;
+ int y;
+ int z;
+ int w;
+
+ v(int a, int b, int c, int d) : x(a), y(b), z(c), w(d) {}
+};
+
+class w
+{
+public:
+ v test(int, int, int, int, int, int, int, int, int, int);
+ w(int in): a(in), b(1234) {}
+
+private:
+ int a;
+ int b;
+};
+
+v w::test(int q, int w, int e, int r, int t, int y, int u, int i, int o, int p)
+{
+// CHECK: define{{.*}}test
+ int res = q*w + e - r*t + y*u*i*o*p;
+ int res2 = (w + e + r + t + y + o)*(p*q);
+ int res3 = res + res2;
+ int res4 = q*e + t*y*i + p*e*w * 6 * 4 * 3;
+
+ v V(res, res2, res3, res4);
+
+ bar();
+// CHECK: call{{.*}}bar
+// CHECK: call void (...) @llvm.fake.use(ptr nonnull %this)
+ return V;
+// CHECK: ret
+}
diff --git a/clang/test/CodeGen/no-fake-use-O0.cpp b/clang/test/CodeGen/no-fake-use-O0.cpp
new file mode 100644
index 00000000000000..67b9fac9b1bffe
--- /dev/null
+++ b/clang/test/CodeGen/no-fake-use-O0.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 %s -O0 -emit-llvm -fextend-this-ptr -o - | FileCheck %s \
+// RUN: --implicit-check-not="call void (...) @llvm.fake.use"
+// RUN: %clang_cc1 %s -O0 -emit-llvm -fextend-lifetimes -o - | FileCheck %s \
+// RUN: --implicit-check-not="call void (...) @llvm.fake.use"
+// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-this-ptr -o - | FileCheck -check-prefix=OPT %s
+// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-lifetimes -o - | FileCheck -check-prefix=OPT %s
+// RUN: %clang_cc1 %s -Os -emit-llvm -fextend-this-ptr -o - | FileCheck -check-prefix=OPT %s
+// RUN: %clang_cc1 %s -Os -emit-llvm -fextend-lifetimes -o - | FileCheck -check-prefix=OPT %s
+// RUN: %clang_cc1 %s -Oz -emit-llvm -fextend-this-ptr -o - | FileCheck -check-prefix=OPT %s
+// RUN: %clang_cc1 %s -Oz -emit-llvm -fextend-lifetimes -o - | FileCheck -check-prefix=OPT %s
+// Check that we do not generate a fake_use call when we are not optimizing.
+
+extern void bar();
+
+class v
+{
+public:
+ int x;
+ int y;
+ int z;
+ int w;
+
+ v(int a, int b, int c, int d) : x(a), y(b), z(c), w(d) {}
+};
+
+class w
+{
+public:
+ v test(int, int, int, int, int, int, int, int, int, int);
+ w(int in): a(in), b(1234) {}
+
+private:
+ int a;
+ int b;
+};
+
+v w::test(int q, int w, int e, int r, int t, int y, int u, int i, int o, int p)
+{
+// CHECK: define{{.*}}test
+ int res = q*w + e - r*t + y*u*i*o*p;
+ int res2 = (w + e + r + t + y + o)*(p*q);
+ int res3 = res + res2;
+ int res4 = q*e + t*y*i + p*e*w * 6 * 4 * 3;
+
+ v V(res, res2, res3, res4);
+
+ bar();
+// OPT: call void (...) @llvm.fake.use
+ return V;
+}
More information about the cfe-commits
mailing list