[clang] [Clang] Add fake use emission to Clang with -fextend-lifetimes (PR #110102)
Stephen Tozer via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 27 06:49:25 PST 2024
https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/110102
>From 7d8646edb373c38ee761de2c338c45b967a423e7 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Wed, 25 Sep 2024 16:55:39 +0100
Subject: [PATCH 1/2] [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.
---
clang/lib/CodeGen/CGCall.cpp | 17 +++++-
clang/lib/CodeGen/CGCleanup.cpp | 7 ++-
clang/lib/CodeGen/CGCleanup.h | 8 +++
clang/lib/CodeGen/CGDecl.cpp | 69 +++++++++++++++++++++++
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/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 | 16 ++++++
clang/test/CodeGen/fake-use-noreturn.cpp | 27 +++++++++
clang/test/CodeGen/fake-use-return-line.c | 15 +++++
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 ++++++++++++++++
21 files changed, 503 insertions(+), 10 deletions(-)
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.cpp
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/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 4ae981e4013e9c..26441b4135529d 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -3566,15 +3566,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 that 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..ba78e5478ac373 100644
--- a/clang/lib/CodeGen/CGCleanup.h
+++ b/clang/lib/CodeGen/CGCleanup.h
@@ -87,6 +87,10 @@ class EHScope {
LLVM_PREFERRED_TYPE(bool)
unsigned IsLifetimeMarker : 1;
+ /// Whether this cleanup is a fake use
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned IsFakeUse : 1;
+
/// Whether the normal cleanup should test the activation flag.
LLVM_PREFERRED_TYPE(bool)
unsigned TestFlagInNormalCleanup : 1;
@@ -352,6 +356,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 +389,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..7fdf18f3ca2e7b 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,14 @@ llvm::Function *CodeGenModule::getLLVMLifetimeEndFn() {
return LifetimeEndFn;
}
+/// Lazily declare the @llvm.fake.use intrinsic.
+llvm::Function *CodeGenModule::getLLVMFakeUseFn() {
+ if (!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 +2776,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 eda96f3e352ce3..630a9c8db95fe5 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 3e2abbd9bc1094..1525eb0f09b6e5 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.
@@ -4975,6 +4989,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..71198134f17413 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(...)
+ 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/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..91526f29936146
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-landingpad.c
@@ -0,0 +1,16 @@
+// 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: llvm.fake.use
+// CHECK-NOT: landingpad
diff --git a/clang/test/CodeGen/fake-use-noreturn.cpp b/clang/test/CodeGen/fake-use-noreturn.cpp
new file mode 100644
index 00000000000000..acfcb0ce90b5e4
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-noreturn.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang %s -S -emit-llvm -fextend-lifetimes -O2 -o - -fno-discard-value-names | FileCheck %s
+//
+// Check we can correctly produce fake uses for function-level variables even
+// when we have a return in a nested conditional and there is no code at the end
+// of the function.
+
+// CHECK-LABEL: define{{.*}}@_Z3fooi
+// CHECK: call{{.*}}llvm.fake.use(i32 %i)
+// CHECK-LABEL: define{{.*}}@_ZN1C3barEi
+// CHECK-DAG: call{{.*}}llvm.fake.use(i32 %i)
+// CHECK-DAG: call{{.*}}llvm.fake.use({{.*}}%this)
+
+void foo(int i) {
+ while (0)
+ if (1)
+ return;
+}
+
+class C {
+ void bar(int i);
+};
+
+void C::bar(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..0c4377058d6628
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-return-line.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -emit-llvm -O2 -debug-info-kind=limited -fextend-lifetimes -o - %s | FileCheck %s
+
+// Clang adjusts the line numbers of returns based on the line numbers of
+// dominating stores to %retval; we test that fake use intrinsics do not affect
+// this, and the return is given the correct line.
+
+// CHECK: define{{.*}}@main
+// CHECK: ret i32{{.*}}!dbg ![[MDINDEX:[0-9]*]]
+// CHECK: ![[MDINDEX]] = !DILocation(line: [[# @LINE + 5]]
+int main()
+{
+ volatile int a = 1;
+ int b = a + 2;
+ return b;
+}
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;
+}
>From 7ce4159da0a6bd1fb305012cd11361df65c984c4 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Wed, 27 Nov 2024 14:47:40 +0000
Subject: [PATCH 2/2] Update all tests to not use LLVM opts, and generally fix
them up; allow fake use emission with disable-O0-optnone
---
clang/lib/CodeGen/CGDecl.cpp | 9 ++-
clang/test/CodeGen/extend-liveness-except.cpp | 34 +++++++++
.../CodeGen/extend-liveness-wide-scalar.cpp | 11 +++
clang/test/CodeGen/extend-liveness1.c | 34 ++++-----
clang/test/CodeGen/extend-liveness2.cpp | 34 ---------
clang/test/CodeGen/fake-use-determinism.c | 14 ++--
clang/test/CodeGen/fake-use-lambda.cpp | 24 +++---
clang/test/CodeGen/fake-use-landingpad.c | 6 +-
clang/test/CodeGen/fake-use-noreturn.cpp | 13 ++--
clang/test/CodeGen/fake-use-return-line.c | 5 +-
clang/test/CodeGen/fake-use-sanitizer.cpp | 74 ++++++++++++-------
clang/test/CodeGen/fake-use-scalar.c | 37 +++++++---
clang/test/CodeGen/fake-use-small-aggs.c | 24 ------
clang/test/CodeGen/fake-use-this.cpp | 34 +++++++++
clang/test/CodeGen/fake-use-while.c | 2 +-
clang/test/CodeGen/fake-use.cpp | 44 -----------
16 files changed, 213 insertions(+), 186 deletions(-)
create mode 100644 clang/test/CodeGen/extend-liveness-except.cpp
create mode 100644 clang/test/CodeGen/extend-liveness-wide-scalar.cpp
delete mode 100644 clang/test/CodeGen/extend-liveness2.cpp
delete mode 100644 clang/test/CodeGen/fake-use-small-aggs.c
create mode 100644 clang/test/CodeGen/fake-use-this.cpp
delete mode 100644 clang/test/CodeGen/fake-use.cpp
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 7fdf18f3ca2e7b..f2eccc0494826a 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1354,6 +1354,11 @@ void CodeGenFunction::EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr) {
}
void CodeGenFunction::EmitFakeUse(Address Addr) {
+ // We do not emit a fake use if we want to apply optnone to this function,
+ // even if we might not apply it anyway due to minsize or similar attributes.
+ if (!CGM.getCodeGenOpts().DisableO0ImplyOptNone &&
+ CGM.getCodeGenOpts().OptimizationLevel == 0)
+ return;
auto NL = ApplyDebugLocation::CreateEmpty(*this);
llvm::Value *V = Builder.CreateLoad(Addr, "fake.use");
llvm::CallInst *C = Builder.CreateCall(CGM.getLLVMFakeUseFn(), {V});
@@ -1447,8 +1452,8 @@ static bool extendLifetime(const ASTContext &Context, const Decl *FuncDecl,
// 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>())
+ // Do not extend variables in nodebug or optnone functions.
+ if (FuncDecl->hasAttr<NoDebugAttr>() || FuncDecl->hasAttr<OptimizeNoneAttr>())
return false;
return true;
}
diff --git a/clang/test/CodeGen/extend-liveness-except.cpp b/clang/test/CodeGen/extend-liveness-except.cpp
new file mode 100644
index 00000000000000..5477ec527413e1
--- /dev/null
+++ b/clang/test/CodeGen/extend-liveness-except.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 %s -O0 -disable-O0-optnone -emit-llvm -fextend-lifetimes -fcxx-exceptions -fexceptions -o - | FileCheck %s
+// This test checks that the fake uses can be generated in exception handling
+// blocks and that we can emit fake uses for the __int128 data type.
+
+extern int bar();
+
+/// Try block: fake use ends at try-block scope.
+// [[BAR_VAL::%[a-zA-Z0-9\.]+]] = invoke{{.*}} i32 @_Z3barv()
+// store i32 %[[BAR_VAL]], ptr [[K_ALLOC_VAL:%[a-zA-Z0-9\.]+]], align 4
+// [[K_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr [[K_ALLOC_VAL]], align 4
+// call void (...) @llvm.fake.use(i32 [[K_FAKE_USE]]) #2
+// br label
+
+/// Catch block: fetching the caught value...
+// CHECK: [[CATCH_PTR:%[a-zA-Z0-9\.]+]] = call ptr @__cxa_begin_catch(
+// CHECK: [[L_VAL:%[a-zA-Z0-9\.]+]] = load i32, ptr [[CATCH_PTR]], align 4
+
+/// Storing to allocas...
+// CHECK-DAG: store i32 8, ptr [[M_ALLOC_VAL:%[a-zA-Z0-9\.]+]]
+// CHECK-DAG: store i32 [[L_VAL]], ptr [[L_ALLOC_VAL:%[a-zA-Z0-9\.]+]], align 4
+
+/// Load into fake uses - expect M to precede L.
+// CHECK: [[M_FAKE_VAL:%[a-zA-Z0-9\.]+]] = load i32, ptr [[M_ALLOC_VAL]]
+// CHECK: call void (...) @llvm.fake.use(i32 [[M_FAKE_VAL]])
+// CHECK: [[L_FAKE_VAL:%[a-zA-Z0-9\.]+]] = load i32, ptr [[L_ALLOC_VAL]]
+// CHECK: call void (...) @llvm.fake.use(i32 [[L_FAKE_VAL]])
+void foo() {
+ try {
+ int k = bar();
+ } catch (int l) {
+ /// The catch block contains a fake use for the local within its scope.
+ int m = 8;
+ }
+}
diff --git a/clang/test/CodeGen/extend-liveness-wide-scalar.cpp b/clang/test/CodeGen/extend-liveness-wide-scalar.cpp
new file mode 100644
index 00000000000000..bdaa5b8f2e34eb
--- /dev/null
+++ b/clang/test/CodeGen/extend-liveness-wide-scalar.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 %s -disable-O0-optnone -emit-llvm -fextend-lifetimes -triple x86_64-unknown-linux -o - | FileCheck %s
+// REQUIRES: x86-registered-target
+// This test checks that the fake uses can be generated in exception handling
+// blocks and that we can emit fake uses for the __int128 data type.
+
+void bar();
+
+// CHECK: call void (...) @llvm.fake.use(i128 %
+void foo(__int128 wide_int) {
+ bar();
+}
diff --git a/clang/test/CodeGen/extend-liveness1.c b/clang/test/CodeGen/extend-liveness1.c
index ef2d00eb6be312..4b9b73438a1d13 100644
--- a/clang/test/CodeGen/extend-liveness1.c
+++ b/clang/test/CodeGen/extend-liveness1.c
@@ -1,29 +1,29 @@
-// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
+// RUN: %clang_cc1 %s -O0 -disable-O0-optnone -emit-llvm -fextend-lifetimes -o - | FileCheck %s --implicit-check-not=llvm.fake.use
// 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 glob_i;
+char glob_c;
+float glob_f;
int foo(int i) {
- // CHECK: define{{.*}}foo
+ // CHECK-LABEL: define{{.*}}foo
if (i < 4) {
- int j = i * 3;
- if (glob1 > 3) {
- float f = globf;
- // CHECK: [[SSAVAL:%[a-z0-9]*]] = load float{{.*}}globf
+ char j = i * 3;
+ if (glob_i > 3) {
+ float f = glob_f;
j = f;
- glob2 = j;
- // CHECK: store{{.*}}glob2
- // CHECK-NEXT: call void (...) @llvm.fake.use(float [[SSAVAL]])
+ glob_c = j;
+ // CHECK: call void (...) @llvm.fake.use(float %
+ // CHECK-NEXT: br label %
}
- glob1 = j;
- // CHECK: store{{.*}}glob1
- // CHECK-NEXT: call void (...) @llvm.fake.use(i32 %j.
+ glob_i = j;
+ // CHECK: call void (...) @llvm.fake.use(i8 %
+ // CHECK-NEXT: br label %
}
- // CHECK: call void (...) @llvm.fake.use(i32 %i)
+ // CHECK: call void (...) @llvm.fake.use(i32 %
// CHECK-NEXT: ret
return 4;
}
+
+// CHECK: declare void @llvm.fake.use(...)
diff --git a/clang/test/CodeGen/extend-liveness2.cpp b/clang/test/CodeGen/extend-liveness2.cpp
deleted file mode 100644
index 119c783c634806..00000000000000
--- a/clang/test/CodeGen/extend-liveness2.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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
index d62efbb4efe7ee..7eca2bb3d175b2 100644
--- a/clang/test/CodeGen/fake-use-determinism.c
+++ b/clang/test/CodeGen/fake-use-determinism.c
@@ -1,15 +1,17 @@
-// RUN: %clang -S -O2 -emit-llvm -fextend-lifetimes %s -o - | FileCheck %s
-// REQUIRES: asserts
+// RUN: %clang_cc1 -O0 -disable-O0-optnone -emit-llvm -fextend-lifetimes %s -o - | FileCheck %s
//
// 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)
+// CHECK: [[K_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %k.addr
+// CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[K_FAKE_USE]]) #2
+// CHECK-NEXT: [[J_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %j.addr
+// CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[J_FAKE_USE]]) #2
+// CHECK-NEXT: [[I_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %i.addr
+// CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[I_FAKE_USE]]) #2
-extern void bar();
+void bar();
void foo(int i, int j, int k)
{
for (int l = 0; l < i; l++) {
diff --git a/clang/test/CodeGen/fake-use-lambda.cpp b/clang/test/CodeGen/fake-use-lambda.cpp
index fd4881b9bcd531..49e168eefefcbc 100644
--- a/clang/test/CodeGen/fake-use-lambda.cpp
+++ b/clang/test/CodeGen/fake-use-lambda.cpp
@@ -27,17 +27,17 @@ int func(int param) {
// 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
+// CHECK-LABEL: 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
+// CHECK-LABEL: 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
index 91526f29936146..e842983f391f44 100644
--- a/clang/test/CodeGen/fake-use-landingpad.c
+++ b/clang/test/CodeGen/fake-use-landingpad.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -O3 -emit-llvm -fextend-lifetimes -fexceptions -o - | FileCheck %s
+// RUN: %clang_cc1 %s -O0 -disable-O0-optnone -emit-llvm -fextend-lifetimes -fexceptions -o - | FileCheck %s --implicit-check-not=landingpad
// Check that fake uses do not mistakenly cause a landing pad to be generated when
// exceptions are enabled.
@@ -11,6 +11,4 @@ void foo(int p) {
// CHECK: define {{.*}} @foo
// CHECK-NOT: personality
-// CHECK: entry:
-// CHECK: llvm.fake.use
-// CHECK-NOT: landingpad
+// CHECK: call void (...) @llvm.fake.use
diff --git a/clang/test/CodeGen/fake-use-noreturn.cpp b/clang/test/CodeGen/fake-use-noreturn.cpp
index acfcb0ce90b5e4..ee82168eef80d0 100644
--- a/clang/test/CodeGen/fake-use-noreturn.cpp
+++ b/clang/test/CodeGen/fake-use-noreturn.cpp
@@ -1,14 +1,15 @@
-// RUN: %clang %s -S -emit-llvm -fextend-lifetimes -O2 -o - -fno-discard-value-names | FileCheck %s
+// RUN: %clang_cc1 %s -emit-llvm -fextend-lifetimes -O0 -disable-O0-optnone -o - | FileCheck %s
//
// Check we can correctly produce fake uses for function-level variables even
// when we have a return in a nested conditional and there is no code at the end
// of the function.
// CHECK-LABEL: define{{.*}}@_Z3fooi
-// CHECK: call{{.*}}llvm.fake.use(i32 %i)
+// CHECK: [[I_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %i.addr
+// CHECK: call void (...) @llvm.fake.use(i32 [[I_FAKE_USE]])
// CHECK-LABEL: define{{.*}}@_ZN1C3barEi
-// CHECK-DAG: call{{.*}}llvm.fake.use(i32 %i)
-// CHECK-DAG: call{{.*}}llvm.fake.use({{.*}}%this)
+// CHECK: [[J_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %j.addr
+// CHECK: call void (...) @llvm.fake.use(i32 [[J_FAKE_USE]])
void foo(int i) {
while (0)
@@ -17,10 +18,10 @@ void foo(int i) {
}
class C {
- void bar(int i);
+ void bar(int j);
};
-void C::bar(int i) {
+void C::bar(int j) {
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
index 0c4377058d6628..0368bb109a3c42 100644
--- a/clang/test/CodeGen/fake-use-return-line.c
+++ b/clang/test/CodeGen/fake-use-return-line.c
@@ -1,11 +1,12 @@
-// RUN: %clang_cc1 -emit-llvm -O2 -debug-info-kind=limited -fextend-lifetimes -o - %s | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -O0 -disable-O0-optnone -debug-info-kind=limited -fextend-lifetimes -o - %s | FileCheck %s
// Clang adjusts the line numbers of returns based on the line numbers of
// dominating stores to %retval; we test that fake use intrinsics do not affect
// this, and the return is given the correct line.
// CHECK: define{{.*}}@main
-// CHECK: ret i32{{.*}}!dbg ![[MDINDEX:[0-9]*]]
+// CHECK: call void (...) @llvm.fake.use(i32
+// CHECK-NEXT: ret i32{{.*}}!dbg ![[MDINDEX:[0-9]*]]
// CHECK: ![[MDINDEX]] = !DILocation(line: [[# @LINE + 5]]
int main()
{
diff --git a/clang/test/CodeGen/fake-use-sanitizer.cpp b/clang/test/CodeGen/fake-use-sanitizer.cpp
index 915924a2b31025..582c40ada891d7 100644
--- a/clang/test/CodeGen/fake-use-sanitizer.cpp
+++ b/clang/test/CodeGen/fake-use-sanitizer.cpp
@@ -1,37 +1,61 @@
-// 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
+// RUN: %clang_cc1 %s -O0 -disable-O0-optnone -emit-llvm -fextend-lifetimes -fsanitize=null -fsanitize-trap=null -o - | FileCheck --check-prefixes=CHECK,NULL --implicit-check-not=ubsantrap %s
+// RUN: %clang_cc1 %s -O0 -disable-O0-optnone -emit-llvm -fextend-lifetimes -o - | FileCheck %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.
+// With -fextend-lifetimes, the compiler previously generated a fake.use of any
+// reference variable at the end of the scope in which its alloca exists. This
+// caused two issues, where we would get fake uses for uninitialized variables
+// if that variable was declared after an early-return, and UBSan's null checks
+// would complain about this.
+// This test verifies that UBSan does not produce null-checks for arguments to
+// llvm.fake.use, and that fake uses are not emitted for a variable on paths
+// it has not been declared.
struct A { short s1, s2; };
-extern A& getA();
+extern long& getA();
void foo()
{
auto& va = getA();
- short s = va.s1 & ~va.s2;
- if (s == 0)
+ if (va < 5)
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:
+// CHECK-LABEL: define{{.*}}foo
+// CHECK: [[VA_CALL:%.+]] = call{{.*}} ptr @_Z4getAv()
+
+/// We check here for the first UBSan check for "va".
+// NULL: [[VA_ISNULL:%.+]] = icmp ne ptr [[VA_CALL]], null
+// NULL: br i1 [[VA_ISNULL]], label %{{[^,]+}}, label %[[VA_TRAP:[^,]+]]
+// NULL: [[VA_TRAP]]:
+// NULL: call void @llvm.ubsantrap(
+
+// CHECK: [[VA_PTR:%.+]] = load ptr, ptr %va
+// CHECK-NEXT: [[VA_CMP:%.+]] = load i64, ptr [[VA_PTR]]
+// CHECK-NEXT: [[VA_CMP_RES:%.+]] = icmp slt i64 [[VA_CMP]], 5
+// CHECK-NEXT: br i1 [[VA_CMP_RES]], label %[[EARLY_EXIT:[^,]+]], label %[[NOT_EARLY_EXIT:[^,]+]]
+
+// CHECK: [[EARLY_EXIT]]:
+// CHECK: br label %cleanup
+
+/// The fake use for "vb" only appears on the path where its declaration is
+/// reached.
+// CHECK: [[NOT_EARLY_EXIT]]:
+// CHECK: [[VB_CALL:%.+]] = call{{.*}} ptr @_Z4getAv()
+
+/// We check here for the second UBSan check for "vb".
+// NULL: [[VB_ISNULL:%.+]] = icmp ne ptr [[VB_CALL]], null
+// NULL: br i1 [[VB_ISNULL]], label %{{[^,]+}}, label %[[VB_TRAP:[^,]+]]
+// NULL: [[VB_TRAP]]:
+// NULL: call void @llvm.ubsantrap(
+
+// CHECK: [[VB_FAKE_USE:%.+]] = load ptr, ptr %vb
+// CHECK-NEXT: call void (...) @llvm.fake.use(ptr [[VB_FAKE_USE]])
+// CHECK: br label %cleanup
+
+// CHECK: cleanup:
+// CHECK: [[VA_FAKE_USE:%.+]] = load ptr, ptr %va
+// CHECK-NEXT: call void (...) @llvm.fake.use(ptr [[VA_FAKE_USE]])
+
+// NULL: declare void @llvm.ubsantrap
diff --git a/clang/test/CodeGen/fake-use-scalar.c b/clang/test/CodeGen/fake-use-scalar.c
index e42216c4a7feda..fd3acf52641c4d 100644
--- a/clang/test/CodeGen/fake-use-scalar.c
+++ b/clang/test/CodeGen/fake-use-scalar.c
@@ -1,22 +1,41 @@
-// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
-// Make sure we don't generate fake.use for non-scalar variables.
+// RUN: %clang_cc1 %s -O0 -disable-O0-optnone -emit-llvm -fextend-lifetimes -o - | FileCheck %s --implicit-check-not=fake.use
+// Make sure we don't generate fake.use for non-scalar variables, unless they
+// are small enough that they may be represented as a scalar in LLVM IR.
// Make sure we don't generate fake.use for volatile variables
// and parameters even when they are scalar.
-struct A {
+struct BigAggr {
unsigned long t;
char c[1024];
unsigned char r[32];
};
+struct SmallAggr {
+ int i;
+ int j;
+};
-int foo(volatile int param)
+int foo(volatile int vol_param, int param)
{
- struct A s;
- volatile int vloc;
- struct A v[128];
- char c[33];
+ struct BigAggr big;
+ struct SmallAggr small;
+ volatile int vol_local;
+ int local;
+ char long_arr[17];
+ char short_arr[16];
return 0;
}
-// CHECK-NOT: fake.use
+// CHECK: [[SMALL_ARR_FAKE_USE:%.+]] = load [16 x i8], ptr %short_arr
+// CHECK: call void (...) @llvm.fake.use([16 x i8] [[SMALL_ARR_FAKE_USE]])
+
+// CHECK: [[LOCAL_FAKE_USE:%.+]] = load i32, ptr %local
+// CHECK: call void (...) @llvm.fake.use(i32 [[LOCAL_FAKE_USE]])
+
+// CHECK: [[SMALL_FAKE_USE:%.+]] = load %struct.SmallAggr, ptr %small
+// CHECK: call void (...) @llvm.fake.use(%struct.SmallAggr [[SMALL_FAKE_USE]])
+
+// CHECK: [[PARAM_FAKE_USE:%.+]] = load i32, ptr %param.addr
+// CHECK: call void (...) @llvm.fake.use(i32 [[PARAM_FAKE_USE]])
+
+// CHECK: declare void @llvm.fake.use
\ No newline at end of file
diff --git a/clang/test/CodeGen/fake-use-small-aggs.c b/clang/test/CodeGen/fake-use-small-aggs.c
deleted file mode 100644
index 236e3a08c533ef..00000000000000
--- a/clang/test/CodeGen/fake-use-small-aggs.c
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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-this.cpp b/clang/test/CodeGen/fake-use-this.cpp
new file mode 100644
index 00000000000000..da559e0aeb0ea1
--- /dev/null
+++ b/clang/test/CodeGen/fake-use-this.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 %s -O0 -disable-O0-optnone -emit-llvm -fextend-this-ptr -o - | FileCheck %s --implicit-check-not=fake.use
+// Check that we generate a fake_use call with the 'this' pointer as argument,
+// and no other fake uses.
+// The call should appear after the call to bar().
+
+void bar();
+
+class C
+{
+public:
+ bool test(int p);
+ C(int v): v(v) {}
+
+private:
+ int v;
+};
+
+bool C::test(int p)
+{
+// CHECK-LABEL: define{{.*}}_ZN1C4testEi(ptr{{[^,]*}} %this, i32{{.*}} %p)
+// CHECK: %this.addr = alloca ptr
+// CHECK: store ptr %this, ptr %this.addr
+ int res = p - v;
+
+ bar();
+// CHECK: call{{.*}}bar
+
+ return res != 0;
+// CHECK: [[FAKE_USE:%.+]] = load ptr, ptr %this.addr
+// CHECK-NEXT: call void (...) @llvm.fake.use(ptr{{.*}} [[FAKE_USE]])
+// CHECK-NEXT: ret
+}
+
+// CHECK: declare void @llvm.fake.use
\ No newline at end of file
diff --git a/clang/test/CodeGen/fake-use-while.c b/clang/test/CodeGen/fake-use-while.c
index 6055a73636f6ce..bfdfab886ce741 100644
--- a/clang/test/CodeGen/fake-use-while.c
+++ b/clang/test/CodeGen/fake-use-while.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
+// RUN: %clang_cc1 %s -O0 -disable-O0-optnone -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
diff --git a/clang/test/CodeGen/fake-use.cpp b/clang/test/CodeGen/fake-use.cpp
deleted file mode 100644
index b58ba3ea60c4a2..00000000000000
--- a/clang/test/CodeGen/fake-use.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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
-}
More information about the cfe-commits
mailing list