Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DISubrangeType #126772

Merged
merged 5 commits into from
Feb 24, 2025
Merged

Add DISubrangeType #126772

merged 5 commits into from
Feb 24, 2025

Conversation

tromey
Copy link
Contributor

@tromey tromey commented Feb 11, 2025

An Ada program can have types that are subranges of other types. This patch adds a new DIType node, DISubrangeType, to represent this concept.

I considered extending the existing DISubrange to do this, but as DISubrange does not derive from DIType, that approach seemed more disruptive.

A DISubrangeType can be used both as an ordinary type, but also as the type of an array index. This is also important for Ada.

Ada subrange types can also be stored using a bias. Representing this in the DWARF required the use of an extension. GCC has been emitting this extension for years, so I've reused it here.

@llvmbot
Copy link
Member

llvmbot commented Feb 11, 2025

@llvm/pr-subscribers-debuginfo

@llvm/pr-subscribers-llvm-ir

Author: Tom Tromey (tromey)

Changes

An Ada program can have types that are subranges of other types. This patch adds a new DIType node, DISubrangeType, to represent this concept.

I considered extending the existing DISubrange to do this, but as DISubrange does not derive from DIType, that approach seemed more disruptive.

A DISubrangeType can be used both as an ordinary type, but also as the type of an array index. This is also important for Ada.

Ada subrange types can also be stored using a bias. Representing this in the DWARF required the use of an extension. GCC has been emitting this extension for years, so I've reused it here.


Patch is 35.00 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/126772.diff

18 Files Affected:

  • (modified) llvm/include/llvm-c/DebugInfo.h (+1)
  • (modified) llvm/include/llvm/Bitcode/LLVMBitCodes.h (+1)
  • (modified) llvm/include/llvm/IR/DIBuilder.h (+20)
  • (modified) llvm/include/llvm/IR/DebugInfoMetadata.h (+94-3)
  • (modified) llvm/include/llvm/IR/Metadata.def (+1)
  • (modified) llvm/lib/AsmParser/LLParser.cpp (+44)
  • (modified) llvm/lib/Bitcode/Reader/MetadataLoader.cpp (+18)
  • (modified) llvm/lib/Bitcode/Writer/BitcodeWriter.cpp (+23)
  • (modified) llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp (+13-6)
  • (modified) llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp (+77-19)
  • (modified) llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h (+4-3)
  • (modified) llvm/lib/IR/AsmWriter.cpp (+20)
  • (modified) llvm/lib/IR/DIBuilder.cpp (+10)
  • (modified) llvm/lib/IR/DebugInfoMetadata.cpp (+42)
  • (modified) llvm/lib/IR/LLVMContextImpl.h (+49)
  • (modified) llvm/lib/IR/Verifier.cpp (+24)
  • (added) llvm/test/Bitcode/subrange_type.ll (+28)
  • (modified) llvm/unittests/IR/MetadataTest.cpp (+33)
diff --git a/llvm/include/llvm-c/DebugInfo.h b/llvm/include/llvm-c/DebugInfo.h
index ac7ee5a7cc9a19d..9d0875a4ed8d8fa 100644
--- a/llvm/include/llvm-c/DebugInfo.h
+++ b/llvm/include/llvm-c/DebugInfo.h
@@ -172,6 +172,7 @@ enum {
   LLVMDIEnumeratorMetadataKind,
   LLVMDIBasicTypeMetadataKind,
   LLVMDIDerivedTypeMetadataKind,
+  LLVMDISubrangeTypeMetadataKind,
   LLVMDICompositeTypeMetadataKind,
   LLVMDISubroutineTypeMetadataKind,
   LLVMDIFileMetadataKind,
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 9eb38c3e4482910..ec2535ac85966ac 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -385,6 +385,7 @@ enum MetadataCodes {
   METADATA_GENERIC_SUBRANGE = 45, // [distinct, count, lo, up, stride]
   METADATA_ARG_LIST = 46,         // [n x [type num, value num]]
   METADATA_ASSIGN_ID = 47,        // [distinct, ...]
+  METADATA_SUBRANGE_TYPE = 48,    // [distinct, ...]
 };
 
 // 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 8bee9f4703dd9c7..fc6b88821b09897 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -667,6 +667,26 @@ namespace llvm {
     /// If \p Implicit is true, also set FlagArtificial.
     static DIType *createObjectPointerType(DIType *Ty, bool Implicit);
 
+    /// Create a type describing a subrange of another type.
+    /// \param Scope          Scope in which this set is defined.
+    /// \param Name           Set name.
+    /// \param File           File where this set is defined.
+    /// \param LineNo         Line number.
+    /// \param SizeInBits     Size.
+    /// \param AlignInBits    Alignment.
+    /// \param Flags          Flags to encode attributes.
+    /// \param Ty             Base type.
+    /// \param LowerBound     Lower bound.
+    /// \param UpperBound     Upper bound.
+    /// \param Stride         Stride, if any.
+    /// \param Bias           Bias, if any.
+    DISubrangeType *
+    createSubrangeType(StringRef Name, DIFile *File, unsigned LineNo,
+                       DIScope *Scope, uint64_t SizeInBits,
+                       uint32_t AlignInBits, DINode::DIFlags Flags, DIType *Ty,
+                       Metadata *LowerBound, Metadata *UpperBound,
+                       Metadata *Stride, Metadata *Bias);
+
     /// Create a permanent forward-declared type.
     DICompositeType *
     createForwardDecl(unsigned Tag, StringRef Name, DIScope *Scope, DIFile *F,
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 8515d8eda85686b..7826514cd3e44aa 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -200,6 +200,7 @@ class DINode : public MDNode {
     case DIEnumeratorKind:
     case DIBasicTypeKind:
     case DIStringTypeKind:
+    case DISubrangeTypeKind:
     case DIDerivedTypeKind:
     case DICompositeTypeKind:
     case DISubroutineTypeKind:
@@ -342,9 +343,6 @@ class DIAssignID : public MDNode {
 };
 
 /// Array subrange.
-///
-/// TODO: Merge into node for DW_TAG_array_type, which should have a custom
-/// type.
 class DISubrange : public DINode {
   friend class LLVMContextImpl;
   friend class MDNode;
@@ -550,6 +548,7 @@ class DIScope : public DINode {
       return false;
     case DIBasicTypeKind:
     case DIStringTypeKind:
+    case DISubrangeTypeKind:
     case DIDerivedTypeKind:
     case DICompositeTypeKind:
     case DISubroutineTypeKind:
@@ -808,6 +807,7 @@ class DIType : public DIScope {
       return false;
     case DIBasicTypeKind:
     case DIStringTypeKind:
+    case DISubrangeTypeKind:
     case DIDerivedTypeKind:
     case DICompositeTypeKind:
     case DISubroutineTypeKind:
@@ -1167,6 +1167,97 @@ inline bool operator!=(DIDerivedType::PtrAuthData Lhs,
   return !(Lhs == Rhs);
 }
 
+/// Subrange type.  This is somewhat similar to DISubrange, but it
+/// is also a DIType.
+class DISubrangeType : public DIType {
+public:
+  typedef PointerUnion<ConstantInt *, DIVariable *, DIExpression *> BoundType;
+
+private:
+  friend class LLVMContextImpl;
+  friend class MDNode;
+
+  DISubrangeType(LLVMContext &C, StorageType Storage, unsigned Line,
+                 uint64_t SizeInBits, uint32_t AlignInBits, DIFlags Flags,
+                 ArrayRef<Metadata *> Ops);
+
+  ~DISubrangeType() = default;
+
+  static DISubrangeType *
+  getImpl(LLVMContext &Context, StringRef Name, DIFile *File, unsigned Line,
+          DIScope *Scope, uint64_t SizeInBits, uint32_t AlignInBits,
+          DIFlags Flags, DIType *BaseType, Metadata *LowerBound,
+          Metadata *UpperBound, Metadata *Stride, Metadata *Bias,
+          StorageType Storage, bool ShouldCreate = true) {
+    return getImpl(Context, getCanonicalMDString(Context, Name), File, Line,
+                   Scope, SizeInBits, AlignInBits, Flags, BaseType, LowerBound,
+                   UpperBound, Stride, Bias, Storage, ShouldCreate);
+  }
+
+  static DISubrangeType *getImpl(LLVMContext &Context, MDString *Name,
+                                 Metadata *File, unsigned Line, Metadata *Scope,
+                                 uint64_t SizeInBits, uint32_t AlignInBits,
+                                 DIFlags Flags, Metadata *BaseType,
+                                 Metadata *LowerBound, Metadata *UpperBound,
+                                 Metadata *Stride, Metadata *Bias,
+                                 StorageType Storage, bool ShouldCreate = true);
+
+  TempDISubrangeType cloneImpl() const {
+    return getTemporary(getContext(), getName(), getFile(), getLine(),
+                        getScope(), getSizeInBits(), getAlignInBits(),
+                        getFlags(), getBaseType(), getRawLowerBound(),
+                        getRawUpperBound(), getRawStride(), getRawBias());
+  }
+
+  BoundType convertRawToBound(Metadata *IN) const;
+
+public:
+  DEFINE_MDNODE_GET(DISubrangeType,
+                    (MDString * Name, Metadata *File, unsigned Line,
+                     Metadata *Scope, uint64_t SizeInBits, uint32_t AlignInBits,
+                     DIFlags Flags, Metadata *BaseType, Metadata *LowerBound,
+                     Metadata *UpperBound, Metadata *Stride, Metadata *Bias),
+                    (Name, File, Line, Scope, SizeInBits, AlignInBits, Flags,
+                     BaseType, LowerBound, UpperBound, Stride, Bias))
+  DEFINE_MDNODE_GET(DISubrangeType,
+                    (StringRef Name, DIFile *File, unsigned Line,
+                     DIScope *Scope, uint64_t SizeInBits, uint32_t AlignInBits,
+                     DIFlags Flags, DIType *BaseType, Metadata *LowerBound,
+                     Metadata *UpperBound, Metadata *Stride, Metadata *Bias),
+                    (Name, File, Line, Scope, SizeInBits, AlignInBits, Flags,
+                     BaseType, LowerBound, UpperBound, Stride, Bias))
+
+  TempDISubrangeType clone() const { return cloneImpl(); }
+
+  /// Get the base type this is derived from.
+  DIType *getBaseType() const { return cast_or_null<DIType>(getRawBaseType()); }
+  Metadata *getRawBaseType() const { return getOperand(3); }
+
+  Metadata *getRawLowerBound() const { return getOperand(4).get(); }
+
+  Metadata *getRawUpperBound() const { return getOperand(5).get(); }
+
+  Metadata *getRawStride() const { return getOperand(6).get(); }
+
+  Metadata *getRawBias() const { return getOperand(7).get(); }
+
+  BoundType getLowerBound() const {
+    return convertRawToBound(getRawLowerBound());
+  }
+
+  BoundType getUpperBound() const {
+    return convertRawToBound(getRawUpperBound());
+  }
+
+  BoundType getStride() const { return convertRawToBound(getRawStride()); }
+
+  BoundType getBias() const { return convertRawToBound(getRawBias()); }
+
+  static bool classof(const Metadata *MD) {
+    return MD->getMetadataID() == DISubrangeTypeKind;
+  }
+};
+
 /// Composite types.
 ///
 /// TODO: Detach from DerivedTypeBase (split out MDEnumType?).
diff --git a/llvm/include/llvm/IR/Metadata.def b/llvm/include/llvm/IR/Metadata.def
index a3cfb9ad6e3e785..7cb257fefbc3838 100644
--- a/llvm/include/llvm/IR/Metadata.def
+++ b/llvm/include/llvm/IR/Metadata.def
@@ -118,6 +118,7 @@ HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIMacroFile)
 HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DICommonBlock)
 HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIStringType)
 HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIGenericSubrange)
+HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DISubrangeType)
 
 #undef HANDLE_METADATA
 #undef HANDLE_METADATA_LEAF
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index ad52a9f493eae0e..71cb9cb75ab6e5b 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -5318,6 +5318,50 @@ bool LLParser::parseGenericDINode(MDNode *&Result, bool IsDistinct) {
   return false;
 }
 
+/// parseDISubrangeType:
+///   ::= !DISubrangeType(name: "whatever", file: !0,
+///                      line: 7, scope: !1, baseType: !2, size: 32,
+///                      align: 32, flags: 0, lowerBound: !3
+///                      upperBound: !4, stride: !5, bias: !6)
+bool LLParser::parseDISubrangeType(MDNode *&Result, bool IsDistinct) {
+#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED)                                    \
+  OPTIONAL(name, MDStringField, );                                             \
+  OPTIONAL(file, MDField, );                                                   \
+  OPTIONAL(line, LineField, );                                                 \
+  OPTIONAL(scope, MDField, );                                                  \
+  OPTIONAL(baseType, MDField, );                                               \
+  OPTIONAL(size, MDUnsignedField, (0, UINT64_MAX));                            \
+  OPTIONAL(align, MDUnsignedField, (0, UINT32_MAX));                           \
+  OPTIONAL(flags, DIFlagField, );                                              \
+  OPTIONAL(lowerBound, MDSignedOrMDField, );                                   \
+  OPTIONAL(upperBound, MDSignedOrMDField, );                                   \
+  OPTIONAL(stride, MDSignedOrMDField, );                                       \
+  OPTIONAL(bias, MDSignedOrMDField, );
+  PARSE_MD_FIELDS();
+#undef VISIT_MD_FIELDS
+
+  auto convToMetadata = [&](MDSignedOrMDField Bound) -> Metadata * {
+    if (Bound.isMDSignedField())
+      return ConstantAsMetadata::get(ConstantInt::getSigned(
+          Type::getInt64Ty(Context), Bound.getMDSignedValue()));
+    if (Bound.isMDField())
+      return Bound.getMDFieldValue();
+    return nullptr;
+  };
+
+  Metadata *LowerBound = convToMetadata(lowerBound);
+  Metadata *UpperBound = convToMetadata(upperBound);
+  Metadata *Stride = convToMetadata(stride);
+  Metadata *Bias = convToMetadata(bias);
+
+  Result = GET_OR_DISTINCT(DISubrangeType,
+                           (Context, name.Val, file.Val, line.Val, scope.Val,
+                            size.Val, align.Val, flags.Val, baseType.Val,
+                            LowerBound, UpperBound, Stride, Bias));
+
+  return false;
+}
+
 /// parseDISubrange:
 ///   ::= !DISubrange(count: 30, lowerBound: 2)
 ///   ::= !DISubrange(count: !node, lowerBound: 2)
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 413d9f68e6cc3b7..0fbab3200fcf1fc 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -1599,6 +1599,24 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
     NextMetadataNo++;
     break;
   }
+  case bitc::METADATA_SUBRANGE_TYPE: {
+    if (Record.size() != 13)
+      return error("Invalid record");
+
+    IsDistinct = Record[0];
+    DINode::DIFlags Flags = static_cast<DINode::DIFlags>(Record[7]);
+    MetadataList.assignValue(
+        GET_OR_DISTINCT(DISubrangeType,
+                        (Context, getMDString(Record[1]),
+                         getMDOrNull(Record[2]), Record[3],
+                         getMDOrNull(Record[4]), Record[5], Record[6], Flags,
+                         getDITypeRefOrNull(Record[8]), getMDOrNull(Record[9]),
+                         getMDOrNull(Record[10]), getMDOrNull(Record[11]),
+                         getMDOrNull(Record[12]))),
+        NextMetadataNo);
+    NextMetadataNo++;
+    break;
+  }
   case bitc::METADATA_COMPOSITE_TYPE: {
     if (Record.size() < 16 || Record.size() > 25)
       return error("Invalid record");
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 7ca63c2c7251ded..244bf8eeb9b1893 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -327,6 +327,8 @@ class ModuleBitcodeWriter : public ModuleBitcodeWriterBase {
                          SmallVectorImpl<uint64_t> &Record, unsigned Abbrev);
   void writeDIDerivedType(const DIDerivedType *N,
                           SmallVectorImpl<uint64_t> &Record, unsigned Abbrev);
+  void writeDISubrangeType(const DISubrangeType *N,
+                           SmallVectorImpl<uint64_t> &Record, unsigned Abbrev);
   void writeDICompositeType(const DICompositeType *N,
                             SmallVectorImpl<uint64_t> &Record, unsigned Abbrev);
   void writeDISubroutineType(const DISubroutineType *N,
@@ -1930,6 +1932,27 @@ void ModuleBitcodeWriter::writeDIDerivedType(const DIDerivedType *N,
   Record.clear();
 }
 
+void ModuleBitcodeWriter::writeDISubrangeType(const DISubrangeType *N,
+                                              SmallVectorImpl<uint64_t> &Record,
+                                              unsigned Abbrev) {
+  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->getScope()));
+  Record.push_back(N->getSizeInBits());
+  Record.push_back(N->getAlignInBits());
+  Record.push_back(N->getFlags());
+  Record.push_back(VE.getMetadataOrNullID(N->getBaseType()));
+  Record.push_back(VE.getMetadataOrNullID(N->getRawLowerBound()));
+  Record.push_back(VE.getMetadataOrNullID(N->getRawUpperBound()));
+  Record.push_back(VE.getMetadataOrNullID(N->getRawStride()));
+  Record.push_back(VE.getMetadataOrNullID(N->getRawBias()));
+
+  Stream.EmitRecord(bitc::METADATA_SUBRANGE_TYPE, Record, Abbrev);
+  Record.clear();
+}
+
 void ModuleBitcodeWriter::writeDICompositeType(
     const DICompositeType *N, SmallVectorImpl<uint64_t> &Record,
     unsigned Abbrev) {
diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp
index de2263c57493bb2..533c554faceddec 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp
@@ -148,20 +148,21 @@ MCSymbol *DebugHandlerBase::getLabelAfterInsn(const MachineInstr *MI) {
 /// If this type is derived from a base type then return base type size.
 uint64_t DebugHandlerBase::getBaseTypeSize(const DIType *Ty) {
   assert(Ty);
-  const DIDerivedType *DDTy = dyn_cast<DIDerivedType>(Ty);
-  if (!DDTy)
-    return Ty->getSizeInBits();
 
-  unsigned Tag = DDTy->getTag();
+  unsigned Tag = Ty->getTag();
 
   if (Tag != dwarf::DW_TAG_member && Tag != dwarf::DW_TAG_typedef &&
       Tag != dwarf::DW_TAG_const_type && Tag != dwarf::DW_TAG_volatile_type &&
       Tag != dwarf::DW_TAG_restrict_type && Tag != dwarf::DW_TAG_atomic_type &&
       Tag != dwarf::DW_TAG_immutable_type &&
       Tag != dwarf::DW_TAG_template_alias)
-    return DDTy->getSizeInBits();
+    return Ty->getSizeInBits();
 
-  DIType *BaseType = DDTy->getBaseType();
+  DIType *BaseType = nullptr;
+  if (const DIDerivedType *DDTy = dyn_cast<DIDerivedType>(Ty))
+    BaseType = DDTy->getBaseType();
+  else if (const DISubrangeType *SRTy = dyn_cast<DISubrangeType>(Ty))
+    BaseType = SRTy->getBaseType();
 
   if (!BaseType)
     return 0;
@@ -187,6 +188,12 @@ bool DebugHandlerBase::isUnsignedDIType(const DIType *Ty) {
     return true;
   }
 
+  if (auto *SRTy = dyn_cast<DISubrangeType>(Ty)) {
+    Ty = SRTy->getBaseType();
+    if (!Ty)
+      return false;
+  }
+
   if (auto *CTy = dyn_cast<DICompositeType>(Ty)) {
     if (CTy->getTag() == dwarf::DW_TAG_enumeration_type) {
       if (!(Ty = CTy->getBaseType()))
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index c0f2878c84bc84c..700a9831f261788 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -621,6 +621,8 @@ DIE *DwarfUnit::createTypeDIE(const DIScope *Context, DIE &ContextDIE,
     construct(ST);
   else if (auto *STy = dyn_cast<DISubroutineType>(Ty))
     construct(STy);
+  else if (auto *SRTy = dyn_cast<DISubrangeType>(Ty))
+    constructSubrangeDIE(TyDIE, SRTy);
   else
     construct(cast<DIDerivedType>(Ty));
 
@@ -1421,10 +1423,69 @@ void DwarfUnit::applySubprogramAttributes(const DISubprogram *SP, DIE &SPDie,
     addFlag(SPDie, dwarf::DW_AT_deleted);
 }
 
-void DwarfUnit::constructSubrangeDIE(DIE &Buffer, const DISubrange *SR,
-                                     DIE *IndexTy) {
+void DwarfUnit::constructSubrangeDIE(DIE &DW_Subrange, const DISubrangeType *SR,
+                                     bool ForArray) {
+  StringRef Name = SR->getName();
+  if (!Name.empty())
+    addString(DW_Subrange, dwarf::DW_AT_name, Name);
+
+  if (SR->getBaseType())
+    addType(DW_Subrange, SR->getBaseType());
+
+  addSourceLine(DW_Subrange, SR);
+
+  if (uint64_t Size = SR->getSizeInBits())
+    addUInt(DW_Subrange, dwarf::DW_AT_byte_size, std::nullopt, Size >> 3);
+  if (uint32_t AlignInBytes = SR->getAlignInBytes())
+    addUInt(DW_Subrange, dwarf::DW_AT_alignment, dwarf::DW_FORM_udata,
+            AlignInBytes);
+
+  if (SR->isBigEndian())
+    addUInt(DW_Subrange, dwarf::DW_AT_endianity, std::nullopt,
+            dwarf::DW_END_big);
+  else if (SR->isLittleEndian())
+    addUInt(DW_Subrange, dwarf::DW_AT_endianity, std::nullopt,
+            dwarf::DW_END_little);
+
+  // The LowerBound value defines the lower bounds which is typically
+  // zero for C/C++. Values are 64 bit.
+  int64_t DefaultLowerBound = getDefaultLowerBound();
+
+  auto AddBoundTypeEntry = [&](dwarf::Attribute Attr,
+                               DISubrangeType::BoundType Bound) -> void {
+    if (auto *BV = Bound.dyn_cast<DIVariable *>()) {
+      if (auto *VarDIE = getDIE(BV))
+        addDIEEntry(DW_Subrange, Attr, *VarDIE);
+    } else if (auto *BE = Bound.dyn_cast<DIExpression *>()) {
+      DIELoc *Loc = new (DIEValueAllocator) DIELoc;
+      DIEDwarfExpression DwarfExpr(*Asm, getCU(), *Loc);
+      DwarfExpr.setMemoryLocationKind();
+      DwarfExpr.addExpression(BE);
+      addBlock(DW_Subrange, Attr, DwarfExpr.finalize());
+    } else if (auto *BI = Bound.dyn_cast<ConstantInt *>()) {
+      if (Attr == dwarf::DW_AT_GNU_bias) {
+        if (BI->getSExtValue() != 0)
+          addUInt(DW_Subrange, Attr, dwarf::DW_FORM_sdata, BI->getSExtValue());
+      } else if (Attr != dwarf::DW_AT_lower_bound || DefaultLowerBound == -1 ||
+                 BI->getSExtValue() != DefaultLowerBound || !ForArray)
+        addSInt(DW_Subrange, Attr, dwarf::DW_FORM_sdata, BI->getSExtValue());
+    }
+  };
+
+  AddBoundTypeEntry(dwarf::DW_AT_lower_bound, SR->getLowerBound());
+
+  AddBoundTypeEntry(dwarf::DW_AT_upper_bound, SR->getUpperBound());
+
+  AddBoundTypeEntry(dwarf::DW_AT_byte_stride, SR->getStride());
+
+  AddBoundTypeEntry(dwarf::DW_AT_GNU_bias, SR->getBias());
+}
+
+void DwarfUnit::constructSubrangeDIE(DIE &Buffer, const DISubrange *SR) {
   DIE &DW_Subrange = createAndAddDIE(dwarf::DW_TAG_subrange_type, Buffer);
-  addDIEEntry(DW_Subrange, dwarf::DW_AT_type, *IndexTy);
+
+  DIE *IdxTy = getIndexTyDie();
+  addDIEEntry(DW_Subrange, dwarf::DW_AT_type, *IdxTy);
 
   // The LowerBound value defines the lower bounds which is typically zero for
   // C/C++. The Count value is the number of elements.  Values are 64 bit. If
@@ -1463,11 +1524,14 @@ void DwarfUnit::constructSubrangeDIE(DIE &Buffer, const DISubrange *SR,
 }
 
 void DwarfUnit::constructGenericSubrangeDIE(DIE &Buffer,
-                                            const DIGenericSubrange *GSR,
-                                    ...
[truncated]

@tromey
Copy link
Contributor Author

tromey commented Feb 11, 2025

Definitely a failure in this patch, but I don't really understand it. Will try to find out.

@Michael137
Copy link
Member

Could you share some examples of what these Ada types look like, how they're used and what the expected DWARF would look like?

@tromey
Copy link
Contributor Author

tromey commented Feb 12, 2025

Could you share some examples of what these Ada types look like, how they're used and what the expected DWARF would look like?

Here's an example of a range type that shows some of the features:

   type Small is range -7 .. -4;
   for Small'Size use 2;

This is an integer range. Objects of this type take 2 bits, so the type is stored with a bias -- that is, the value -7 is represented as 0 in memory. It looks like this in the DWARF (this is the GCC output but the LLVM output is similar):

 <2><119e>: Abbrev Number: 4 (DW_TAG_subrange_type)
    <119f>   DW_AT_lower_bound : -7
    <11a0>   DW_AT_upper_bound : -4
    <11a1>   DW_AT_GNU_bias    : -7
    <11a2>   DW_AT_name        : (indirect string, offset: 0x1b2b): bias__small
    <11a6>   DW_AT_type        : <0x125a>

@pogo59
Copy link
Collaborator

pogo59 commented Feb 12, 2025

FTR, Pascal also has subrange types, so this can be useful beyond Ada. I suspect Pascal compilers would also use a bias if the type is used as an array index, and maybe if it was a packed type, although I haven't worked on any Pascal compilers for decades.

@tromey
Copy link
Contributor Author

tromey commented Feb 12, 2025

Oops, I forgot the "how they're used" part. They are just integers:

   Y : Small := -5;
   Y1 : Small := -7;

Also worth pointing out that in Ada you can have a subrange of an enumeration type (and I think fixed point types -- but I haven't submitted the fixed point work yet). Also, the bounds can be dynamic.

@adrian-prantl
Copy link
Collaborator

Haven't looked into the semantics of the new node kind, but the mechanical part of the implementation and test coverage LGTM.

@tromey
Copy link
Contributor Author

tromey commented Feb 12, 2025

I made some changes to try to fix the failure pointed out by the bot. I am not sure if IRTest ran here (via check-llvm and check-llvm-unit) but I ran it by hand and it worked.

Copy link

github-actions bot commented Feb 12, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@tromey
Copy link
Contributor Author

tromey commented Feb 12, 2025

Maybe worth pointing out that the bias extension is being standardized: https://dwarfstd.org/issues/190809.1.html

When DWARF 6 is released this will be an easy fix.

Pierre-Marie documented the extension when it was added to GCC: https://gcc.gnu.org/wiki/DW_AT_GNU_bias

This patch refactors DebugHandlerBase::getBaseTypeSize a little,
making it less dependent on DIDerivedType.  This is preparation for a
subsequent patch that adds a new case here.
DwarfUnit::constructArrayTypeDIE has a FIXME comment related to an
over-broad cast.  As I am planning to add a new case in this spot,
this patch first refactors it to be more precise.
A future patch will add the ability to specify the index type of an
array.  To facilitate this, this patch moves the creation of the
current index type out of DwarfUnit::constructArrayTypeDIE and into
the methods where it is needed.
@tromey
Copy link
Contributor Author

tromey commented Feb 13, 2025

Definitely a failure in this patch, but I don't really understand it. Will try to find out.

Valgrind helped track it down. I was missing an initializer in MDNodeKeyImpl<DISubrangeType>. I would have thought some warning would have triggered for this. Anyway will upload the new patch once rebuilding + testing is done.

An Ada program can have types that are subranges of other types.  This
patch adds a new DIType node, DISubrangeType, to represent this
concept.

I considered extending the existing DISubrange to do this, but as
DISubrange does not derive from DIType, that approach seemed more
disruptive.

A DISubrangeType can be used both as an ordinary type, but also as the
type of an array index.  This is also important for Ada.
@tromey
Copy link
Contributor Author

tromey commented Feb 24, 2025

Ping.

@dwblaikie dwblaikie merged commit e298fc2 into llvm:main Feb 24, 2025
6 of 9 checks passed
@tromey tromey deleted the subrange-type branch February 24, 2025 18:13
farzonl added a commit to farzonl/llvm-project that referenced this pull request Feb 24, 2025
1. Fix build break caused by llvm#126772 by adding `writeDISubrangeType`
   stub to `DXILBitcodeWriter.cpp`
2. Fix build break caused by llvm#128480 by adding implementation of pure virtual
   method `TargetSubtargetInfo::getRegisterInfo`
farzonl added a commit to farzonl/llvm-project that referenced this pull request Feb 24, 2025
1. Fix build break caused by llvm#126772 by adding `writeDISubrangeType`
   stub to `DXILBitcodeWriter.cpp`
2. Fix build break caused by llvm#128480 by adding implementation of pure virtual
   method `TargetSubtargetInfo::getRegisterInfo`
farzonl added a commit that referenced this pull request Feb 24, 2025
1. Fix build break caused by #126772 by adding `writeDISubrangeType`
stub to `DXILBitcodeWriter.cpp`
2. Fix build break caused by #128480 by adding implementation of pure
virtual method `TargetSubtargetInfo::getRegisterInfo`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants