wolfgangp created this revision.
Herald added a subscriber: JDevlieghere.

The patch implements the clang support for generating artificial uses of local 
variables and parameters to aid with debugging of optimized code. I presented 
this concept in my lightning talk "Debugging of optimized code: Extending the 
lifetime of local variables" at the October 2017 LLVM conference.

Since the concept is not universally supported, the purpose of this review is 
to make the patch available to interested parties and perhaps to restart the 
discussion.

This patch requires the patch posted in https://reviews.llvm.org/D41043 (the 
llvm portion).

The implementation generates calls to the llvm intrinsic fake.use in similar 
fashion as end of lifetime markers.


https://reviews.llvm.org/D41044

Files:
  include/clang/Driver/Options.td
  include/clang/Frontend/CodeGenOptions.def
  lib/CodeGen/CGDecl.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/CodeGen/CodeGenModule.h
  lib/Driver/ToolChains/Clang.cpp
  lib/Frontend/CompilerInvocation.cpp
  test/CodeGen/fake-use.cpp

Index: test/CodeGen/fake-use.cpp
===================================================================
--- test/CodeGen/fake-use.cpp
+++ test/CodeGen/fake-use.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -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: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -999,6 +999,11 @@
   Opts.CudaGpuBinaryFileNames =
       Args.getAllArgValues(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
@@ -4434,6 +4434,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/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -484,6 +484,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;
@@ -970,6 +973,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
@@ -421,6 +421,20 @@
     }
   };
 
+  // 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.
@@ -3625,6 +3639,8 @@
 
   RValue EmitAtomicExpr(AtomicExpr *E);
 
+  void EmitFakeUse(Address Addr);
+
   //===--------------------------------------------------------------------===//
   //                         Annotations Emission
   //===--------------------------------------------------------------------===//
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp
+++ lib/CodeGen/CGDecl.cpp
@@ -955,6 +955,12 @@
   C->setDoesNotThrow();
 }
 
+void CodeGenFunction::EmitFakeUse(Address Addr) {
+  llvm::Value *V = Builder.CreateLoad(Addr, "fake.use");
+  llvm::CallInst *C = Builder.CreateCall(CGM.getLLVMFakeUseFn(), {V});
+  C->setDoesNotThrow();
+}
+
 /// EmitAutoVarAlloca - Emit the alloca and debug information for a
 /// local variable.  Does not emit initialization or destruction.
 CodeGenFunction::AutoVarEmission
@@ -1140,6 +1146,13 @@
                                          emission.getAllocatedAddress(),
                                          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>(NormalCleanup, emission.getAllocatedAddress());
+  }
+
   return emission;
 }
 
@@ -1757,6 +1770,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
@@ -1897,6 +1919,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>(NormalCleanup, DeclPtr);
+    } else if (CGM.getCodeGenOpts().ExtendThisPtr) {
+      // Enter only the 'this' pointer for -fextend-this-ptr.
+      if (&D == CXXABIThisDecl)
+        EHStack.pushCleanup<FakeUse>(NormalCleanup, DeclPtr);
+    }
+  }
+
   // Emit debug info for param declaration.
   if (CGDebugInfo *DI = getDebugInfo()) {
     if (CGM.getCodeGenOpts().getDebugInfo() >=
Index: include/clang/Frontend/CodeGenOptions.def
===================================================================
--- include/clang/Frontend/CodeGenOptions.def
+++ include/clang/Frontend/CodeGenOptions.def
@@ -278,6 +278,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
@@ -1564,7 +1564,12 @@
  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 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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to