[llvm] Piotrekjeremicz/diproperty node (PR #215776)
Piotr Jeremicz via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 12 04:23:33 PDT 2026
https://github.com/piotrekjeremicz created https://github.com/llvm/llvm-project/pull/215776
None
>From 7f59a5354e50fb02c96162f90513e024b0ea5bfa Mon Sep 17 00:00:00 2001
From: Augusto Noronha <anoronha at apple.com>
Date: Tue, 28 Jul 2026 13:07:58 -0700
Subject: [PATCH 1/6] [DebugInfo] Draft implementation of DW_TAG_property
---
llvm/docs/LangRef.md | 4 +
llvm/include/llvm-c/DebugInfo.h | 1 +
llvm/include/llvm/BinaryFormat/Dwarf.def | 15 ++++
llvm/include/llvm/Bitcode/LLVMBitCodes.h | 1 +
llvm/include/llvm/IR/DIBuilder.h | 13 +++
llvm/include/llvm/IR/DebugInfoMetadata.h | 85 +++++++++++++++++++
llvm/include/llvm/IR/Metadata.def | 1 +
llvm/lib/AsmParser/LLParser.cpp | 18 ++++
llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 5 ++
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 8 ++
llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 37 ++++++++
llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h | 2 +
llvm/lib/IR/AsmWriter.cpp | 5 ++
llvm/lib/IR/DIBuilder.cpp | 6 ++
llvm/lib/IR/DebugInfo.cpp | 2 +
llvm/lib/IR/DebugInfoMetadata.cpp | 15 ++++
llvm/lib/IR/LLVMContextImpl.h | 26 ++++++
llvm/lib/IR/Verifier.cpp | 20 +++++
.../DirectX/DXILWriter/DXILBitcodeWriter.cpp | 4 +
llvm/test/Assembler/diproperty.ll | 20 +++++
llvm/test/DebugInfo/Generic/property.ll | 59 +++++++++++++
llvm/test/Verifier/diproperty.ll | 18 ++++
llvm/unittests/IR/MetadataTest.cpp | 41 +++++++++
23 files changed, 406 insertions(+)
create mode 100644 llvm/test/Assembler/diproperty.ll
create mode 100644 llvm/test/DebugInfo/Generic/property.ll
create mode 100644 llvm/test/Verifier/diproperty.ll
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index f2d19e4b6280c..dde58daa82bbe 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -7253,6 +7253,10 @@ be used for the structure type.
getter: "getFoo", attributes: 7, type: !2)
```
+##### DIProperty
+
+TODO: write this.
+
##### DIImportedEntity
`DIImportedEntity` nodes represent entities (such as modules) imported into a
diff --git a/llvm/include/llvm-c/DebugInfo.h b/llvm/include/llvm-c/DebugInfo.h
index 155cbe32ee15e..2f7afa3f16aba 100644
--- a/llvm/include/llvm-c/DebugInfo.h
+++ b/llvm/include/llvm-c/DebugInfo.h
@@ -211,6 +211,7 @@ enum {
LLVMDIAssignIDMetadataKind,
LLVMDISubrangeTypeMetadataKind,
LLVMDIFixedPointTypeMetadataKind,
+ LLVMDIPropertyMetadataKind,
};
typedef unsigned LLVMMetadataKind;
diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def
index 32195aae562c3..9d80fb8f42573 100644
--- a/llvm/include/llvm/BinaryFormat/Dwarf.def
+++ b/llvm/include/llvm/BinaryFormat/Dwarf.def
@@ -237,6 +237,11 @@ HANDLE_DW_TAG(0x0048, call_site, 5, DWARF, DW_KIND_NONE)
HANDLE_DW_TAG(0x0049, call_site_parameter, 5, DWARF, DW_KIND_NONE)
HANDLE_DW_TAG(0x004a, skeleton_unit, 5, DWARF, DW_KIND_NONE)
HANDLE_DW_TAG(0x004b, immutable_type, 5, DWARF, DW_KIND_TYPE)
+// New in DWARF v6:
+HANDLE_DW_TAG(0x004c, property, 6, DWARF, DW_KIND_NONE)
+HANDLE_DW_TAG(0x004d, property_getter, 6, DWARF, DW_KIND_NONE)
+HANDLE_DW_TAG(0x004e, property_setter, 6, DWARF, DW_KIND_NONE)
+HANDLE_DW_TAG(0x004f, property_stored, 6, DWARF, DW_KIND_NONE)
// Vendor extensions:
HANDLE_DW_TAG(0x4081, MIPS_loop, 0, MIPS, DW_KIND_NONE)
// Conflicting:
@@ -432,6 +437,16 @@ HANDLE_DW_AT(0x8c, loclists_base, 5, DWARF)
// New in Dwarf v6:
HANDLE_DW_AT(0x90, language_name, 6, DWARF)
HANDLE_DW_AT(0x91, language_version, 6, DWARF)
+// TODO: DWARF v6 adds DW_AT_property_forward, used by the DW_TAG_property_getter
+// / _setter / _stored entries to refer to whatever implements the accessor. Add
+// it here, in numeric order with the entries above.
+//
+// Get its code and form class from Table 8.5 of the DWARF 6 draft rather than
+// from any summary of the proposal. Earlier write-ups of this proposal
+// circulated a different code, which the committee did not end up using -- and
+// picking the wrong one produces object files that every consumer misreads,
+// which no test in this tree would catch. Verifying a code against the actual
+// spec table is the habit worth building here.
// Vendor extensions:
HANDLE_DW_AT(0x806, GHS_namespace_alias, 0, GHS)
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 358f9a65a80af..a937a4ca6a9ab 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -400,6 +400,7 @@ enum MetadataCodes {
METADATA_ASSIGN_ID = 47, // [distinct, ...]
METADATA_SUBRANGE_TYPE = 48, // [distinct, ...]
METADATA_FIXED_POINT_TYPE = 49, // [distinct, ...]
+ METADATA_PROPERTY = 50, // [distinct, name, file, line, type, getterForward]
};
// The constants block (CONSTANTS_BLOCK_ID) describes emission for each
diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index 3acb9c5f955e8..de2bc2f311a24 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -554,6 +554,19 @@ namespace llvm {
StringRef GetterName, StringRef SetterName,
unsigned PropertyAttributes, DIType *Ty);
+ /// Create debugging information entry for a property, i.e. an entity that
+ /// is accessed like a data member but whose access is implemented by an
+ /// accessor.
+ /// \param Name Property name.
+ /// \param File File where this property is defined.
+ /// \param LineNumber Line number.
+ /// \param Ty Type of the property.
+ /// \param GetterForward The data member the getter forwards to, holding the
+ /// property's backing storage.
+ LLVM_ABI DIProperty *createProperty(StringRef Name, DIFile *File,
+ unsigned LineNumber, DIType *Ty,
+ DIDerivedType *GetterForward);
+
/// Create debugging information entry for a class.
/// \param Scope Scope in which this class is defined.
/// \param Name class name.
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 846342bee6071..62573d59fb204 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -223,6 +223,7 @@ class DINode : public MDNode {
case DILocalVariableKind:
case DILabelKind:
case DIObjCPropertyKind:
+ case DIPropertyKind:
case DIImportedEntityKind:
case DIModuleKind:
case DIGenericSubrangeKind:
@@ -4425,6 +4426,90 @@ class DIObjCProperty : public DINode {
}
};
+/// A property of a class or structure.
+///
+/// Represents a DWARF \c DW_TAG_property: an entity that is syntactically
+/// accessed like a data member, but whose access is implemented by invoking a
+/// user-defined or compiler-generated accessor.
+///
+/// Currently only the getter is modelled, and it must forward to a data member
+/// holding the property's backing storage. This describes languages that
+/// synthesize a property over separate storage, such as a Swift property
+/// wrapper, where \c x is a generated getter over a stored member \c _x. A
+/// consumer can then read the storage directly instead of calling the getter.
+class DIProperty : public DINode {
+ friend class LLVMContextImpl;
+ friend class MDNode;
+
+ unsigned Line;
+
+ DIProperty(LLVMContext &C, StorageType Storage, unsigned Line,
+ ArrayRef<Metadata *> Ops);
+ ~DIProperty() = default;
+
+ static DIProperty *getImpl(LLVMContext &Context, StringRef Name, DIFile *File,
+ unsigned Line, DIType *Type, DINode *GetterForward,
+ StorageType Storage, bool ShouldCreate = true) {
+ return getImpl(Context, getCanonicalMDString(Context, Name), File, Line,
+ Type, GetterForward, Storage, ShouldCreate);
+ }
+ LLVM_ABI static DIProperty *getImpl(LLVMContext &Context, MDString *Name,
+ Metadata *File, unsigned Line,
+ Metadata *Type, Metadata *GetterForward,
+ StorageType Storage,
+ bool ShouldCreate = true);
+
+ TempDIProperty cloneImpl() const {
+ return getTemporary(getContext(), getName(), getFile(), getLine(),
+ getType(), getGetterForward());
+ }
+
+public:
+ DEFINE_MDNODE_GET(DIProperty,
+ (StringRef Name, DIFile *File, unsigned Line, DIType *Type,
+ DINode *GetterForward),
+ (Name, File, Line, Type, GetterForward))
+ DEFINE_MDNODE_GET(DIProperty,
+ (MDString * Name, Metadata *File, unsigned Line,
+ Metadata *Type, Metadata *GetterForward),
+ (Name, File, Line, Type, GetterForward))
+
+ TempDIProperty clone() const { return cloneImpl(); }
+
+ unsigned getLine() const { return Line; }
+ StringRef getName() const { return getStringOperand(0); }
+ DIFile *getFile() const { return cast_or_null<DIFile>(getRawFile()); }
+ DIType *getType() const { return cast_or_null<DIType>(getRawType()); }
+
+ /// The entity the getter forwards to, i.e. the target of
+ /// \c DW_AT_property_forward on this property's \c DW_TAG_property_getter
+ /// child. This is the data member holding the property's backing storage.
+ DINode *getGetterForward() const {
+ return cast_or_null<DINode>(getRawGetterForward());
+ }
+
+ StringRef getFilename() const {
+ if (auto *F = getFile())
+ return F->getFilename();
+ return "";
+ }
+
+ StringRef getDirectory() const {
+ if (auto *F = getFile())
+ return F->getDirectory();
+ return "";
+ }
+
+ MDString *getRawName() const { return getOperandAs<MDString>(0); }
+ Metadata *getRawFile() const { return getOperand(1); }
+ Metadata *getRawType() const { return getOperand(2); }
+ Metadata *getRawGetterForward() const { return getOperand(3); }
+
+ static bool classof(const Metadata *MD) {
+ return MD->getMetadataID() == DIPropertyKind;
+ }
+};
+
/// An imported module (C++ using directive or similar).
///
/// Uses the SubclassData32 Metadata slot.
diff --git a/llvm/include/llvm/IR/Metadata.def b/llvm/include/llvm/IR/Metadata.def
index 511bf48707f00..49404674f4e7b 100644
--- a/llvm/include/llvm/IR/Metadata.def
+++ b/llvm/include/llvm/IR/Metadata.def
@@ -110,6 +110,7 @@ HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIGlobalVariable)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DILocalVariable)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DILabel)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIObjCProperty)
+HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIProperty)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIImportedEntity)
HANDLE_SPECIALIZED_MDNODE_LEAF(DIAssignID)
HANDLE_SPECIALIZED_MDNODE_BRANCH(DIMacroNode)
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index edff818b3b152..20fc99cf1ae3a 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -6700,6 +6700,24 @@ bool LLParser::parseDIObjCProperty(MDNode *&Result, bool IsDistinct) {
return false;
}
+/// parseDIProperty:
+/// ::= !DIProperty(name: "x", file: !1, line: 7, type: !2,
+/// getterForward: !3)
+bool LLParser::parseDIProperty(MDNode *&Result, bool IsDistinct) {
+#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \
+ OPTIONAL(name, MDStringField, ); \
+ OPTIONAL(file, MDField, ); \
+ OPTIONAL(line, LineField, ); \
+ OPTIONAL(type, MDField, ); \
+ OPTIONAL(getterForward, MDField, );
+ PARSE_MD_FIELDS();
+#undef VISIT_MD_FIELDS
+
+ Result = GET_OR_DISTINCT(DIProperty, (Context, name.Val, file.Val, line.Val,
+ type.Val, getterForward.Val));
+ return false;
+}
+
/// parseDIImportedEntity:
/// ::= !DIImportedEntity(tag: DW_TAG_imported_module, scope: !0, entity: !1,
/// line: 7, name: "foo", elements: !2)
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 8b7beb1a8ff9e..00cdaa3c2cdea 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -996,6 +996,7 @@ MetadataLoader::MetadataLoaderImpl::lazyLoadModuleMetadataBlock() {
case bitc::METADATA_LABEL:
case bitc::METADATA_EXPRESSION:
case bitc::METADATA_OBJC_PROPERTY:
+ case bitc::METADATA_PROPERTY:
case bitc::METADATA_IMPORTED_ENTITY:
case bitc::METADATA_GLOBAL_VAR_EXPR:
case bitc::METADATA_GENERIC_SUBRANGE:
@@ -2416,6 +2417,10 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
NextMetadataNo++;
break;
}
+ case bitc::METADATA_PROPERTY: {
+ // TODO: implement this.
+ break;
+ }
case bitc::METADATA_IMPORTED_ENTITY: {
if (Record.size() < 6 || Record.size() > 8)
return error("Invalid DIImportedEntity record");
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 571336c217797..3988c14133ce6 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -410,6 +410,8 @@ class ModuleBitcodeWriter : public ModuleBitcodeWriterBase {
unsigned Abbrev);
void writeDIObjCProperty(const DIObjCProperty *N,
SmallVectorImpl<uint64_t> &Record, unsigned Abbrev);
+ void writeDIProperty(const DIProperty *N, SmallVectorImpl<uint64_t> &Record,
+ unsigned Abbrev);
void writeDIImportedEntity(const DIImportedEntity *N,
SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev);
@@ -2510,6 +2512,12 @@ void ModuleBitcodeWriter::writeDIObjCProperty(const DIObjCProperty *N,
Record.clear();
}
+void ModuleBitcodeWriter::writeDIProperty(const DIProperty *N,
+ SmallVectorImpl<uint64_t> &Record,
+ unsigned Abbrev) {
+ // TODO: implement this.
+}
+
void ModuleBitcodeWriter::writeDIImportedEntity(
const DIImportedEntity *N, SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev) {
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index 78c0769e49161..d63bd2e581229 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -510,6 +510,12 @@ void DwarfUnit::addSourceLine(DIE &Die, const DIObjCProperty *Ty) {
addSourceLine(Die, Ty->getLine(), /*Column*/ 0, Ty->getFile());
}
+void DwarfUnit::addSourceLine(DIE &Die, const DIProperty *P) {
+ assert(P);
+
+ addSourceLine(Die, P->getLine(), /*Column*/ 0, P->getFile());
+}
+
void DwarfUnit::addConstantFPValue(DIE &Die, const ConstantFP *CFP) {
// Pass this down to addConstantValue as an unsigned bag of bits.
addConstantValue(Die, CFP->getValueAPF().bitcastToAPInt(), true);
@@ -1145,6 +1151,8 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
if (unsigned PropertyAttributes = Property->getAttributes())
addUInt(ElemDie, dwarf::DW_AT_APPLE_property_attribute, std::nullopt,
PropertyAttributes);
+ } else if (auto *Property = dyn_cast<DIProperty>(Element)) {
+ constructPropertyDIE(Buffer, Property);
} else if (auto *Composite = dyn_cast<DICompositeType>(Element)) {
if (Composite->getTag() == dwarf::DW_TAG_variant_part) {
DIE &VariantPart = createAndAddDIE(Composite->getTag(), Buffer);
@@ -2012,6 +2020,35 @@ DIE &DwarfUnit::constructMemberDIE(DIE &Buffer, const DIDerivedType *DT) {
return MemberDie;
}
+void DwarfUnit::constructPropertyDIE(DIE &Buffer, const DIProperty *P) {
+ // TODO: Emit the property. The DIE tree this needs to produce is:
+ //
+ // DW_TAG_property
+ // DW_AT_name <- P->getName()
+ // DW_AT_type <- P->getType()
+ // DW_AT_decl_file/line <- addSourceLine() has an overload for this
+ // DW_TAG_property_getter (a child DIE, not an attribute)
+ // DW_AT_property_forward (a reference to P->getGetterForward())
+ //
+ // createAndAddDIE, addString, addType and addDIEEntry are the helpers you
+ // want. constructMemberDIE, just above, is a good model to follow -- note in
+ // particular how it takes the metadata node as createAndAddDIE's third
+ // argument, and what that buys you.
+ //
+ // Two questions to answer rather than guess at:
+ //
+ // - getGetterForward() hands you a DINode, but addDIEEntry needs a DIE. How
+ // do you get from one to the other? The DW_AT_APPLE_property code at the
+ // end of constructMemberDIE does the same lookup. What should happen when
+ // that lookup finds nothing -- is it better to emit the getter child
+ // anyway, or to leave it off?
+ //
+ // - test/DebugInfo/Generic/property.ll declares two classes, Foo and Bar,
+ // whose elements lists hold the same two nodes in opposite orders. Both
+ // must produce the same DWARF. If only one of them does, the fix is
+ // probably not inside this function.
+}
+
DIE *DwarfUnit::getOrCreateStaticMemberDIE(const DIDerivedType *DT) {
if (!DT)
return nullptr;
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
index 139fae5621940..1ed2625255d21 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
@@ -228,6 +228,7 @@ class DwarfUnit : public DIEUnit {
void addSourceLine(DIE &Die, const DILabel *L);
void addSourceLine(DIE &Die, const DIType *Ty);
void addSourceLine(DIE &Die, const DIObjCProperty *Ty);
+ void addSourceLine(DIE &Die, const DIProperty *P);
/// Add constant value entry in variable DIE.
void addConstantValue(DIE &Die, const ConstantInt *CI, const DIType *Ty);
@@ -389,6 +390,7 @@ class DwarfUnit : public DIEUnit {
void constructArrayTypeDIE(DIE &Buffer, const DICompositeType *CTy);
void constructEnumTypeDIE(DIE &Buffer, const DICompositeType *CTy);
DIE &constructMemberDIE(DIE &Buffer, const DIDerivedType *DT);
+ void constructPropertyDIE(DIE &Buffer, const DIProperty *P);
void constructTemplateTypeParameterDIE(DIE &Buffer,
const DITemplateTypeParameter *TP);
void constructTemplateValueParameterDIE(DIE &Buffer,
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index cad4f17b0db91..2a1ebb99f3351 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -2700,6 +2700,11 @@ static void writeDIObjCProperty(raw_ostream &Out, const DIObjCProperty *N,
Out << ")";
}
+static void writeDIProperty(raw_ostream &Out, const DIProperty *N,
+ AsmWriterContext &WriterCtx) {
+ // TODO: implement this
+}
+
static void writeDIImportedEntity(raw_ostream &Out, const DIImportedEntity *N,
AsmWriterContext &WriterCtx) {
Out << "!DIImportedEntity(";
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index fb18fc3cc90c5..6317db812dd02 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -572,6 +572,12 @@ DIBuilder::createObjCProperty(StringRef Name, DIFile *File, unsigned LineNumber,
SetterName, PropertyAttributes, Ty);
}
+DIProperty *DIBuilder::createProperty(StringRef Name, DIFile *File,
+ unsigned LineNumber, DIType *Ty,
+ DIDerivedType *GetterForward) {
+ return DIProperty::get(VMContext, Name, File, LineNumber, Ty, GetterForward);
+}
+
DITemplateTypeParameter *
DIBuilder::createTemplateTypeParameter(DIScope *Context, StringRef Name,
DIType *Ty, bool isDefault) {
diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index e164ec54ead60..a42707604e61f 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -273,6 +273,8 @@ void DebugInfoFinder::processType(DIType *DT) {
processType(T);
else if (auto *SP = dyn_cast<DISubprogram>(D))
processSubprogram(SP);
+ else if (auto *P = dyn_cast<DIProperty>(D))
+ processType(P->getType());
else if (auto *SR = dyn_cast_or_null<DISubrange>(D)) {
auto VisitBound = [&](DISubrange::BoundType Bound) {
if (auto *BV = dyn_cast_if_present<DIVariable *>(Bound))
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 88f7f2f6240e0..0f5422971ef76 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -2624,6 +2624,21 @@ DIObjCProperty *DIObjCProperty::getImpl(
DEFINE_GETIMPL_STORE(DIObjCProperty, (Line, Attributes), Ops);
}
+DIProperty::DIProperty(LLVMContext &C, StorageType Storage, unsigned Line,
+ ArrayRef<Metadata *> Ops)
+ : DINode(C, DIPropertyKind, Storage, dwarf::DW_TAG_property, Ops),
+ Line(Line) {}
+
+DIProperty *DIProperty::getImpl(LLVMContext &Context, MDString *Name,
+ Metadata *File, unsigned Line, Metadata *Type,
+ Metadata *GetterForward, StorageType Storage,
+ bool ShouldCreate) {
+ assert(isCanonical(Name) && "Expected canonical MDString");
+ DEFINE_GETIMPL_LOOKUP(DIProperty, (Name, File, Line, Type, GetterForward));
+ Metadata *Ops[] = {Name, File, Type, GetterForward};
+ DEFINE_GETIMPL_STORE(DIProperty, (Line), Ops);
+}
+
DIImportedEntity *DIImportedEntity::getImpl(LLVMContext &Context, unsigned Tag,
Metadata *Scope, Metadata *Entity,
Metadata *File, unsigned Line,
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 41c8a92c56eda..4848597cd11af 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -1396,6 +1396,32 @@ template <> struct MDNodeKeyImpl<DIObjCProperty> {
}
};
+template <> struct MDNodeKeyImpl<DIProperty> {
+ MDString *Name;
+ Metadata *File;
+ unsigned Line;
+ Metadata *Type;
+ Metadata *GetterForward;
+
+ MDNodeKeyImpl(MDString *Name, Metadata *File, unsigned Line, Metadata *Type,
+ Metadata *GetterForward)
+ : Name(Name), File(File), Line(Line), Type(Type),
+ GetterForward(GetterForward) {}
+ MDNodeKeyImpl(const DIProperty *N)
+ : Name(N->getRawName()), File(N->getRawFile()), Line(N->getLine()),
+ Type(N->getRawType()), GetterForward(N->getRawGetterForward()) {}
+
+ bool isKeyOf(const DIProperty *RHS) const {
+ return Name == RHS->getRawName() && File == RHS->getRawFile() &&
+ Line == RHS->getLine() && Type == RHS->getRawType() &&
+ GetterForward == RHS->getRawGetterForward();
+ }
+
+ unsigned getHashValue() const {
+ return hash_combine(Name, File, Line, Type, GetterForward);
+ }
+};
+
template <> struct MDNodeKeyImpl<DIImportedEntity> {
unsigned Tag;
Metadata *Scope;
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 407c7a9122d3c..5d004b5b52f30 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1770,6 +1770,26 @@ void Verifier::visitDIObjCProperty(const DIObjCProperty &N) {
CheckDI(isa<DIFile>(F), "invalid file", &N, F);
}
+void Verifier::visitDIProperty(const DIProperty &N) {
+ CheckDI(N.getTag() == dwarf::DW_TAG_property, "invalid tag", &N);
+ if (auto *T = N.getRawType())
+ CheckDI(isType(T), "invalid type ref", &N, T);
+ if (auto *F = N.getRawFile())
+ CheckDI(isa<DIFile>(F), "invalid file", &N, F);
+ // TODO: Check getterForward. It ends up as the DW_AT_property_forward of a
+ // DW_TAG_property_getter, and the only thing the backend currently knows how
+ // to point at is the data member holding the property's backing storage.
+ //
+ // Note the DWARF specification is deliberately looser than we need to be
+ // here: it also allows a property getter to forward to a subprogram, a
+ // variable or a constant. Restricting this is a choice about what we support
+ // today, not a transcription of the spec, so the check should be tight enough
+ // that a wrong node is caught early rather than turning into bad DWARF.
+ //
+ // test/Verifier/diproperty.ll has the cases that must be rejected, and the
+ // diagnostic they expect.
+}
+
void Verifier::visitDIImportedEntity(const DIImportedEntity &N) {
CheckDI(N.getTag() == dwarf::DW_TAG_imported_module ||
N.getTag() == dwarf::DW_TAG_imported_declaration,
diff --git a/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp b/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp
index 6f14f70014b01..99ea68b466206 100644
--- a/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp
+++ b/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp
@@ -325,6 +325,10 @@ class DXILBitcodeWriter {
}
void writeDIObjCProperty(const DIObjCProperty *N,
SmallVectorImpl<uint64_t> &Record, unsigned Abbrev);
+ void writeDIProperty(const DIProperty *N, SmallVectorImpl<uint64_t> &Record,
+ unsigned Abbrev) {
+ llvm_unreachable("DXIL cannot contain DIProperty Nodes");
+ }
void writeDIImportedEntity(const DIImportedEntity *N,
SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev);
diff --git a/llvm/test/Assembler/diproperty.ll b/llvm/test/Assembler/diproperty.ll
new file mode 100644
index 0000000000000..d24a8de1f3785
--- /dev/null
+++ b/llvm/test/Assembler/diproperty.ll
@@ -0,0 +1,20 @@
+; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s
+; RUN: verify-uselistorder %s
+
+; !5 and !6 are identical, so uniquing collapses them to a single node.
+; CHECK: !named = !{!0, !1, !2, !3, !5, !6, !6}
+!named = !{!0, !1, !2, !3, !4, !5, !6}
+
+!0 = distinct !{}
+!1 = !DIFile(filename: "path/to/file", directory: "/path/to/dir")
+!2 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!3 = !DICompositeType(tag: DW_TAG_class_type, name: "Foo", elements: !{!4, !5})
+
+; CHECK: !5 = !DIDerivedType(tag: DW_TAG_member, name: "_x", scope: !3, file: !1, line: 8, baseType: !2, size: 32)
+!4 = !DIDerivedType(tag: DW_TAG_member, name: "_x", scope: !3, file: !1,
+ line: 8, baseType: !2, size: 32)
+
+; CHECK-NEXT: !6 = !DIProperty(name: "x", file: !1, line: 8, type: !2, getterForward: !5)
+!5 = !DIProperty(name: "x", file: !1, line: 8, type: !2, getterForward: !4)
+
+!6 = !DIProperty(name: "x", file: !1, line: 8, type: !2, getterForward: !4)
diff --git a/llvm/test/DebugInfo/Generic/property.ll b/llvm/test/DebugInfo/Generic/property.ll
new file mode 100644
index 0000000000000..e7992531f1fbe
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/property.ll
@@ -0,0 +1,59 @@
+; UNSUPPORTED: target={{.*}}-aix{{.*}}
+;
+; RUN: llc -filetype=obj -o %t.o %s
+; RUN: llvm-dwarfdump --debug-info %t.o | FileCheck %s
+; RUN: llvm-dwarfdump --verify %t.o | FileCheck %s --check-prefix=VERIFY
+
+; VERIFY: No errors.
+
+; A property whose getter forwards to the data member holding its backing
+; storage. This models a Swift property wrapper, where `x` is a synthesized
+; getter over the stored member `_x`, so a consumer can read the storage
+; directly instead of calling the getter.
+;
+; "Foo" lists the member before the property, "Bar" lists the property before
+; the member. Emission must not depend on that order.
+
+; CHECK: DW_TAG_class_type
+; CHECK: DW_AT_name ("Foo")
+;
+; CHECK: 0x[[FOO_X:[0-9a-f]+]]: DW_TAG_member
+; CHECK: DW_AT_name ("_x")
+;
+; CHECK: DW_TAG_property
+; CHECK: DW_AT_name ("x")
+; CHECK: DW_AT_type {{.*}} "Int"
+; CHECK: DW_AT_decl_line (8)
+; CHECK: DW_TAG_property_getter
+; CHECK: DW_AT_property_forward (0x[[FOO_X]] "_x")
+
+; CHECK: DW_TAG_class_type
+; CHECK: DW_AT_name ("Bar")
+;
+; CHECK: 0x[[BAR_Y:[0-9a-f]+]]: DW_TAG_member
+; CHECK: DW_AT_name ("_y")
+;
+; CHECK: DW_TAG_property
+; CHECK: DW_AT_name ("y")
+; CHECK: DW_TAG_property_getter
+; CHECK: DW_AT_property_forward (0x[[BAR_Y]] "_y")
+
+!llvm.module.flags = !{!0, !1}
+!llvm.dbg.cu = !{!2}
+
+!0 = !{i32 7, !"Dwarf Version", i32 5}
+!1 = !{i32 2, !"Debug Info Version", i32 3}
+!2 = distinct !DICompileUnit(language: DW_LANG_Swift, file: !3, producer: "hand written", isOptimized: false, emissionKind: FullDebug, retainedTypes: !4)
+!3 = !DIFile(filename: "t.swift", directory: "/tmp")
+!4 = !{!5, !10}
+
+!5 = !DICompositeType(tag: DW_TAG_class_type, name: "Foo", scope: !3, file: !3, line: 7, size: 64, elements: !6)
+!6 = !{!7, !9}
+!7 = !DIDerivedType(tag: DW_TAG_member, name: "_x", scope: !5, file: !3, line: 8, baseType: !8, size: 64)
+!8 = !DIBasicType(name: "Int", size: 64, encoding: DW_ATE_signed)
+!9 = !DIProperty(name: "x", file: !3, line: 8, type: !8, getterForward: !7)
+
+!10 = !DICompositeType(tag: DW_TAG_class_type, name: "Bar", scope: !3, file: !3, line: 12, size: 64, elements: !11)
+!11 = !{!13, !12}
+!12 = !DIDerivedType(tag: DW_TAG_member, name: "_y", scope: !10, file: !3, line: 13, baseType: !8, size: 64)
+!13 = !DIProperty(name: "y", file: !3, line: 13, type: !8, getterForward: !12)
diff --git a/llvm/test/Verifier/diproperty.ll b/llvm/test/Verifier/diproperty.ll
new file mode 100644
index 0000000000000..64c56e86fb6b5
--- /dev/null
+++ b/llvm/test/Verifier/diproperty.ll
@@ -0,0 +1,18 @@
+; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s
+
+!named = !{!0, !1, !2, !3, !4}
+
+!0 = !DIFile(filename: "path/to/file", directory: "/path/to/dir")
+!1 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+; A getter must forward to the data member holding the backing storage.
+; CHECK: property getterForward must be a member
+!2 = !DIProperty(name: "x", file: !0, line: 8, type: !1, getterForward: !1)
+
+; CHECK: property getterForward must be a member
+!3 = !DIProperty(name: "x", file: !0, line: 8, type: !1, getterForward: !0)
+
+; A pointer is a DIDerivedType, but not a DW_TAG_member.
+; CHECK: property getterForward must be a member
+!4 = !DIProperty(name: "x", file: !0, line: 8, type: !1, getterForward: !5)
+!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !1, size: 64)
diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index 65398b5ca2b1f..93ad970a879f7 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -4840,6 +4840,47 @@ TEST_F(DIObjCPropertyTest, get) {
EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));
}
+typedef MetadataTest DIPropertyTest;
+
+TEST_F(DIPropertyTest, get) {
+ // The backing storage a property getter forwards to.
+ auto GetMember = [&](StringRef Name) {
+ return DIDerivedType::getDistinct(
+ Context, dwarf::DW_TAG_member, Name, nullptr, 0, nullptr,
+ getBasicType("basictype"), 8, 8, 0, std::nullopt, {}, DINode::FlagZero);
+ };
+
+ StringRef Name = "x";
+ DIFile *File = getFile();
+ unsigned Line = 5;
+ DIType *Type = getBasicType("basic");
+ DIDerivedType *GetterForward = GetMember("_x");
+
+ auto *N = DIProperty::get(Context, Name, File, Line, Type, GetterForward);
+
+ EXPECT_EQ(dwarf::DW_TAG_property, N->getTag());
+ EXPECT_EQ(Name, N->getName());
+ EXPECT_EQ(File, N->getFile());
+ EXPECT_EQ(Line, N->getLine());
+ EXPECT_EQ(Type, N->getType());
+ EXPECT_EQ(GetterForward, N->getGetterForward());
+ EXPECT_EQ(N, DIProperty::get(Context, Name, File, Line, Type, GetterForward));
+
+ EXPECT_NE(N,
+ DIProperty::get(Context, "other", File, Line, Type, GetterForward));
+ EXPECT_NE(
+ N, DIProperty::get(Context, Name, getFile(), Line, Type, GetterForward));
+ EXPECT_NE(
+ N, DIProperty::get(Context, Name, File, Line + 1, Type, GetterForward));
+ EXPECT_NE(N, DIProperty::get(Context, Name, File, Line, getBasicType("other"),
+ GetterForward));
+ EXPECT_NE(
+ N, DIProperty::get(Context, Name, File, Line, Type, GetMember("_other")));
+
+ TempDIProperty Temp = N->clone();
+ EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));
+}
+
typedef MetadataTest DIImportedEntityTest;
TEST_F(DIImportedEntityTest, get) {
>From 8541c49298febc43b69a2b640f1d0ecffab95544 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 10 Aug 2026 19:33:59 +0200
Subject: [PATCH 2/6] [DebugInfo] Rename DIProperty operand `GetterForward` to
`Getter`
---
llvm/include/llvm/Bitcode/LLVMBitCodes.h | 2 +-
llvm/include/llvm/IR/DIBuilder.h | 4 ++--
llvm/include/llvm/IR/DebugInfoMetadata.h | 22 +++++++++++-----------
llvm/lib/AsmParser/LLParser.cpp | 6 +++---
llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 4 ++--
llvm/lib/IR/DIBuilder.cpp | 4 ++--
llvm/lib/IR/DebugInfoMetadata.cpp | 6 +++---
llvm/lib/IR/LLVMContextImpl.h | 12 ++++++------
llvm/lib/IR/Verifier.cpp | 2 +-
llvm/test/Assembler/diproperty.ll | 6 +++---
llvm/test/DebugInfo/Generic/property.ll | 4 ++--
llvm/test/Verifier/diproperty.ll | 12 ++++++------
llvm/unittests/IR/MetadataTest.cpp | 16 ++++++++--------
13 files changed, 50 insertions(+), 50 deletions(-)
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index a937a4ca6a9ab..3c6a742d67fb3 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -400,7 +400,7 @@ enum MetadataCodes {
METADATA_ASSIGN_ID = 47, // [distinct, ...]
METADATA_SUBRANGE_TYPE = 48, // [distinct, ...]
METADATA_FIXED_POINT_TYPE = 49, // [distinct, ...]
- METADATA_PROPERTY = 50, // [distinct, name, file, line, type, getterForward]
+ METADATA_PROPERTY = 50, // [distinct, name, file, line, type, getter]
};
// The constants block (CONSTANTS_BLOCK_ID) describes emission for each
diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index de2bc2f311a24..1dbe153ffeaec 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -561,11 +561,11 @@ namespace llvm {
/// \param File File where this property is defined.
/// \param LineNumber Line number.
/// \param Ty Type of the property.
- /// \param GetterForward The data member the getter forwards to, holding the
+ /// \param Getter The data member the getter forwards to, holding the
/// property's backing storage.
LLVM_ABI DIProperty *createProperty(StringRef Name, DIFile *File,
unsigned LineNumber, DIType *Ty,
- DIDerivedType *GetterForward);
+ DIDerivedType *Getter);
/// Create debugging information entry for a class.
/// \param Scope Scope in which this class is defined.
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 62573d59fb204..cf6871cdf7405 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -4448,31 +4448,31 @@ class DIProperty : public DINode {
~DIProperty() = default;
static DIProperty *getImpl(LLVMContext &Context, StringRef Name, DIFile *File,
- unsigned Line, DIType *Type, DINode *GetterForward,
+ unsigned Line, DIType *Type, DINode *Getter,
StorageType Storage, bool ShouldCreate = true) {
return getImpl(Context, getCanonicalMDString(Context, Name), File, Line,
- Type, GetterForward, Storage, ShouldCreate);
+ Type, Getter, Storage, ShouldCreate);
}
LLVM_ABI static DIProperty *getImpl(LLVMContext &Context, MDString *Name,
Metadata *File, unsigned Line,
- Metadata *Type, Metadata *GetterForward,
+ Metadata *Type, Metadata *Getter,
StorageType Storage,
bool ShouldCreate = true);
TempDIProperty cloneImpl() const {
return getTemporary(getContext(), getName(), getFile(), getLine(),
- getType(), getGetterForward());
+ getType(), getGetter());
}
public:
DEFINE_MDNODE_GET(DIProperty,
(StringRef Name, DIFile *File, unsigned Line, DIType *Type,
- DINode *GetterForward),
- (Name, File, Line, Type, GetterForward))
+ DINode *Getter),
+ (Name, File, Line, Type, Getter))
DEFINE_MDNODE_GET(DIProperty,
(MDString * Name, Metadata *File, unsigned Line,
- Metadata *Type, Metadata *GetterForward),
- (Name, File, Line, Type, GetterForward))
+ Metadata *Type, Metadata *Getter),
+ (Name, File, Line, Type, Getter))
TempDIProperty clone() const { return cloneImpl(); }
@@ -4484,8 +4484,8 @@ class DIProperty : public DINode {
/// The entity the getter forwards to, i.e. the target of
/// \c DW_AT_property_forward on this property's \c DW_TAG_property_getter
/// child. This is the data member holding the property's backing storage.
- DINode *getGetterForward() const {
- return cast_or_null<DINode>(getRawGetterForward());
+ DINode *getGetter() const {
+ return cast_or_null<DINode>(getRawGetter());
}
StringRef getFilename() const {
@@ -4503,7 +4503,7 @@ class DIProperty : public DINode {
MDString *getRawName() const { return getOperandAs<MDString>(0); }
Metadata *getRawFile() const { return getOperand(1); }
Metadata *getRawType() const { return getOperand(2); }
- Metadata *getRawGetterForward() const { return getOperand(3); }
+ Metadata *getRawGetter() const { return getOperand(3); }
static bool classof(const Metadata *MD) {
return MD->getMetadataID() == DIPropertyKind;
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 20fc99cf1ae3a..468373c82ff82 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -6702,19 +6702,19 @@ bool LLParser::parseDIObjCProperty(MDNode *&Result, bool IsDistinct) {
/// parseDIProperty:
/// ::= !DIProperty(name: "x", file: !1, line: 7, type: !2,
-/// getterForward: !3)
+/// getter: !3)
bool LLParser::parseDIProperty(MDNode *&Result, bool IsDistinct) {
#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \
OPTIONAL(name, MDStringField, ); \
OPTIONAL(file, MDField, ); \
OPTIONAL(line, LineField, ); \
OPTIONAL(type, MDField, ); \
- OPTIONAL(getterForward, MDField, );
+ OPTIONAL(getter, MDField, );
PARSE_MD_FIELDS();
#undef VISIT_MD_FIELDS
Result = GET_OR_DISTINCT(DIProperty, (Context, name.Val, file.Val, line.Val,
- type.Val, getterForward.Val));
+ type.Val, getter.Val));
return false;
}
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index d63bd2e581229..c32ad6fd4a106 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -2028,7 +2028,7 @@ void DwarfUnit::constructPropertyDIE(DIE &Buffer, const DIProperty *P) {
// DW_AT_type <- P->getType()
// DW_AT_decl_file/line <- addSourceLine() has an overload for this
// DW_TAG_property_getter (a child DIE, not an attribute)
- // DW_AT_property_forward (a reference to P->getGetterForward())
+ // DW_AT_property_forward (a reference to P->getGetter())
//
// createAndAddDIE, addString, addType and addDIEEntry are the helpers you
// want. constructMemberDIE, just above, is a good model to follow -- note in
@@ -2037,7 +2037,7 @@ void DwarfUnit::constructPropertyDIE(DIE &Buffer, const DIProperty *P) {
//
// Two questions to answer rather than guess at:
//
- // - getGetterForward() hands you a DINode, but addDIEEntry needs a DIE. How
+ // - getGetter() hands you a DINode, but addDIEEntry needs a DIE. How
// do you get from one to the other? The DW_AT_APPLE_property code at the
// end of constructMemberDIE does the same lookup. What should happen when
// that lookup finds nothing -- is it better to emit the getter child
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index 6317db812dd02..44719331589bb 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -574,8 +574,8 @@ DIBuilder::createObjCProperty(StringRef Name, DIFile *File, unsigned LineNumber,
DIProperty *DIBuilder::createProperty(StringRef Name, DIFile *File,
unsigned LineNumber, DIType *Ty,
- DIDerivedType *GetterForward) {
- return DIProperty::get(VMContext, Name, File, LineNumber, Ty, GetterForward);
+ DIDerivedType *Getter) {
+ return DIProperty::get(VMContext, Name, File, LineNumber, Ty, Getter);
}
DITemplateTypeParameter *
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 0f5422971ef76..47b99613173b6 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -2631,11 +2631,11 @@ DIProperty::DIProperty(LLVMContext &C, StorageType Storage, unsigned Line,
DIProperty *DIProperty::getImpl(LLVMContext &Context, MDString *Name,
Metadata *File, unsigned Line, Metadata *Type,
- Metadata *GetterForward, StorageType Storage,
+ Metadata *Getter, StorageType Storage,
bool ShouldCreate) {
assert(isCanonical(Name) && "Expected canonical MDString");
- DEFINE_GETIMPL_LOOKUP(DIProperty, (Name, File, Line, Type, GetterForward));
- Metadata *Ops[] = {Name, File, Type, GetterForward};
+ DEFINE_GETIMPL_LOOKUP(DIProperty, (Name, File, Line, Type, Getter));
+ Metadata *Ops[] = {Name, File, Type, Getter};
DEFINE_GETIMPL_STORE(DIProperty, (Line), Ops);
}
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 4848597cd11af..9914f98186457 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -1401,24 +1401,24 @@ template <> struct MDNodeKeyImpl<DIProperty> {
Metadata *File;
unsigned Line;
Metadata *Type;
- Metadata *GetterForward;
+ Metadata *Getter;
MDNodeKeyImpl(MDString *Name, Metadata *File, unsigned Line, Metadata *Type,
- Metadata *GetterForward)
+ Metadata *Getter)
: Name(Name), File(File), Line(Line), Type(Type),
- GetterForward(GetterForward) {}
+ Getter(Getter) {}
MDNodeKeyImpl(const DIProperty *N)
: Name(N->getRawName()), File(N->getRawFile()), Line(N->getLine()),
- Type(N->getRawType()), GetterForward(N->getRawGetterForward()) {}
+ Type(N->getRawType()), Getter(N->getRawGetter()) {}
bool isKeyOf(const DIProperty *RHS) const {
return Name == RHS->getRawName() && File == RHS->getRawFile() &&
Line == RHS->getLine() && Type == RHS->getRawType() &&
- GetterForward == RHS->getRawGetterForward();
+ Getter == RHS->getRawGetter();
}
unsigned getHashValue() const {
- return hash_combine(Name, File, Line, Type, GetterForward);
+ return hash_combine(Name, File, Line, Type, Getter);
}
};
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 5d004b5b52f30..847023aa8b4ad 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1776,7 +1776,7 @@ void Verifier::visitDIProperty(const DIProperty &N) {
CheckDI(isType(T), "invalid type ref", &N, T);
if (auto *F = N.getRawFile())
CheckDI(isa<DIFile>(F), "invalid file", &N, F);
- // TODO: Check getterForward. It ends up as the DW_AT_property_forward of a
+ // TODO: Check getter. It ends up as the DW_AT_property_forward of a
// DW_TAG_property_getter, and the only thing the backend currently knows how
// to point at is the data member holding the property's backing storage.
//
diff --git a/llvm/test/Assembler/diproperty.ll b/llvm/test/Assembler/diproperty.ll
index d24a8de1f3785..fe42ac5953a44 100644
--- a/llvm/test/Assembler/diproperty.ll
+++ b/llvm/test/Assembler/diproperty.ll
@@ -14,7 +14,7 @@
!4 = !DIDerivedType(tag: DW_TAG_member, name: "_x", scope: !3, file: !1,
line: 8, baseType: !2, size: 32)
-; CHECK-NEXT: !6 = !DIProperty(name: "x", file: !1, line: 8, type: !2, getterForward: !5)
-!5 = !DIProperty(name: "x", file: !1, line: 8, type: !2, getterForward: !4)
+; CHECK-NEXT: !6 = !DIProperty(name: "x", file: !1, line: 8, type: !2, getter: !5)
+!5 = !DIProperty(name: "x", file: !1, line: 8, type: !2, getter: !4)
-!6 = !DIProperty(name: "x", file: !1, line: 8, type: !2, getterForward: !4)
+!6 = !DIProperty(name: "x", file: !1, line: 8, type: !2, getter: !4)
diff --git a/llvm/test/DebugInfo/Generic/property.ll b/llvm/test/DebugInfo/Generic/property.ll
index e7992531f1fbe..28cff08049b37 100644
--- a/llvm/test/DebugInfo/Generic/property.ll
+++ b/llvm/test/DebugInfo/Generic/property.ll
@@ -51,9 +51,9 @@
!6 = !{!7, !9}
!7 = !DIDerivedType(tag: DW_TAG_member, name: "_x", scope: !5, file: !3, line: 8, baseType: !8, size: 64)
!8 = !DIBasicType(name: "Int", size: 64, encoding: DW_ATE_signed)
-!9 = !DIProperty(name: "x", file: !3, line: 8, type: !8, getterForward: !7)
+!9 = !DIProperty(name: "x", file: !3, line: 8, type: !8, getter: !7)
!10 = !DICompositeType(tag: DW_TAG_class_type, name: "Bar", scope: !3, file: !3, line: 12, size: 64, elements: !11)
!11 = !{!13, !12}
!12 = !DIDerivedType(tag: DW_TAG_member, name: "_y", scope: !10, file: !3, line: 13, baseType: !8, size: 64)
-!13 = !DIProperty(name: "y", file: !3, line: 13, type: !8, getterForward: !12)
+!13 = !DIProperty(name: "y", file: !3, line: 13, type: !8, getter: !12)
diff --git a/llvm/test/Verifier/diproperty.ll b/llvm/test/Verifier/diproperty.ll
index 64c56e86fb6b5..53f2f6de3acde 100644
--- a/llvm/test/Verifier/diproperty.ll
+++ b/llvm/test/Verifier/diproperty.ll
@@ -6,13 +6,13 @@
!1 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
; A getter must forward to the data member holding the backing storage.
-; CHECK: property getterForward must be a member
-!2 = !DIProperty(name: "x", file: !0, line: 8, type: !1, getterForward: !1)
+; CHECK: property getter must be a member
+!2 = !DIProperty(name: "x", file: !0, line: 8, type: !1, getter: !1)
-; CHECK: property getterForward must be a member
-!3 = !DIProperty(name: "x", file: !0, line: 8, type: !1, getterForward: !0)
+; CHECK: property getter must be a member
+!3 = !DIProperty(name: "x", file: !0, line: 8, type: !1, getter: !0)
; A pointer is a DIDerivedType, but not a DW_TAG_member.
-; CHECK: property getterForward must be a member
-!4 = !DIProperty(name: "x", file: !0, line: 8, type: !1, getterForward: !5)
+; CHECK: property getter must be a member
+!4 = !DIProperty(name: "x", file: !0, line: 8, type: !1, getter: !5)
!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !1, size: 64)
diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index 93ad970a879f7..c856bfbfc6955 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -4854,26 +4854,26 @@ TEST_F(DIPropertyTest, get) {
DIFile *File = getFile();
unsigned Line = 5;
DIType *Type = getBasicType("basic");
- DIDerivedType *GetterForward = GetMember("_x");
+ DIDerivedType *Getter = GetMember("_x");
- auto *N = DIProperty::get(Context, Name, File, Line, Type, GetterForward);
+ auto *N = DIProperty::get(Context, Name, File, Line, Type, Getter);
EXPECT_EQ(dwarf::DW_TAG_property, N->getTag());
EXPECT_EQ(Name, N->getName());
EXPECT_EQ(File, N->getFile());
EXPECT_EQ(Line, N->getLine());
EXPECT_EQ(Type, N->getType());
- EXPECT_EQ(GetterForward, N->getGetterForward());
- EXPECT_EQ(N, DIProperty::get(Context, Name, File, Line, Type, GetterForward));
+ EXPECT_EQ(Getter, N->getGetter());
+ EXPECT_EQ(N, DIProperty::get(Context, Name, File, Line, Type, Getter));
EXPECT_NE(N,
- DIProperty::get(Context, "other", File, Line, Type, GetterForward));
+ DIProperty::get(Context, "other", File, Line, Type, Getter));
EXPECT_NE(
- N, DIProperty::get(Context, Name, getFile(), Line, Type, GetterForward));
+ N, DIProperty::get(Context, Name, getFile(), Line, Type, Getter));
EXPECT_NE(
- N, DIProperty::get(Context, Name, File, Line + 1, Type, GetterForward));
+ N, DIProperty::get(Context, Name, File, Line + 1, Type, Getter));
EXPECT_NE(N, DIProperty::get(Context, Name, File, Line, getBasicType("other"),
- GetterForward));
+ Getter));
EXPECT_NE(
N, DIProperty::get(Context, Name, File, Line, Type, GetMember("_other")));
>From 917f99be116f004ecd81e7c30fbbd82411c425ff Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 10 Aug 2026 20:19:39 +0200
Subject: [PATCH 3/6] [DebugInfo] Emit `DW_TAG_property` DIEs in DwarfUnit
---
llvm/include/llvm/BinaryFormat/Dwarf.def | 11 +----
llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 1 +
llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 49 ++++++++++------------
llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h | 3 ++
4 files changed, 28 insertions(+), 36 deletions(-)
diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def
index 9d80fb8f42573..054fa292b7c53 100644
--- a/llvm/include/llvm/BinaryFormat/Dwarf.def
+++ b/llvm/include/llvm/BinaryFormat/Dwarf.def
@@ -437,16 +437,7 @@ HANDLE_DW_AT(0x8c, loclists_base, 5, DWARF)
// New in Dwarf v6:
HANDLE_DW_AT(0x90, language_name, 6, DWARF)
HANDLE_DW_AT(0x91, language_version, 6, DWARF)
-// TODO: DWARF v6 adds DW_AT_property_forward, used by the DW_TAG_property_getter
-// / _setter / _stored entries to refer to whatever implements the accessor. Add
-// it here, in numeric order with the entries above.
-//
-// Get its code and form class from Table 8.5 of the DWARF 6 draft rather than
-// from any summary of the proposal. Earlier write-ups of this proposal
-// circulated a different code, which the committee did not end up using -- and
-// picking the wrong one produces object files that every consumer misreads,
-// which no test in this tree would catch. Verifying a code against the actual
-// spec table is the habit worth building here.
+HANDLE_DW_AT(0x95, property_forward, 6, DWARF)
// Vendor extensions:
HANDLE_DW_AT(0x806, GHS_namespace_alias, 0, GHS)
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 0b21819cb7bdf..c94ef946e20eb 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -1340,6 +1340,7 @@ void DwarfDebug::finalizeModuleInfo() {
// Emit DW_AT_containing_type attribute to connect types with their
// vtable holding type.
TheCU.constructContainingTypeDIEs();
+ TheCU.constructPropertyForwardDIEs();
// Add CU specific attributes if we need to add any.
// If we're splitting the dwarf out now that we've got the entire
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index c32ad6fd4a106..52ba8d2e8a3b2 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -2021,32 +2021,29 @@ DIE &DwarfUnit::constructMemberDIE(DIE &Buffer, const DIDerivedType *DT) {
}
void DwarfUnit::constructPropertyDIE(DIE &Buffer, const DIProperty *P) {
- // TODO: Emit the property. The DIE tree this needs to produce is:
- //
- // DW_TAG_property
- // DW_AT_name <- P->getName()
- // DW_AT_type <- P->getType()
- // DW_AT_decl_file/line <- addSourceLine() has an overload for this
- // DW_TAG_property_getter (a child DIE, not an attribute)
- // DW_AT_property_forward (a reference to P->getGetter())
- //
- // createAndAddDIE, addString, addType and addDIEEntry are the helpers you
- // want. constructMemberDIE, just above, is a good model to follow -- note in
- // particular how it takes the metadata node as createAndAddDIE's third
- // argument, and what that buys you.
- //
- // Two questions to answer rather than guess at:
- //
- // - getGetter() hands you a DINode, but addDIEEntry needs a DIE. How
- // do you get from one to the other? The DW_AT_APPLE_property code at the
- // end of constructMemberDIE does the same lookup. What should happen when
- // that lookup finds nothing -- is it better to emit the getter child
- // anyway, or to leave it off?
- //
- // - test/DebugInfo/Generic/property.ll declares two classes, Foo and Bar,
- // whose elements lists hold the same two nodes in opposite orders. Both
- // must produce the same DWARF. If only one of them does, the fix is
- // probably not inside this function.
+ DIE &PropertyDie = createAndAddDIE(dwarf::DW_TAG_property, Buffer, P);
+ addString(PropertyDie, dwarf::DW_AT_name, P->getName());
+ if (DIType *Ty = P->getType())
+ addType(PropertyDie, Ty);
+ addSourceLine(PropertyDie, P);
+
+ if (DINode *Getter = P->getGetter()) {
+ DIE &GetterDie = createAndAddDIE(dwarf::DW_TAG_property_getter, PropertyDie);
+ PropertyForwardMap.insert(std::make_pair(&GetterDie, Getter));
+ }
+}
+
+void DwarfUnit::constructPropertyForwardDIEs() {
+ for (auto &P : PropertyForwardMap) {
+ DIE &GetterDie = *P.first;
+ const DINode *Target = P.second;
+ if (!Target)
+ continue;
+ DIE *TargetDie = getDIE(Target);
+ if (!TargetDie)
+ continue;
+ addDIEEntry(GetterDie, dwarf::DW_AT_property_forward, *TargetDie);
+ }
}
DIE *DwarfUnit::getOrCreateStaticMemberDIE(const DIDerivedType *DT) {
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
index 1ed2625255d21..42d537128642e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
@@ -73,6 +73,7 @@ class DwarfUnit : public DIEUnit {
/// DW_AT_containing_type attribute. This attribute points to a DIE that
/// corresponds to the MDNode mapped with the subprogram DIE.
DenseMap<DIE *, const DINode *> ContainingTypeMap;
+ DenseMap<DIE *, const DINode *> PropertyForwardMap;
DwarfUnit(dwarf::Tag, const DICompileUnit *Node, AsmPrinter *A,
DwarfDebug *DW, DwarfFile *DWU, unsigned UniqueID = 0);
@@ -280,6 +281,8 @@ class DwarfUnit : public DIEUnit {
/// Construct DIEs for types that contain vtables.
void constructContainingTypeDIEs();
+ void constructPropertyForwardDIEs();
+
/// Construct function argument DIEs.
///
/// \returns The index of the object parameter in \c Args if one exists.
>From 439f207b9b83fe4ff11811e279cd834fc632e1b2 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 10 Aug 2026 21:20:34 +0200
Subject: [PATCH 4/6] [DebugInfo] Finish DIProperty bitcode, printing, and
verification stubs
---
llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 13 ++++++++++++-
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 10 +++++++++-
llvm/lib/DebugInfo/DWARF/DWARFDie.cpp | 5 +++++
llvm/lib/IR/AsmWriter.cpp | 9 ++++++++-
llvm/lib/IR/Verifier.cpp | 19 +++++++------------
5 files changed, 41 insertions(+), 15 deletions(-)
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 00cdaa3c2cdea..0a370e0eaa0eb 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -2418,7 +2418,18 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
break;
}
case bitc::METADATA_PROPERTY: {
- // TODO: implement this.
+ if (Record.size() != 6)
+ return error("Invalid record");
+
+ IsDistinct = Record[0];
+ MetadataList.assignValue(
+ GET_OR_DISTINCT(DIProperty,
+ (Context, getMDString(Record[1]),
+ getMDOrNull(Record[2]), Record[3],
+ getDITypeRefOrNull(Record[4]),
+ getMDOrNull(Record[5]))),
+ NextMetadataNo);
+ NextMetadataNo++;
break;
}
case bitc::METADATA_IMPORTED_ENTITY: {
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 3988c14133ce6..e08d5b403c552 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -2515,7 +2515,15 @@ void ModuleBitcodeWriter::writeDIObjCProperty(const DIObjCProperty *N,
void ModuleBitcodeWriter::writeDIProperty(const DIProperty *N,
SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev) {
- // TODO: implement this.
+ Record.push_back(N->isDistinct());
+ Record.push_back(VE.getMetadataOrNullID(N->getRawName()));
+ Record.push_back(VE.getMetadataOrNullID(N->getFile()));
+ Record.push_back(N->getLine());
+ Record.push_back(VE.getMetadataOrNullID(N->getType()));
+ Record.push_back(VE.getMetadataOrNullID(N->getGetter()));
+
+ Stream.EmitRecord(bitc::METADATA_PROPERTY, Record, Abbrev);
+ Record.clear();
}
void ModuleBitcodeWriter::writeDIImportedEntity(
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
index 997e2b60e8125..3deaa75b03f59 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
@@ -251,6 +251,11 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die,
Die.getAttributeValueAsReferencedDie(FormValue).getName(
DINameKind::LinkageName))
OS << Space << "\"" << Name << '\"';
+ } else if (Attr == DW_AT_property_forward) {
+ if (const char *Name =
+ Die.getAttributeValueAsReferencedDie(FormValue).getName(
+ DINameKind::ShortName))
+ OS << Space << "\"" << Name << '\"';
} else if (Attr == DW_AT_APPLE_property) {
auto PropDIE = Die.getAttributeValueAsReferencedDie(FormValue);
if (auto PropNameOrErr = getApplePropertyName(PropDIE))
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 2a1ebb99f3351..35be0b18bad99 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -2702,7 +2702,14 @@ static void writeDIObjCProperty(raw_ostream &Out, const DIObjCProperty *N,
static void writeDIProperty(raw_ostream &Out, const DIProperty *N,
AsmWriterContext &WriterCtx) {
- // TODO: implement this
+ Out << "!DIProperty(";
+ MDFieldPrinter Printer(Out, WriterCtx);
+ Printer.printString("name", N->getName());
+ Printer.printMetadata("file", N->getRawFile());
+ Printer.printInt("line", N->getLine());
+ Printer.printMetadata("type", N->getRawType());
+ Printer.printMetadata("getter", N->getRawGetter());
+ Out << ")";
}
static void writeDIImportedEntity(raw_ostream &Out, const DIImportedEntity *N,
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 847023aa8b4ad..61b040cf91303 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1776,18 +1776,13 @@ void Verifier::visitDIProperty(const DIProperty &N) {
CheckDI(isType(T), "invalid type ref", &N, T);
if (auto *F = N.getRawFile())
CheckDI(isa<DIFile>(F), "invalid file", &N, F);
- // TODO: Check getter. It ends up as the DW_AT_property_forward of a
- // DW_TAG_property_getter, and the only thing the backend currently knows how
- // to point at is the data member holding the property's backing storage.
- //
- // Note the DWARF specification is deliberately looser than we need to be
- // here: it also allows a property getter to forward to a subprogram, a
- // variable or a constant. Restricting this is a choice about what we support
- // today, not a transcription of the spec, so the check should be tight enough
- // that a wrong node is caught early rather than turning into bad DWARF.
- //
- // test/Verifier/diproperty.ll has the cases that must be rejected, and the
- // diagnostic they expect.
+ // DWARF allows a property getter to forward to a subprogram, variable, or
+ // constant too, but the backend only knows how to forward to a member.
+ if (DINode *G = N.getGetter()) {
+ auto *DT = dyn_cast<DIDerivedType>(G);
+ CheckDI(DT && DT->getTag() == dwarf::DW_TAG_member,
+ "property getter must be a member", &N, G);
+ }
}
void Verifier::visitDIImportedEntity(const DIImportedEntity &N) {
>From 8e2cf3358d9117a94431a78709d7c0603f6157e0 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 10 Aug 2026 21:20:57 +0200
Subject: [PATCH 5/6] [DebugInfo] Fix Bar's CHECK order in property.ll
---
llvm/test/DebugInfo/Generic/property.ll | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/test/DebugInfo/Generic/property.ll b/llvm/test/DebugInfo/Generic/property.ll
index 28cff08049b37..052e736779cf1 100644
--- a/llvm/test/DebugInfo/Generic/property.ll
+++ b/llvm/test/DebugInfo/Generic/property.ll
@@ -30,13 +30,13 @@
; CHECK: DW_TAG_class_type
; CHECK: DW_AT_name ("Bar")
;
-; CHECK: 0x[[BAR_Y:[0-9a-f]+]]: DW_TAG_member
-; CHECK: DW_AT_name ("_y")
-;
; CHECK: DW_TAG_property
; CHECK: DW_AT_name ("y")
; CHECK: DW_TAG_property_getter
-; CHECK: DW_AT_property_forward (0x[[BAR_Y]] "_y")
+; CHECK: DW_AT_property_forward (0x[[BAR_Y:[0-9a-f]+]] "_y")
+;
+; CHECK: 0x[[BAR_Y]]: DW_TAG_member
+; CHECK: DW_AT_name ("_y")
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2}
>From 775e44a7bd45fc0b4f75224f047f7b7173bdc599 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Wed, 12 Aug 2026 12:12:56 +0200
Subject: [PATCH 6/6] [DebugInfo] Exclude DW_TAG_property from name-index
completeness checks
Properties are accessed through their containing subprogram/type, not
looked up globally by name, so they belong in the same
not-globally-visible category as DW_TAG_member.
---
llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
index 32775f348bb22..ebd54e18bbca9 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
@@ -1986,8 +1986,11 @@ void DWARFVerifier::verifyNameIndexCompleteness(
case DW_TAG_GNU_template_template_param:
return;
- // Object members aren't globally visible.
+ // Object members aren't globally visible. Properties are accessed through
+ // their containing subprogram/type, not looked up globally by name, so
+ // they belong in the same category.
case DW_TAG_member:
+ case DW_TAG_property:
return;
// DW_TAG_LLVM_annotation DIEs attach metadata to other DIEs.
More information about the llvm-commits
mailing list