wolfgangp updated this revision to Diff 175307.
wolfgangp added a comment.
Rebased on r347577 with some test cases.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D41044/new/
https://reviews.llvm.org/D41044
Files:
include/clang/Driver/Options.td
include/clang/Frontend/CodeGenOptions.def
lib/CodeGen/CGCall.cpp
lib/CodeGen/CGCleanup.cpp
lib/CodeGen/CGCleanup.h
lib/CodeGen/CGDecl.cpp
lib/CodeGen/CodeGenFunction.h
lib/CodeGen/CodeGenModule.h
lib/CodeGen/EHScopeStack.h
lib/Driver/ToolChains/Clang.cpp
lib/Frontend/CompilerInvocation.cpp
test/CodeGen/fake-use-determinism.c
test/CodeGen/fake-use-landingpad.c
test/CodeGen/fake-use-noreturn.c
test/CodeGen/fake-use-return-line.c
test/CodeGen/fake-use-sanitizer.cpp
test/CodeGen/fake-use-scalar.c
test/CodeGen/fake-use-while.c
test/CodeGen/fake-use.cpp
test/CodeGen/no-fake-use-O0.cpp
Index: test/CodeGen/no-fake-use-O0.cpp
===================================================================
--- /dev/null
+++ test/CodeGen/no-fake-use-O0.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 %s -O0 -emit-llvm -fextend-this-ptr -o - | FileCheck %s
+// RUN: %clang_cc1 %s -O0 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
+// 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();
+// CHECK-NOT: call void (...) @llvm.fake.use
+// OPT: call void (...) @llvm.fake.use
+ return V;
+}
Index: test/CodeGen/fake-use.cpp
===================================================================
--- /dev/null
+++ 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(%class.w* %this)
+ return V;
+// CHECK: ret
+}
Index: test/CodeGen/fake-use-while.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-while.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 %s -S -emit-llvm -fextend-lifetimes -o %t.ll
+//
+// 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{{.*}}main
+// CHECK: call{{.*}}llvm.fake.use
+
+void foo() {
+ {
+ while (1) {
+ int ret;
+ if (1)
+ return;
+ }
+ }
+}
Index: test/CodeGen/fake-use-scalar.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-scalar.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
+// Make sure we don't generate fake.use for non-scalar
+// variables.
+
+struct A {
+ unsigned long t;
+ char c[1024];
+ unsigned char r[32];
+};
+
+
+int foo()
+{
+ struct A s;
+ struct A v[128];
+ char c[32];
+ return 0;
+}
+
+// CHECK-NOT: fake.use
Index: test/CodeGen/fake-use-sanitizer.cpp
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-sanitizer.cpp
@@ -0,0 +1,38 @@
+// 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
+// REQUIRES: asserts
+
+// 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 functions 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:
Index: test/CodeGen/fake-use-return-line.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-return-line.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -S -emit-llvm -O2 -debug-info-kind=limited -fextend-lifetimes -o - %s | FileCheck %s
+int main()
+{
+ volatile int a = 1;
+ int b = a + 2;
+ return b;
+}
+// CHECK: define{{.*}}@main
+// CHECK: ret i32{{.*}}!dbg ![[MDINDEX:[0-9]*]]
+// CHECK: ![[MDINDEX]] = !DILocation(line: 6
Index: test/CodeGen/fake-use-noreturn.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-noreturn.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 %s -S -emit-llvm -fextend-lifetimes -o %t.ll
+//
+// Check we don't assert when we have a return in a nested conditional and
+// there is no code at the end of the function.
+
+// CHECK: define{{.*}}main
+// CHECK: call{{.*}}llvm.fake.use
+
+void foo(int i) {
+ while (0)
+ if (1)
+ return;
+}
Index: test/CodeGen/fake-use-landingpad.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-landingpad.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 %s -O3 -emit-llvm -fextend-lifetimes -fexceptions -o - | FileCheck %s
+// REQUIRES: asserts
+
+// Check that fake uses do not mistakenly cause a landing pad to be generated when
+// exceptions are enabled.
+
+extern void bar(int);
+void foo(int p) {
+ int a = 17;
+ bar(a);
+}
+
+// CHECK: define {{.*}} @foo
+// CHECK-NOT: personality
+// CHECK: entry:
+// CHECK-NOT: landingpad
Index: test/CodeGen/fake-use-determinism.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-determinism.c
@@ -0,0 +1,26 @@
+// RUN: %clang -S -O2 -emit-llvm -fextend-lifetimes %s -o - | FileCheck %s
+// REQUIRES: asserts
+//
+// Non-deterministic order of fake.use instructions.
+//
+// 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.
+//
+// Different targets may have different forms. For example, on X86, we get a
+// line like:
+// tail call void (...) @llvm.fake.use(i32 %i)
+// but on ARM, we get a line like:
+// tail call arm_aapcs_vfpcc void (...) @llvm.fake.use(i32 %i)
+
+// 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();
+ }
+}
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -1152,6 +1152,11 @@
Opts.CudaGpuBinaryFileName =
Args.getLastArgValue(OPT_fcuda_include_gpubinary);
+ Opts.ExtendThisPtr = Opts.OptimizationLevel > 0 &&
+ Args.hasArg(OPT_fextend_this_ptr);
+ Opts.ExtendLifetimes = Opts.OptimizationLevel > 0 &&
+ Args.hasArg(OPT_fextend_lifetimes);
+
Opts.Backchain = Args.hasArg(OPT_mbackchain);
Opts.EmitCheckPathComponentsToStrip = getLastArgIntValue(
Index: lib/Driver/ToolChains/Clang.cpp
===================================================================
--- lib/Driver/ToolChains/Clang.cpp
+++ lib/Driver/ToolChains/Clang.cpp
@@ -4970,6 +4970,11 @@
if (Args.hasArg(options::OPT_fretain_comments_from_system_headers))
CmdArgs.push_back("-fretain-comments-from-system-headers");
+ if (Args.hasArg(options::OPT_fextend_this_ptr))
+ CmdArgs.push_back("-fextend-this-ptr");
+ if (Args.hasArg(options::OPT_fextend_lifetimes))
+ CmdArgs.push_back("-fextend-lifetimes");
+
// Forward -fcomment-block-commands to -cc1.
Args.AddAllArgs(CmdArgs, options::OPT_fcomment_block_commands);
// Forward -fparse-all-comments to -cc1.
Index: lib/CodeGen/EHScopeStack.h
===================================================================
--- lib/CodeGen/EHScopeStack.h
+++ lib/CodeGen/EHScopeStack.h
@@ -93,6 +93,11 @@
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
Index: lib/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -495,6 +495,9 @@
/// void @llvm.lifetime.end(i64 %size, i8* nocapture <ptr>)
llvm::Constant *LifetimeEndFn = nullptr;
+ /// void @llvm.fake.use(i8* nocapture <ptr>)
+ llvm::Constant *FakeUseFn = nullptr;
+
GlobalDecl initializedGlobalDecl;
std::unique_ptr<SanitizerMetadata> SanitizerMD;
@@ -1007,6 +1010,7 @@
llvm::Constant *getLLVMLifetimeStartFn();
llvm::Constant *getLLVMLifetimeEndFn();
+ llvm::Constant *getLLVMFakeUseFn();
// Make sure that this type is translated.
void UpdateCompletedType(const TagDecl *TD);
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -505,6 +505,21 @@
}
};
+ // We are using objects of this 'cleanup' class to emit fake.use calls
+ // for -fextend-lifetimes. 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.
@@ -3966,6 +3981,8 @@
RValue EmitAtomicExpr(AtomicExpr *E);
+ void EmitFakeUse(Address Addr);
+
//===--------------------------------------------------------------------===//
// Annotations Emission
//===--------------------------------------------------------------------===//
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp
+++ lib/CodeGen/CGDecl.cpp
@@ -1089,6 +1089,15 @@
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
@@ -1350,6 +1359,12 @@
EHStack.pushCleanup<CallLifetimeEnd>(NormalEHLifetimeMarker,
emission.getOriginalAllocatedAddress(),
emission.getSizeForLifetimeMarkers());
+ // Analogous to lifetime markers, we use a 'cleanup' to emit fake.use
+ // calls for local variables.
+ if (CGM.getCodeGenOpts().ExtendLifetimes &&
+ hasScalarEvaluationKind(D.getType())) {
+ EHStack.pushCleanup<FakeUse>(NormalFakeUse, emission.getAllocatedAddress());
+ }
return emission;
}
@@ -1979,6 +1994,15 @@
return LifetimeEndFn;
}
+/// Lazily declare the @llvm.fake.use intrinsic.
+llvm::Constant *CodeGenModule::getLLVMFakeUseFn() {
+ if (FakeUseFn)
+ return FakeUseFn;
+ FakeUseFn =
+ llvm::Intrinsic::getDeclaration(&getModule(), llvm::Intrinsic::fake_use);
+ return FakeUseFn;
+}
+
namespace {
/// A cleanup to perform a release of an object at the end of a
/// function. This is used to balance out the incoming +1 of a
@@ -2152,6 +2176,21 @@
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 (CurFuncDecl && !CurFuncDecl->isImplicit() &&
+ (&D == CXXABIThisDecl || !D.isImplicit())) {
+ if (CGM.getCodeGenOpts().ExtendLifetimes) {
+ if (IsScalar)
+ EHStack.pushCleanup<FakeUse>(NormalFakeUse, DeclPtr);
+ } else if (CGM.getCodeGenOpts().ExtendThisPtr) {
+ // Enter only the 'this' pointer for -fextend-this-ptr.
+ if (&D == CXXABIThisDecl)
+ EHStack.pushCleanup<FakeUse>(NormalFakeUse, DeclPtr);
+ }
+ }
+
// Emit debug info for param declaration.
if (CGDebugInfo *DI = getDebugInfo()) {
if (CGM.getCodeGenOpts().getDebugInfo() >=
Index: lib/CodeGen/CGCleanup.h
===================================================================
--- lib/CodeGen/CGCleanup.h
+++ lib/CodeGen/CGCleanup.h
@@ -77,6 +77,9 @@
/// Whether this cleanup is a lifetime marker
unsigned IsLifetimeMarker : 1;
+ /// Whether this cleanup is a fake use
+ unsigned IsFakeUse : 1;
+
/// Whether the normal cleanup should test the activation flag.
unsigned TestFlagInNormalCleanup : 1;
@@ -321,6 +324,9 @@
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 != nullptr; }
Address getActiveFlag() const {
return Address(ActiveFlag, CharUnits::One());
Index: lib/CodeGen/CGCleanup.cpp
===================================================================
--- lib/CodeGen/CGCleanup.cpp
+++ lib/CodeGen/CGCleanup.cpp
@@ -149,7 +149,8 @@
EHScopeStack::stable_iterator Old) const {
for (EHScopeStack::iterator it = begin(); stabilize(it) != Old; it++) {
EHCleanupScope *cleanup = dyn_cast<EHCleanupScope>(&*it);
- if (!cleanup || !cleanup->isLifetimeMarker())
+ // Augment this test to also ignore fake use "cleanups".
+ if (!cleanup || !(cleanup->isLifetimeMarker() || cleanup->isFakeUse()))
return false;
}
@@ -188,6 +189,7 @@
bool IsEHCleanup = Kind & EHCleanup;
bool IsActive = !(Kind & InactiveCleanup);
bool IsLifetimeMarker = Kind & LifetimeMarker;
+ bool IsFakeUse = Kind & FakeUse;
EHCleanupScope *Scope =
new (Buffer) EHCleanupScope(IsNormalCleanup,
IsEHCleanup,
@@ -202,6 +204,8 @@
InnermostEHScope = stable_begin();
if (IsLifetimeMarker)
Scope->setLifetimeMarker();
+ if (IsFakeUse)
+ Scope->setFakeUse();
return Scope->getCleanupBuffer();
}
Index: lib/CodeGen/CGCall.cpp
===================================================================
--- lib/CodeGen/CGCall.cpp
+++ lib/CodeGen/CGCall.cpp
@@ -2757,6 +2757,14 @@
II != IE; ++II) {
if (llvm::IntrinsicInst *Intrinsic =
dyn_cast<llvm::IntrinsicInst>(&*II)) {
+ // Ignore fake uses and the instructions that load their
+ // operands.
+ if (Intrinsic->getIntrinsicID() == llvm::Intrinsic::fake_use) {
+ const llvm::Value *FakeUseArg = Intrinsic->getArgOperand(0);
+ if (++II == IE || &*II != FakeUseArg)
+ break;
+ continue;
+ }
if (Intrinsic->getIntrinsicID() == llvm::Intrinsic::lifetime_end) {
const llvm::Value *CastAddr = Intrinsic->getArgOperand(1);
++II;
Index: include/clang/Frontend/CodeGenOptions.def
===================================================================
--- include/clang/Frontend/CodeGenOptions.def
+++ include/clang/Frontend/CodeGenOptions.def
@@ -309,6 +309,12 @@
/// The default TLS model to use.
ENUM_CODEGENOPT(DefaultTLSModel, TLSModel, 2, GeneralDynamicTLSModel)
+/// Whether to extend the live range of the this ptr.
+CODEGENOPT(ExtendThisPtr, 1, 0)
+
+/// Whether to extend the live range of all local variables.
+CODEGENOPT(ExtendLifetimes, 1, 0)
+
/// Number of path components to strip when emitting checks. (0 == full
/// filename)
VALUE_CODEGENOPT(EmitCheckPathComponentsToStrip, 32, 0)
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -1758,11 +1758,16 @@
Flags<[CC1Option]>, HelpText<"Place each data in its own section (ELF Only)">;
def fno_data_sections : Flag <["-"], "fno-data-sections">, Group<f_Group>,
Flags<[CC1Option]>;
+def fextend_this_ptr : Flag <["-"], "fextend-this-ptr">, Group<f_Group>,
+ HelpText<"Extend the lifetime of the 'this' pointer to improve visibility "
+ "in optimized debugging">, Flags<[CC1Option]>;
+def fextend_lifetimes : Flag <["-"], "fextend-lifetimes">, Group<f_Group>,
+ HelpText<"Extend the lifetimes of local variables and parameters to improve "
+ "visibility in optimized debugging">, Flags<[CC1Option]>;
def fstack_size_section : Flag<["-"], "fstack-size-section">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Emit section containing metadata on function stack sizes">;
def fno_stack_size_section : Flag<["-"], "fno-stack-size-section">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Don't emit section containing metadata on function stack sizes">;
-
def funique_section_names : Flag <["-"], "funique-section-names">,
Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Use unique names for text and data sections (ELF Only)">;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits