[clang] [Clang] Fix preserved C++ field indices (PR #226790)
Tamir Duberstein via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 27 07:27:05 PDT 2026
https://github.com/tamird created https://github.com/llvm/llvm-project/pull/226790
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
>From efd179e7017e6a51db46fb5b8ecf48f27b51ef30 Mon Sep 17 00:00:00 2001
From: Tamir Duberstein <tamird at gmail.com>
Date: Sun, 27 Sep 2026 09:05:08 -0400
Subject: [PATCH] [Clang] Fix preserved C++ field indices
Since 048493f88, preserved field accesses have used source field ordinals
as debug-info indices. 4754814c5 accounted for omitted unnamed bitfields,
but C++ debug info also contains bases, static members and vtable
pointers. For "struct S : Base { int x, y; }", the index for y therefore
identifies x. BPF CO-RE uses that index to select the field and its offset.
Record each field's index while emitting its debug info and return it
with the record type. Complete the record and its bases for these
accesses: limited and constructor debug modes can otherwise leave only
forward declarations, without the layout needed for relocation.
Assisted-by: OpenAI Codex
---
clang/lib/CodeGen/CGDebugInfo.cpp | 21 ++++--
clang/lib/CodeGen/CGDebugInfo.h | 8 ++-
clang/lib/CodeGen/CGExpr.cpp | 46 ++++---------
clang/lib/CodeGen/CodeGenFunction.h | 3 -
.../builtin-preserve-access-index.cpp | 68 +++++++++++++++++++
5 files changed, 103 insertions(+), 43 deletions(-)
create mode 100644 clang/test/CodeGenCXX/builtin-preserve-access-index.cpp
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"
More information about the cfe-commits
mailing list