[clang] [Clang] Add fake use emission to Clang with -fextend-lifetimes (PR #110102)
Stephen Tozer via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 29 11:01:20 PST 2024
https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/110102
>From fdfa695bc74847f5cc366bfcbf142bd5c2e3937f 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/4] [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 20455dbb820914d..b8265e5afcdcbf5 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -3595,15 +3595,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 d9c0dbe45d6cf90..87858fda263ca7a 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 c73c97146abc4d4..ba78e5478ac373b 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 47b21bc9f63f045..2efac1df914e9d8 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1352,6 +1352,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
@@ -1411,6 +1419,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
@@ -1663,6 +1704,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;
}
@@ -2524,6 +2576,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
@@ -2717,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 f8138e9cee250c8..607dfeaf42e9169 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 eaea0d8a08ac06f..81aaab80767e58f 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.
@@ -4991,6 +5005,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 741b0f17da6584d..819d6f0fb1565a9 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -603,6 +603,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;
@@ -1280,6 +1283,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 0c667e80bb6d8cf..ed11dc2bb05d73f 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 000000000000000..ef2d00eb6be3121
--- /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 000000000000000..119c783c634806d
--- /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 000000000000000..d62efbb4efe7ee7
--- /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 000000000000000..fd4881b9bcd5316
--- /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 000000000000000..91526f29936146c
--- /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 000000000000000..acfcb0ce90b5e49
--- /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 000000000000000..0c4377058d66280
--- /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 000000000000000..915924a2b310259
--- /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 000000000000000..e42216c4a7feda3
--- /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 000000000000000..236e3a08c533ef7
--- /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 000000000000000..6055a73636f6ce5
--- /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 000000000000000..b58ba3ea60c4a23
--- /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 000000000000000..67b9fac9b1bffeb
--- /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 cde7206e365e6c9609d69ea926e6d5b8f27bad64 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/4] 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 2efac1df914e9d8..f808127460be1b9 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1353,6 +1353,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});
@@ -1446,8 +1451,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 000000000000000..5477ec527413e10
--- /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 000000000000000..bdaa5b8f2e34eb2
--- /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 ef2d00eb6be3121..4b9b73438a1d133 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 119c783c634806d..000000000000000
--- 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 d62efbb4efe7ee7..7eca2bb3d175b20 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 fd4881b9bcd5316..49e168eefefcbca 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 91526f29936146c..e842983f391f44f 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 acfcb0ce90b5e49..ee82168eef80d01 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 0c4377058d66280..0368bb109a3c429 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 915924a2b310259..582c40ada891d73 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 e42216c4a7feda3..fd3acf52641c4d0 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 236e3a08c533ef7..000000000000000
--- 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 000000000000000..da559e0aeb0ea18
--- /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 6055a73636f6ce5..bfdfab886ce7411 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 b58ba3ea60c4a23..000000000000000
--- 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
-}
>From 124ada42c9eb6aff186a30f5acc41a9bce490e5b Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Fri, 29 Nov 2024 18:55:45 +0000
Subject: [PATCH 3/4] Move O0 logic to front of codegen, minor test and code
fixups
---
clang/lib/CodeGen/CGCleanup.cpp | 6 +++++-
clang/lib/CodeGen/CGDecl.cpp | 2 +-
clang/lib/Frontend/CompilerInvocation.cpp | 7 +++++++
clang/test/CodeGen/fake-use-landingpad.c | 2 +-
4 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp
index 87858fda263ca7a..7e1c5b7da955229 100644
--- a/clang/lib/CodeGen/CGCleanup.cpp
+++ b/clang/lib/CodeGen/CGCleanup.cpp
@@ -116,7 +116,11 @@ 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() || cleanup->isFakeUse()))
+ // If this is anything other than a lifetime marker or fake use cleanup,
+ // then the scope stack does not contain only noop cleanups.
+ if (!cleanup)
+ return false;
+ if (!cleanup->isLifetimeMarker() && !cleanup->isFakeUse())
return false;
}
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index f808127460be1b9..b70ddf5f5b18458 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1717,7 +1717,7 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
if (CGM.getCodeGenOpts().ExtendLifetimes) {
if (extendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl))
EHStack.pushCleanup<FakeUse>(NormalFakeUse,
- emission.getAllocatedAddress());
+ emission.getOriginalAllocatedAddress());
}
return emission;
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 3dd94c31b2bc7a6..fbdbfcbc682e5a4 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1834,6 +1834,13 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
Opts.setInlining(CodeGenOptions::NormalInlining);
}
+ // Extended lifetimes are meaningless if we are not going to run any
+ // optimizations, so skip them here.
+ if (Opts.OptimizationLevel == 0 && !Opts.DisableO0ImplyOptNone) {
+ Opts.ExtendLifetimes = false;
+ Opts.ExtendThisPtr = false;
+ }
+
// PIC defaults to -fno-direct-access-external-data while non-PIC defaults to
// -fdirect-access-external-data.
Opts.DirectAccessExternalData =
diff --git a/clang/test/CodeGen/fake-use-landingpad.c b/clang/test/CodeGen/fake-use-landingpad.c
index e842983f391f44f..1f23c583254643d 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 -O0 -disable-O0-optnone -emit-llvm -fextend-lifetimes -fexceptions -o - | FileCheck %s --implicit-check-not=landingpad
+// 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.
>From e8a38f0462c4c5deaad0cc5fb8e57f1b23aa80e3 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Fri, 29 Nov 2024 19:00:55 +0000
Subject: [PATCH 4/4] Adjust test to not assume size of 'unsigned'
---
clang/test/CodeGen/fake-use-scalar.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/test/CodeGen/fake-use-scalar.c b/clang/test/CodeGen/fake-use-scalar.c
index fd3acf52641c4d0..8882775a093cf40 100644
--- a/clang/test/CodeGen/fake-use-scalar.c
+++ b/clang/test/CodeGen/fake-use-scalar.c
@@ -21,13 +21,13 @@ int foo(volatile int vol_param, int param)
struct SmallAggr small;
volatile int vol_local;
int local;
- char long_arr[17];
- char short_arr[16];
+ unsigned long_arr[5];
+ unsigned short_arr[4];
return 0;
}
-// 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: [[SMALL_ARR_FAKE_USE:%.+]] = load [4 x i[[#UINT_SIZE:]]], ptr %short_arr
+// CHECK: call void (...) @llvm.fake.use([4 x i[[#UINT_SIZE]]] [[SMALL_ARR_FAKE_USE]])
// CHECK: [[LOCAL_FAKE_USE:%.+]] = load i32, ptr %local
// CHECK: call void (...) @llvm.fake.use(i32 [[LOCAL_FAKE_USE]])
More information about the cfe-commits
mailing list