StephenTozer created this revision.
StephenTozer added reviewers: dblaikie, aprantl, echristo.
StephenTozer added a project: debug-info.
Herald added a reviewer: aaron.ballman.
Herald added a project: All.
StephenTozer requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay.
Herald added a project: clang.

This patch is part of a set of patches that add an `-fextend-lifetimes` flag to 
clang, which extends the lifetimes of local variables and parameters for 
improved debuggability. In addition to that flag, the patch series adds a new 
function attribute to disable the post-RA scheduler (which is applied by 
`-fextend-lifetimes`), a pragma to selectively disable `-fextend-lifetimes`, 
and an `-fextend-this-ptr` flag which functions as `-fextend-lifetimes` for 
`this` pointers only. All changes and tests in these patches were written by 
@wolfgangp, though I will be managing these reviews and addressing any comments 
raised. Discussion on the approach of this patch series as a whole should be 
directed to this patch, in order to simplify review.

---

This particular patch implements the clang side of the `-fextend-lifetimes` and 
`-fextend-this-ptr` flags, including adding the command line flags, applying 
the `disable-post-ra` attribute to functions having lifetimes extended (see the 
first patch in this sequence), and the emission of `llvm.fake.use` intrinsics 
for variables at the end of their scopes. The main bulk of the patch concerns 
emitting fake.use intrinsics, which are treated as noop cleanups since they 
should be emitted at all points where the variable falls out of scope.

In our (Sony's) own internal measurements (presented at EuroLLVM2023 
<https://llvm.org/devmtg/2023-05/slides/Lightning-Talks/04-Tozer-UsingAutomatedTestsToTuneOgSlides.pdf>),
 using this flag at `-O1` gave a 2.4% performance cost for a 22.1% increase in 
step and variable availability compared to just `-O1`. From these results, it 
appears that using this flag gives an efficient increase in debuggability of 
programs for its performance cost, and should be very useful for development 
builds.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D157613

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/CodeGenOptions.def
  clang/include/clang/Driver/Options.td
  clang/lib/CodeGen/CGCall.cpp
  clang/lib/CodeGen/CGCleanup.cpp
  clang/lib/CodeGen/CGCleanup.h
  clang/lib/CodeGen/CGDecl.cpp
  clang/lib/CodeGen/CodeGenFunction.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/lib/CodeGen/CodeGenModule.h
  clang/lib/CodeGen/EHScopeStack.h
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/test/CodeGen/extend-liveness1.c
  clang/test/CodeGen/extend-liveness2.cpp
  clang/test/CodeGen/fake-use-determinism.c
  clang/test/CodeGen/fake-use-lambda.cpp
  clang/test/CodeGen/fake-use-landingpad.c
  clang/test/CodeGen/fake-use-noreturn.c
  clang/test/CodeGen/fake-use-return-line.c
  clang/test/CodeGen/fake-use-sanitizer.cpp
  clang/test/CodeGen/fake-use-scalar.c
  clang/test/CodeGen/fake-use-small-aggs.c
  clang/test/CodeGen/fake-use-while.c
  clang/test/CodeGen/fake-use.cpp
  clang/test/CodeGen/no-fake-use-O0.cpp
  clang/test/CodeGen/suppress-post-ra.c

Index: clang/test/CodeGen/suppress-post-ra.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/suppress-post-ra.c
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 %s -S -emit-llvm -O2 -fextend-lifetimes -o - | FileCheck %s
+
+// Emit the function attribute disable-post-ra when
+// -fextend-lifetimes is on.
+
+// CHECK: attributes #0 = {{{.*}}"disable-post-ra"
+
+void foo() {}
Index: clang/test/CodeGen/no-fake-use-O0.cpp
===================================================================
--- /dev/null
+++ 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;
+}
Index: clang/test/CodeGen/fake-use.cpp
===================================================================
--- /dev/null
+++ 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
+}
Index: clang/test/CodeGen/fake-use-while.c
===================================================================
--- /dev/null
+++ clang/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: clang/test/CodeGen/fake-use-small-aggs.c
===================================================================
--- /dev/null
+++ 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();
+}
+
Index: clang/test/CodeGen/fake-use-scalar.c
===================================================================
--- /dev/null
+++ 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
Index: clang/test/CodeGen/fake-use-sanitizer.cpp
===================================================================
--- /dev/null
+++ 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:
Index: clang/test/CodeGen/fake-use-return-line.c
===================================================================
--- /dev/null
+++ clang/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: clang/test/CodeGen/fake-use-noreturn.c
===================================================================
--- /dev/null
+++ clang/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: clang/test/CodeGen/fake-use-landingpad.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/fake-use-landingpad.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 %s -O3 -emit-llvm -fextend-lifetimes -fexceptions -o - | FileCheck %s
+
+// Check that fake uses do not mistakenly cause a landing pad to be generated when
+// exceptions are enabled.
+
+extern void bar(int);
+void foo(int p) {
+  int a = 17;
+  bar(a);
+}
+
+// CHECK:      define {{.*}} @foo
+// CHECK-NOT:  personality
+// CHECK:      entry:
+// CHECK-NOT:  landingpad
Index: clang/test/CodeGen/fake-use-lambda.cpp
===================================================================
--- /dev/null
+++ 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
Index: clang/test/CodeGen/fake-use-determinism.c
===================================================================
--- /dev/null
+++ 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 %i)
+// CHECK-NEXT:  call {{.*}}void (...) @llvm.fake.use(i32 %j)
+// CHECK-NEXT:  call {{.*}}void (...) @llvm.fake.use(i32 %k)
+
+extern void bar();
+void foo(int i, int j, int k)
+{
+   for (int l = 0; l < i; l++) {
+      bar();
+   }
+}
Index: clang/test/CodeGen/extend-liveness2.cpp
===================================================================
--- /dev/null
+++ 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)
+  // CHECK: ret void
+}
Index: clang/test/CodeGen/extend-liveness1.c
===================================================================
--- /dev/null
+++ 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;
+}
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -2052,6 +2052,11 @@
                       Args.getAllArgValues(OPT_fsanitize_trap_EQ), Diags,
                       Opts.SanitizeTrap);
 
+  Opts.ExtendThisPtr =
+      Opts.OptimizationLevel > 0 && Args.hasArg(OPT_fextend_this_ptr);
+  Opts.ExtendLifetimes =
+      Opts.OptimizationLevel > 0 && Args.hasArg(OPT_fextend_lifetimes);
+
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 
   if (Args.hasArg(options::OPT_ffinite_loops))
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -7067,6 +7067,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: clang/lib/CodeGen/EHScopeStack.h
===================================================================
--- clang/lib/CodeGen/EHScopeStack.h
+++ clang/lib/CodeGen/EHScopeStack.h
@@ -87,6 +87,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
@@ -352,8 +357,8 @@
   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; }
Index: clang/lib/CodeGen/CodeGenModule.h
===================================================================
--- clang/lib/CodeGen/CodeGenModule.h
+++ clang/lib/CodeGen/CodeGenModule.h
@@ -576,6 +576,9 @@
   /// void @llvm.lifetime.end(i64 %size, i8* nocapture <ptr>)
   llvm::Function *LifetimeEndFn = nullptr;
 
+  /// void @llvm.fake.use(i8* nocapture <ptr>)
+  llvm::Function *FakeUseFn = nullptr;
+
   std::unique_ptr<SanitizerMetadata> SanitizerMD;
 
   llvm::MapVector<const Decl *, bool> DeferredEmptyCoverageMappingDecls;
@@ -1179,6 +1182,7 @@
 
   llvm::Function *getLLVMLifetimeStartFn();
   llvm::Function *getLLVMLifetimeEndFn();
+  llvm::Function *getLLVMFakeUseFn();
 
   // Make sure that this type is translated.
   void UpdateCompletedType(const TagDecl *TD);
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -643,6 +643,20 @@
     }
   };
 
+  // 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.
@@ -4560,6 +4574,8 @@
 
   RValue EmitAtomicExpr(AtomicExpr *E);
 
+  void EmitFakeUse(Address Addr);
+
   //===--------------------------------------------------------------------===//
   //                         Annotations Emission
   //===--------------------------------------------------------------------===//
Index: clang/lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.cpp
+++ clang/lib/CodeGen/CodeGenFunction.cpp
@@ -359,9 +359,9 @@
   // 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) {
Index: clang/lib/CodeGen/CGDecl.cpp
===================================================================
--- clang/lib/CodeGen/CGDecl.cpp
+++ clang/lib/CodeGen/CGDecl.cpp
@@ -1376,6 +1376,14 @@
   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
@@ -1435,6 +1443,39 @@
   }
 }
 
+/// 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
@@ -1684,6 +1725,17 @@
                                          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;
 }
 
@@ -2474,6 +2526,15 @@
   return LifetimeEndFn;
 }
 
+/// Lazily declare the @llvm.fake.use intrinsic.
+llvm::Function *CodeGenModule::getLLVMFakeUseFn() {
+  if (FakeUseFn)
+    return FakeUseFn;
+  FakeUseFn =
+      llvm::Intrinsic::getDeclaration(&getModule(), llvm::Intrinsic::fake_use);
+  return FakeUseFn;
+}
+
 namespace {
   /// A cleanup to perform a release of an object at the end of a
   /// function.  This is used to balance out the incoming +1 of a
@@ -2667,6 +2728,15 @@
 
   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 &&
Index: clang/lib/CodeGen/CGCleanup.h
===================================================================
--- clang/lib/CodeGen/CGCleanup.h
+++ clang/lib/CodeGen/CGCleanup.h
@@ -75,6 +75,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;
 
@@ -295,6 +298,7 @@
     CleanupBits.IsEHCleanup = isEH;
     CleanupBits.IsActive = true;
     CleanupBits.IsLifetimeMarker = false;
+    CleanupBits.IsFakeUse = false;
     CleanupBits.TestFlagInNormalCleanup = false;
     CleanupBits.TestFlagInEHCleanup = false;
     CleanupBits.CleanupSize = cleanupSize;
@@ -320,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.isValid(); }
   Address getActiveFlag() const {
     return ActiveFlag;
Index: clang/lib/CodeGen/CGCleanup.cpp
===================================================================
--- clang/lib/CodeGen/CGCleanup.cpp
+++ clang/lib/CodeGen/CGCleanup.cpp
@@ -141,11 +141,11 @@
   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;
   }
 
@@ -183,6 +183,7 @@
   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
@@ -205,6 +206,8 @@
     InnermostEHScope = stable_begin();
   if (IsLifetimeMarker)
     Scope->setLifetimeMarker();
+  if (IsFakeUse)
+    Scope->setFakeUse();
 
   // With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup
   if (CGF->getLangOpts().EHAsynch && IsEHCleanup && !IsLifetimeMarker &&
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -2491,6 +2491,10 @@
     if (shouldDisableTailCalls())
       FuncAttrs.addAttribute("disable-tail-calls", "true");
 
+    // Suppress the machine instruction scheduler when -fextend-lifetimes is on.
+    if (CodeGenOpts.ExtendLifetimes)
+      FuncAttrs.addAttribute("disable-post-ra");
+
     // CPU/feature overrides.  addDefaultFunctionDefinitionAttributes
     // handles these separately to set them based on the global defaults.
     GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs);
@@ -3476,14 +3480,26 @@
 
     // Look at directly preceding instruction, skipping bitcasts and lifetime
     // markers.
-    for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) {
-      if (isa<llvm::BitCastInst>(&I))
+    llvm::Instruction *I = &IP->back();
+    for (llvm::BasicBlock::reverse_iterator II = IP->rbegin(), IE = IP->rend();
+         II != IE; ++II) {
+      if (isa<llvm::BitCastInst>(&*II))
         continue;
-      if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I))
-        if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end)
+      if (auto *Intrinsic = dyn_cast<llvm::IntrinsicInst>(&*II)) {
+        if (Intrinsic->getIntrinsicID() == llvm::Intrinsic::lifetime_end)
           continue;
 
-      return GetStoreIfValid(&I);
+        // 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;
+        }
+      }
+      I = &*II;
+      return GetStoreIfValid(I);
     }
     return nullptr;
   }
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -3307,6 +3307,14 @@
   Flags<[CC1Option, NoDriverOption]>,
   HelpText<"Filename (or -) to write stack usage output to">,
   MarshallingInfoString<CodeGenOpts<"StackUsageOutput">>;
+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]>,
+  MarshallingInfoFlag<CodeGenOpts<"ExtendThisPtr">>;
+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]>,
+  MarshallingInfoFlag<CodeGenOpts<"ExtendLifetimes">>;
 
 defm unique_basic_block_section_names : BoolFOption<"unique-basic-block-section-names",
   CodeGenOpts<"UniqueBasicBlockSectionNames">, DefaultFalse,
Index: clang/include/clang/Basic/CodeGenOptions.def
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.def
+++ clang/include/clang/Basic/CodeGenOptions.def
@@ -432,6 +432,12 @@
 /// Bit size of immediate TLS offsets (0 == use the default).
 VALUE_CODEGENOPT(TLSSize, 8, 0)
 
+/// Whether to extend the live range of the `this` pointer.
+CODEGENOPT(ExtendThisPtr, 1, 0)
+
+/// Whether to extend the live ranges of all local variables.
+CODEGENOPT(ExtendLifetimes, 1, 0)
+
 /// The default stack protector guard offset to use.
 VALUE_CODEGENOPT(StackProtectorGuardOffset, 32, INT_MAX)
 
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -2361,6 +2361,12 @@
   let Documentation = [ObjCBoxableDocs];
 }
 
+def ExtendLifetimesDisable : InheritableAttr {
+  let Spellings = [];
+  let Subjects = SubjectList<[Function]>;
+  let Documentation = [ExtendLifetimesDisableDocs];
+}
+
 def OptimizeNone : InheritableAttr {
   let Spellings = [Clang<"optnone">];
   let Subjects = SubjectList<[Function, ObjCMethod]>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to