[clang] [llvm] [clang][DebugInfo] Emit DW_AT_object_pointer on function declarations with explicit `this` (PR #122928)

Michael Buch via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 14 08:18:37 PST 2025


https://github.com/Michael137 created https://github.com/llvm/llvm-project/pull/122928

In https://github.com/llvm/llvm-project/pull/122897 we started attaching `DW_AT_object_pointer` to function definitions. This patch does the same but for function declarations (which we do for implicit object pointers already).

Fixes https://github.com/llvm/llvm-project/issues/120974

>From b1a937b23f76ac47d1f0340772c17734137eb261 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Tue, 14 Jan 2025 11:33:33 +0000
Subject: [PATCH] [clang][DebugInfo] Emit DW_AT_object_pointer on function
 definitions with explicit `this`

We currently don't emit `DW_AT_object_pointer` on function
declarations or definitions. The DWARFv5 spec doesn't mandate
this attribute be present *only* for implicit object parameters:
```
If the member function entry describes a non-static member function,
then that entry has a DW_AT_object_pointer attribute whose value is a reference to
the formal parameter entry that corresponds to the object for which the
function is called.

That parameter also has a DW_AT_artificial attribute whose value is true.
```

The part about `DW_AT_artificial` seems overly restrictive, and not true for
explicit object parameters. We probably should relax this part of the DWARF spec.

This will help LLDB identify static vs. non-static member functions (see
https://github.com/llvm/llvm-project/issues/120856).

GCC suffers from the same issue: https://godbolt.org/z/h4jeT54G5

Partially fixes https://github.com/llvm/llvm-project/issues/120974
---
 clang/lib/CodeGen/CGDebugInfo.cpp             | 24 ++++++++++++----
 .../CodeGenCXX/debug-info-object-pointer.cpp  | 28 +++++++++++++++++++
 llvm/include/llvm-c/DebugInfo.h               | 11 +++++---
 llvm/include/llvm/IR/DIBuilder.h              |  6 ++--
 llvm/lib/IR/DIBuilder.cpp                     |  8 ++++--
 llvm/lib/IR/DebugInfo.cpp                     |  9 +++---
 6 files changed, 67 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/debug-info-object-pointer.cpp

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index d7e5e95b7873a0..3905633468335e 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2016,13 +2016,15 @@ llvm::DISubroutineType *CGDebugInfo::getOrCreateInstanceMethodType(
   // First element is always return type. For 'void' functions it is NULL.
   Elts.push_back(Args[0]);
 
-  // "this" pointer is always first argument.
-  // ThisPtr may be null if the member function has an explicit 'this'
-  // parameter.
-  if (!ThisPtr.isNull()) {
+  const bool HasExplicitObjectParameter = ThisPtr.isNull();
+
+  // "this" pointer is always first argument. For explicit "this"
+  // parameters, it will already be in Args[1].
+  if (!HasExplicitObjectParameter) {
     llvm::DIType *ThisPtrType = getOrCreateType(ThisPtr, Unit);
     TypeCache[ThisPtr.getAsOpaquePtr()].reset(ThisPtrType);
-    ThisPtrType = DBuilder.createObjectPointerType(ThisPtrType);
+    ThisPtrType =
+        DBuilder.createObjectPointerType(ThisPtrType, /*Implicit=*/true);
     Elts.push_back(ThisPtrType);
   }
 
@@ -2030,6 +2032,13 @@ llvm::DISubroutineType *CGDebugInfo::getOrCreateInstanceMethodType(
   for (unsigned i = 1, e = Args.size(); i != e; ++i)
     Elts.push_back(Args[i]);
 
+  // Attach FlagObjectPointer to the explicit "this" parameter.
+  if (HasExplicitObjectParameter) {
+    assert(Elts.size() >= 2 &&
+           "Expected at least return type and object parameter.");
+    Elts[1] = DBuilder.createObjectPointerType(Args[1], /*Implicit=*/false);
+  }
+
   llvm::DITypeRefArray EltTypeArray = DBuilder.getOrCreateTypeArray(Elts);
 
   return DBuilder.createSubroutineType(EltTypeArray, OriginalFunc->getFlags(),
@@ -4829,6 +4838,9 @@ llvm::DILocalVariable *CGDebugInfo::EmitDeclare(const VarDecl *VD,
     if (IPD->getParameterKind() == ImplicitParamKind::CXXThis ||
         IPD->getParameterKind() == ImplicitParamKind::ObjCSelf)
       Flags |= llvm::DINode::FlagObjectPointer;
+  } else if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
+    if (PVD->isExplicitObjectParameter())
+      Flags |= llvm::DINode::FlagObjectPointer;
   }
 
   // Note: Older versions of clang used to emit byval references with an extra
@@ -5115,7 +5127,7 @@ llvm::DIType *CGDebugInfo::CreateSelfType(const QualType &QualTy,
   llvm::DIType *CachedTy = getTypeOrNull(QualTy);
   if (CachedTy)
     Ty = CachedTy;
-  return DBuilder.createObjectPointerType(Ty);
+  return DBuilder.createObjectPointerType(Ty, /*Implicit=*/true);
 }
 
 void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable(
diff --git a/clang/test/CodeGenCXX/debug-info-object-pointer.cpp b/clang/test/CodeGenCXX/debug-info-object-pointer.cpp
new file mode 100644
index 00000000000000..49079f59909968
--- /dev/null
+++ b/clang/test/CodeGenCXX/debug-info-object-pointer.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -x c++ -std=c++23 -debug-info-kind=limited -emit-llvm < %s | FileCheck %s
+
+// CHECK: !DISubprogram(name: "bar",
+// CHECK-SAME:          flags: DIFlagPrototyped
+// CHECK: !DIDerivedType(tag: DW_TAG_pointer_type
+// CHECK-SAME:           flags: DIFlagArtificial | DIFlagObjectPointer
+//
+// CHECK: !DISubprogram(name: "explicit_this",
+//                      flags: DIFlagPrototyped
+//
+// CHECK: !DIDerivedType(tag: DW_TAG_rvalue_reference_type
+// CHECK-SAME:           flags: DIFlagObjectPointer)
+//
+// CHECK: !DILocalVariable(name: "this", arg: 1
+// CHECK-SAME:             flags: DIFlagArtificial | DIFlagObjectPointer
+//
+// CHECK-NOT: DIFlagArtificial
+// CHECK: !DILocalVariable(arg: 1, {{.*}}, flags: DIFlagObjectPointer)
+
+struct Foo {
+  void bar() {}
+  void explicit_this(this Foo &&) {}
+};
+
+void f() {
+  Foo{}.bar();
+  Foo{}.explicit_this();
+}
diff --git a/llvm/include/llvm-c/DebugInfo.h b/llvm/include/llvm-c/DebugInfo.h
index 07f87d44088e7e..ac7ee5a7cc9a19 100644
--- a/llvm/include/llvm-c/DebugInfo.h
+++ b/llvm/include/llvm-c/DebugInfo.h
@@ -870,13 +870,16 @@ LLVMDIBuilderCreateObjCProperty(LLVMDIBuilderRef Builder,
                                 LLVMMetadataRef Ty);
 
 /**
- * Create a uniqued DIType* clone with FlagObjectPointer and FlagArtificial set.
+ * Create a uniqued DIType* clone with FlagObjectPointer. If \c Implicit
+ * is true, then also set FlagArtificial.
  * \param Builder   The DIBuilder.
  * \param Type      The underlying type to which this pointer points.
+ * \param Implicit  Indicates whether this pointer was implicitly generated
+ *                  (i.e., not spelled out in source).
  */
-LLVMMetadataRef
-LLVMDIBuilderCreateObjectPointerType(LLVMDIBuilderRef Builder,
-                                     LLVMMetadataRef Type);
+LLVMMetadataRef LLVMDIBuilderCreateObjectPointerType(LLVMDIBuilderRef Builder,
+                                                     LLVMMetadataRef Type,
+                                                     LLVMBool Implicit);
 
 /**
  * Create debugging information entry for a qualified
diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index cb1150c269a1da..6c479415b9ed27 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -662,9 +662,9 @@ namespace llvm {
     /// Create a uniqued clone of \p Ty with FlagArtificial set.
     static DIType *createArtificialType(DIType *Ty);
 
-    /// Create a uniqued clone of \p Ty with FlagObjectPointer and
-    /// FlagArtificial set.
-    static DIType *createObjectPointerType(DIType *Ty);
+    /// Create a uniqued clone of \p Ty with FlagObjectPointer set.
+    /// If \p Implicit is true, also set FlagArtificial.
+    static DIType *createObjectPointerType(DIType *Ty, bool Implicit);
 
     /// Create a permanent forward-declared type.
     DICompositeType *createForwardDecl(unsigned Tag, StringRef Name,
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index b240a2a39de362..d9bd4f11e89a39 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -644,11 +644,15 @@ DIType *DIBuilder::createArtificialType(DIType *Ty) {
   return createTypeWithFlags(Ty, DINode::FlagArtificial);
 }
 
-DIType *DIBuilder::createObjectPointerType(DIType *Ty) {
+DIType *DIBuilder::createObjectPointerType(DIType *Ty, bool Implicit) {
   // FIXME: Restrict this to the nodes where it's valid.
   if (Ty->isObjectPointer())
     return Ty;
-  DINode::DIFlags Flags = DINode::FlagObjectPointer | DINode::FlagArtificial;
+  DINode::DIFlags Flags = DINode::FlagObjectPointer;
+
+  if (Implicit)
+    Flags |= DINode::FlagArtificial;
+
   return createTypeWithFlags(Ty, Flags);
 }
 
diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index e5b45e0082a823..4ce518009bd3ea 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -1432,10 +1432,11 @@ LLVMDIBuilderCreateObjCProperty(LLVMDIBuilderRef Builder,
                   PropertyAttributes, unwrapDI<DIType>(Ty)));
 }
 
-LLVMMetadataRef
-LLVMDIBuilderCreateObjectPointerType(LLVMDIBuilderRef Builder,
-                                     LLVMMetadataRef Type) {
-  return wrap(unwrap(Builder)->createObjectPointerType(unwrapDI<DIType>(Type)));
+LLVMMetadataRef LLVMDIBuilderCreateObjectPointerType(LLVMDIBuilderRef Builder,
+                                                     LLVMMetadataRef Type,
+                                                     LLVMBool Implicit) {
+  return wrap(unwrap(Builder)->createObjectPointerType(unwrapDI<DIType>(Type),
+                                                       Implicit));
 }
 
 LLVMMetadataRef



More information about the llvm-commits mailing list