[clang] [Clang] Fix preserved C++ field indices (PR #226790)

via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 27 07:27:49 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: Tamir Duberstein (tamird)

<details>
<summary>Changes</summary>

This separates the Clang producer issue identified while reviewing #<!-- -->226717 from BTF’s handling of C++ record elements. The intrinsic contract is unchanged, and the producer correction can be reviewed independently of the backend changes.

Assisted-by: OpenAI Codex


---
Full diff: https://github.com/llvm/llvm-project/pull/226790.diff


5 Files Affected:

- (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+16-5) 
- (modified) clang/lib/CodeGen/CGDebugInfo.h (+6-2) 
- (modified) clang/lib/CodeGen/CGExpr.cpp (+13-33) 
- (modified) clang/lib/CodeGen/CodeGenFunction.h (-3) 
- (added) clang/test/CodeGenCXX/builtin-preserve-access-index.cpp (+68) 


``````````diff
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index b6418753a6a65..42fc602ac1a8c 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2108,6 +2108,7 @@ void CGDebugInfo::CollectRecordLambdaFields(
 
     llvm::DIFile *VUnit = getOrCreateFile(Loc);
 
+    FieldIndexCache[*Field] = elements.size();
     elements.push_back(createFieldType(
         GetLambdaCaptureName(Capture), Field->getType(), Loc,
         Field->getAccess(), FieldOffset, Align, VUnit, RecordTy, CXXDecl));
@@ -2228,6 +2229,7 @@ void CGDebugInfo::CollectRecordNormalField(
                         OffsetInBits, Align, tunit, RecordTy, RD, Annotations);
   }
 
+  FieldIndexCache[field] = elements.size();
   elements.push_back(FieldType);
 }
 
@@ -3087,11 +3089,20 @@ void CGDebugInfo::CollectVTableInfo(const CXXRecordDecl *RD, llvm::DIFile *Unit,
   EltTys.push_back(VPtrMember);
 }
 
-llvm::DIType *CGDebugInfo::getOrCreateRecordType(QualType RTy,
-                                                 SourceLocation Loc) {
-  assert(CGM.getCodeGenOpts().hasReducedDebugInfo());
-  llvm::DIType *T = getOrCreateType(RTy, getOrCreateFile(Loc));
-  return T;
+std::pair<llvm::DIType *, unsigned>
+CGDebugInfo::getOrCreateRecordField(QualType Ty, const FieldDecl *Field) {
+  const RecordDecl *RD = Field->getParent();
+  // Preserve-access intrinsics need complete layouts, including base classes.
+  if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+    CXXRD->forallBases([this](const CXXRecordDecl *Base) {
+      completeClass(Base);
+      return true;
+    });
+  completeClass(RD);
+  llvm::DIType *T = getOrCreateStandaloneType(Ty, RD->getLocation());
+  auto I = FieldIndexCache.find(Field);
+  assert(I != FieldIndexCache.end() && "Missing field debug information");
+  return {T, I->second};
 }
 
 llvm::DIType *CGDebugInfo::getOrCreateInterfaceType(QualType D,
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 8a46e3f0e60bb..fca587517a035 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -103,6 +103,9 @@ class CGDebugInfo {
   /// Cache of previously constructed Types.
   llvm::DenseMap<const void *, llvm::TrackingMDRef> TypeCache;
 
+  /// DI element indices used by preserve-access intrinsics.
+  llvm::DenseMap<const FieldDecl *, unsigned> FieldIndexCache;
+
   /// Cache that maps VLA types to size expressions for that type,
   /// represented by instantiated Metadata nodes.
   llvm::SmallDenseMap<QualType, llvm::Metadata *> SizeExprCache;
@@ -623,8 +626,9 @@ class CGDebugInfo {
   /// Emit C++ namespace alias.
   llvm::DIImportedEntity *EmitNamespaceAlias(const NamespaceAliasDecl &NA);
 
-  /// Emit record type's standalone debug info.
-  llvm::DIType *getOrCreateRecordType(QualType Ty, SourceLocation L);
+  /// Emit complete record debug info and return the field's element index.
+  std::pair<llvm::DIType *, unsigned>
+  getOrCreateRecordField(QualType Ty, const FieldDecl *Field);
 
   /// Emit an Objective-C interface type standalone debug info.
   llvm::DIType *getOrCreateInterfaceType(QualType Ty, SourceLocation Loc);
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 4a481c01f6a68..fe90ae0af73c2 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5851,23 +5851,6 @@ LValue CodeGenFunction::EmitLValueForLambdaField(const FieldDecl *Field) {
   return EmitLValueForLambdaField(Field, CXXABIThisValue);
 }
 
-/// Get the field index in the debug info. The debug info structure/union
-/// will ignore the unnamed bitfields.
-unsigned CodeGenFunction::getDebugInfoFIndex(const RecordDecl *Rec,
-                                             unsigned FieldIndex) {
-  unsigned I = 0, Skipped = 0;
-
-  for (auto *F : Rec->getDefinition()->fields()) {
-    if (I == FieldIndex)
-      break;
-    if (F->isUnnamedBitField())
-      Skipped++;
-    I++;
-  }
-
-  return FieldIndex - Skipped;
-}
-
 /// Get the address of a zero-sized field within a record. The resulting
 /// address doesn't necessarily have the right type.
 static Address emitAddrOfZeroSizeField(CodeGenFunction &CGF, Address Base,
@@ -5930,14 +5913,14 @@ static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base,
 static Address emitPreserveStructAccess(CodeGenFunction &CGF, LValue base,
                                         Address addr, const FieldDecl *field) {
   const RecordDecl *rec = field->getParent();
-  llvm::DIType *DbgInfo = CGF.getDebugInfo()->getOrCreateStandaloneType(
-      base.getType(), rec->getLocation());
+  auto [DbgInfo, DIIndex] =
+      CGF.getDebugInfo()->getOrCreateRecordField(base.getType(), field);
 
   unsigned idx =
       CGF.CGM.getTypes().getCGRecordLayout(rec).getLLVMFieldNo(field);
 
-  return CGF.Builder.CreatePreserveStructAccessIndex(
-      addr, idx, CGF.getDebugInfoFIndex(rec, field->getFieldIndex()), DbgInfo);
+  return CGF.Builder.CreatePreserveStructAccessIndex(addr, idx, DIIndex,
+                                                     DbgInfo);
 }
 
 static bool hasAnyVptr(const QualType Type, const ASTContext &Context) {
@@ -5989,11 +5972,10 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, const FieldDecl *field,
             Addr = Builder.CreateStructGEP(Addr, Idx, field->getName());
         }
       } else {
-        llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateRecordType(
-            getContext().getCanonicalTagType(rec), rec->getLocation());
-        Addr = Builder.CreatePreserveStructAccessIndex(
-            Addr, Idx, getDebugInfoFIndex(rec, field->getFieldIndex()),
-            DbgInfo);
+        auto [DbgInfo, DIIndex] = getDebugInfo()->getOrCreateRecordField(
+            getContext().getCanonicalTagType(rec), field);
+        Addr = Builder.CreatePreserveStructAccessIndex(Addr, Idx, DIIndex,
+                                                       DbgInfo);
       }
     }
     const unsigned SS =
@@ -6069,13 +6051,11 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, const FieldDecl *field,
     if (IsInPreservedAIRegion ||
         (getDebugInfo() && rec->hasAttr<BPFPreserveAccessIndexAttr>())) {
       // Remember the original union field index
-      llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateStandaloneType(base.getType(),
-          rec->getLocation());
-      addr =
-          Address(Builder.CreatePreserveUnionAccessIndex(
-                      addr.emitRawPointer(*this),
-                      getDebugInfoFIndex(rec, field->getFieldIndex()), DbgInfo),
-                  addr.getElementType(), addr.getAlignment());
+      auto [DbgInfo, DIIndex] =
+          getDebugInfo()->getOrCreateRecordField(base.getType(), field);
+      addr = Address(Builder.CreatePreserveUnionAccessIndex(
+                         addr.emitRawPointer(*this), DIIndex, DbgInfo),
+                     addr.getElementType(), addr.getAlignment());
     }
 
     if (FieldType->isReferenceType())
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 653e883012229..fe0704e517a95 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3491,9 +3491,6 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// Converts Location to a DebugLoc, if debug information is enabled.
   llvm::DebugLoc SourceLocToDebugLoc(SourceLocation Location);
 
-  /// Get the record field index as represented in debug info.
-  unsigned getDebugInfoFIndex(const RecordDecl *Rec, unsigned FieldIndex);
-
   //===--------------------------------------------------------------------===//
   //                            Declaration Emission
   //===--------------------------------------------------------------------===//
diff --git a/clang/test/CodeGenCXX/builtin-preserve-access-index.cpp b/clang/test/CodeGenCXX/builtin-preserve-access-index.cpp
new file mode 100644
index 0000000000000..2a8fbb1562f8a
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-preserve-access-index.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -triple bpfel -emit-llvm -debug-info-kind=limited -disable-llvm-passes %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64 -emit-llvm -debug-info-kind=limited -disable-llvm-passes %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple bpfel -emit-llvm -debug-info-kind=constructor -disable-llvm-passes %s -o - | FileCheck %s
+
+struct Base {
+  Base();
+  int base;
+};
+struct Record : Base {
+  static int first;
+  int field;
+  static int second;
+  unsigned bits : 3;
+  void method();
+};
+union Union {
+  int first;
+  static int member;
+  long second;
+};
+struct Dynamic {
+  virtual void method();
+  int field;
+};
+
+int *field(Record *p) {
+  return __builtin_preserve_access_index(&p->field);
+}
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0({{.*}}, i32 1, i32 2), {{.*}}!llvm.preserve.access.index ![[RECORD:[0-9]+]]
+
+unsigned bits(Record *p) {
+  return __builtin_preserve_access_index(p->bits);
+}
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0({{.*}}, i32 2, i32 4), {{.*}}!llvm.preserve.access.index ![[RECORD]]
+
+long *member(Union *p) {
+  return __builtin_preserve_access_index(&p->second);
+}
+// CHECK: call ptr @llvm.preserve.union.access.index.p0.p0({{.*}}, i32 2), {{.*}}!llvm.preserve.access.index ![[UNION:[0-9]+]]
+
+int *dynamic(Dynamic *p) {
+  return __builtin_preserve_access_index(&p->field);
+}
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0({{.*}}, i32 1, i32 1), {{.*}}!llvm.preserve.access.index ![[DYNAMIC:[0-9]+]]
+
+int lambda(int a, int b) {
+  return [a, b] { return __builtin_preserve_access_index(b); }();
+}
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0({{.*}}, i32 1, i32 1), {{.*}}!llvm.preserve.access.index ![[LAMBDA:[0-9]+]]
+
+// The intrinsic indices refer to the complete DI element lists, including
+// bases, static members, and the vtable pointer before the accessed fields.
+// CHECK-DAG: ![[RECORD]] = distinct !DICompositeType({{.*}}name: "Record", {{.*}}elements: ![[RECORD_ELEMENTS:[0-9]+]]
+// CHECK-DAG: ![[RECORD_ELEMENTS]] = !{!{{[0-9]+}}, !{{[0-9]+}}, ![[FIELD:[0-9]+]], !{{[0-9]+}}, ![[BITS:[0-9]+]], !{{[0-9]+}}}
+// CHECK-DAG: ![[FIELD]] = !DIDerivedType(tag: DW_TAG_member, name: "field"
+// CHECK-DAG: ![[BITS]] = !DIDerivedType(tag: DW_TAG_member, name: "bits"
+// CHECK-DAG: ![[UNION]] = distinct !DICompositeType({{.*}}name: "Union", {{.*}}elements: ![[UNION_ELEMENTS:[0-9]+]]
+// CHECK-DAG: ![[UNION_ELEMENTS]] = !{!{{[0-9]+}}, !{{[0-9]+}}, ![[SECOND:[0-9]+]]}
+// CHECK-DAG: ![[SECOND]] = !DIDerivedType(tag: DW_TAG_member, name: "second"
+// CHECK-DAG: ![[DYNAMIC]] = distinct !DICompositeType({{.*}}name: "Dynamic", {{.*}}elements: ![[DYNAMIC_ELEMENTS:[0-9]+]]
+// CHECK-DAG: ![[DYNAMIC_ELEMENTS]] = !{!{{[0-9]+}}, ![[DYNAMIC_FIELD:[0-9]+]], !{{[0-9]+}}}
+// CHECK-DAG: ![[DYNAMIC_FIELD]] = !DIDerivedType(tag: DW_TAG_member, name: "field"
+// CHECK-DAG: ![[LAMBDA]] = distinct !DICompositeType({{.*}}elements: ![[LAMBDA_ELEMENTS:[0-9]+]]
+// CHECK-DAG: ![[LAMBDA_ELEMENTS]] = !{!{{[0-9]+}}, ![[CAPTURE:[0-9]+]]}
+// CHECK-DAG: ![[CAPTURE]] = !DIDerivedType(tag: DW_TAG_member, name: "b"
+// CHECK-DAG: !DICompositeType({{.*}}name: "Base", {{.*}}elements: ![[BASE_ELEMENTS:[0-9]+]]
+// CHECK-DAG: ![[BASE_ELEMENTS]] = !{![[BASE_FIELD:[0-9]+]], !{{[0-9]+}}}
+// CHECK-DAG: ![[BASE_FIELD]] = !DIDerivedType(tag: DW_TAG_member, name: "base"

``````````

</details>


https://github.com/llvm/llvm-project/pull/226790


More information about the cfe-commits mailing list