[llvm] [debuginfo] Add DW_AT_type support to DW_TAG_string_type DIEs (1 of 3) (PR #217419)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 12:46:48 PDT 2026


https://github.com/timsmith78 updated https://github.com/llvm/llvm-project/pull/217419

>From a188484d277481e211abdf9b28da424f7af8b8a7 Mon Sep 17 00:00:00 2001
From: Timothy Smith <timothy.smith at hpe.com>
Date: Wed, 19 Aug 2026 11:03:23 -0500
Subject: [PATCH 1/7] Add DW_AT_type support to DW_TAG_string_type DIEs

The DWARF spec allows for the DW_TAG_string_type DIE to contain a
DW_AT_type attribute which can be helpful for supporting non-ASCII
character types in a debugger.  This requires adding a DIType member
to DIStringType in LLVM IR, and propagating it to the DWARF generator
in the backend. (Issue #95440)
---
 llvm/include/llvm/IR/DebugInfoMetadata.h   | 37 +++++++++++++---------
 llvm/lib/AsmParser/LLParser.cpp            |  5 +--
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp |  7 ++--
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp  |  1 +
 llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp  |  3 ++
 llvm/lib/IR/AsmWriter.cpp                  |  1 +
 llvm/lib/IR/DebugInfoMetadata.cpp          | 16 +++++-----
 llvm/lib/IR/LLVMContextImpl.h              | 11 ++++---
 llvm/test/DebugInfo/fortran-string-type.ll |  6 +++-
 9 files changed, 55 insertions(+), 32 deletions(-)

diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index dd11edf7f6935..85b44a08394b1 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -1175,37 +1175,37 @@ class DIStringType : public DIType {
                                StringRef Name, Metadata *StringLength,
                                Metadata *StrLenExp, Metadata *StrLocationExp,
                                uint64_t SizeInBits, uint32_t AlignInBits,
-                               unsigned Encoding, StorageType Storage,
-                               bool ShouldCreate = true) {
+                               unsigned Encoding, Metadata *CharType,
+                               StorageType Storage, bool ShouldCreate = true) {
     auto *SizeInBitsNode = ConstantAsMetadata::get(
         ConstantInt::get(Type::getInt64Ty(Context), SizeInBits));
     return getImpl(Context, Tag, getCanonicalMDString(Context, Name),
                    StringLength, StrLenExp, StrLocationExp, SizeInBitsNode,
-                   AlignInBits, Encoding, Storage, ShouldCreate);
+                   AlignInBits, Encoding, CharType, Storage, ShouldCreate);
   }
   static DIStringType *getImpl(LLVMContext &Context, unsigned Tag,
                                MDString *Name, Metadata *StringLength,
                                Metadata *StrLenExp, Metadata *StrLocationExp,
                                uint64_t SizeInBits, uint32_t AlignInBits,
-                               unsigned Encoding, StorageType Storage,
-                               bool ShouldCreate = true) {
+                               unsigned Encoding, Metadata *CharType,
+                               StorageType Storage, bool ShouldCreate = true) {
     auto *SizeInBitsNode = ConstantAsMetadata::get(
         ConstantInt::get(Type::getInt64Ty(Context), SizeInBits));
     return getImpl(Context, Tag, Name, StringLength, StrLenExp, StrLocationExp,
-                   SizeInBitsNode, AlignInBits, Encoding, Storage,
+                   SizeInBitsNode, AlignInBits, Encoding, CharType, Storage,
                    ShouldCreate);
   }
   LLVM_ABI static DIStringType *
   getImpl(LLVMContext &Context, unsigned Tag, MDString *Name,
           Metadata *StringLength, Metadata *StrLenExp, Metadata *StrLocationExp,
           Metadata *SizeInBits, uint32_t AlignInBits, unsigned Encoding,
-          StorageType Storage, bool ShouldCreate = true);
+          Metadata *CharType, StorageType Storage, bool ShouldCreate = true);
 
   TempDIStringType cloneImpl() const {
     return getTemporary(getContext(), getTag(), getRawName(),
                         getRawStringLength(), getRawStringLengthExp(),
                         getRawStringLocationExp(), getRawSizeInBits(),
-                        getAlignInBits(), getEncoding());
+                        getAlignInBits(), getEncoding(), getRawCharType());
   }
 
 public:
@@ -1213,28 +1213,31 @@ class DIStringType : public DIType {
                     (unsigned Tag, StringRef Name, uint64_t SizeInBits,
                      uint32_t AlignInBits),
                     (Tag, Name, nullptr, nullptr, nullptr, SizeInBits,
-                     AlignInBits, 0))
+                     AlignInBits, 0, nullptr))
   DEFINE_MDNODE_GET(DIStringType,
                     (unsigned Tag, MDString *Name, Metadata *StringLength,
                      Metadata *StringLengthExp, Metadata *StringLocationExp,
                      uint64_t SizeInBits, uint32_t AlignInBits,
-                     unsigned Encoding),
+                     unsigned Encoding, Metadata *CharType = nullptr),
                     (Tag, Name, StringLength, StringLengthExp,
-                     StringLocationExp, SizeInBits, AlignInBits, Encoding))
+                     StringLocationExp, SizeInBits, AlignInBits, Encoding,
+                     CharType))
   DEFINE_MDNODE_GET(DIStringType,
                     (unsigned Tag, StringRef Name, Metadata *StringLength,
                      Metadata *StringLengthExp, Metadata *StringLocationExp,
                      uint64_t SizeInBits, uint32_t AlignInBits,
-                     unsigned Encoding),
+                     unsigned Encoding, Metadata *CharType = nullptr),
                     (Tag, Name, StringLength, StringLengthExp,
-                     StringLocationExp, SizeInBits, AlignInBits, Encoding))
+                     StringLocationExp, SizeInBits, AlignInBits, Encoding,
+                     CharType))
   DEFINE_MDNODE_GET(DIStringType,
                     (unsigned Tag, MDString *Name, Metadata *StringLength,
                      Metadata *StringLengthExp, Metadata *StringLocationExp,
                      Metadata *SizeInBits, uint32_t AlignInBits,
-                     unsigned Encoding),
+                     unsigned Encoding, Metadata *CharType = nullptr),
                     (Tag, Name, StringLength, StringLengthExp,
-                     StringLocationExp, SizeInBits, AlignInBits, Encoding))
+                     StringLocationExp, SizeInBits, AlignInBits, Encoding,
+                     CharType))
 
   TempDIStringType clone() const { return cloneImpl(); }
 
@@ -1256,6 +1259,8 @@ class DIStringType : public DIType {
 
   unsigned getEncoding() const { return Encoding; }
 
+  DIType *getCharType() const { return cast_or_null<DIType>(getRawCharType()); }
+
   Metadata *getRawStringLength() const { return getOperand(MY_FIRST_OPERAND); }
 
   Metadata *getRawStringLengthExp() const {
@@ -1265,6 +1270,8 @@ class DIStringType : public DIType {
   Metadata *getRawStringLocationExp() const {
     return getOperand(MY_FIRST_OPERAND + 2);
   }
+
+  Metadata *getRawCharType() const { return getOperand(MY_FIRST_OPERAND + 3); }
 };
 
 /// Derived types.
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 93a79a7035e6f..96db98eb2320b 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -6035,7 +6035,8 @@ bool LLParser::parseDIStringType(MDNode *&Result, bool IsDistinct) {
   OPTIONAL(stringLocationExpression, MDField, );                               \
   OPTIONAL(size, MDUnsignedOrMDField, (0, UINT64_MAX));                        \
   OPTIONAL(align, MDUnsignedField, (0, UINT32_MAX));                           \
-  OPTIONAL(encoding, DwarfAttEncodingField, );
+  OPTIONAL(encoding, DwarfAttEncodingField, );                                 \
+  OPTIONAL(charType, MDField, );
   PARSE_MD_FIELDS();
 #undef VISIT_MD_FIELDS
 
@@ -6043,7 +6044,7 @@ bool LLParser::parseDIStringType(MDNode *&Result, bool IsDistinct) {
       DIStringType,
       (Context, tag.Val, name.Val, stringLength.Val, stringLengthExpression.Val,
        stringLocationExpression.Val, size.getValueAsMetadata(Context),
-       align.Val, encoding.Val));
+       align.Val, encoding.Val, charType.Val));
   return false;
 }
 
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 8b7beb1a8ff9e..21150835bebea 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -1654,7 +1654,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
     break;
   }
   case bitc::METADATA_STRING_TYPE: {
-    if (Record.size() > 9 || Record.size() < 8)
+    if (Record.size() > 10 || Record.size() < 8)
       return error("Invalid record");
 
     IsDistinct = Record[0] & 1;
@@ -1666,13 +1666,16 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
     unsigned Offset = SizeIs8 ? 5 : 6;
     Metadata *SizeInBits =
         getMetadataOrConstant(SizeIsMetadata, Record[Offset]);
+    Metadata *CharType = (Record.size() > Offset + 3)
+                             ? getMDOrNull(Record[Offset + 3])
+                             : nullptr;
 
     MetadataList.assignValue(
         GET_OR_DISTINCT(DIStringType,
                         (Context, Record[1], getMDString(Record[2]),
                          getMDOrNull(Record[3]), getMDOrNull(Record[4]),
                          StringLocationExp, SizeInBits, Record[Offset + 1],
-                         Record[Offset + 2])),
+                         Record[Offset + 2], CharType)),
         NextMetadataNo);
     NextMetadataNo++;
     break;
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 9fd39e2f901ec..2a9630eccc376 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -2058,6 +2058,7 @@ void ModuleBitcodeWriter::writeDIStringType(const DIStringType *N,
   Record.push_back(VE.getMetadataOrNullID(N->getRawSizeInBits()));
   Record.push_back(N->getAlignInBits());
   Record.push_back(N->getEncoding());
+  Record.push_back(VE.getMetadataOrNullID(N->getRawCharType()));
 
   Stream.EmitRecord(bitc::METADATA_STRING_TYPE, Record, Abbrev);
   Record.clear();
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index dd16095f0e823..80bdcca010f2d 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -859,6 +859,9 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DIStringType *STy) {
     addUInt(Buffer, dwarf::DW_AT_encoding, dwarf::DW_FORM_data1,
             STy->getEncoding());
   }
+
+  if (STy->getCharType())
+    addType(Buffer, STy->getCharType());
 }
 
 void DwarfUnit::constructTypeDIE(DIE &Buffer, const DIDerivedType *DTy) {
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index c3202eea12c28..7757c626d6fcf 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -2278,6 +2278,7 @@ static void writeDIStringType(raw_ostream &Out, const DIStringType *N,
   Printer.printInt("align", N->getAlignInBits());
   Printer.printDwarfEnum("encoding", N->getEncoding(),
                          dwarf::AttributeEncodingString);
+  Printer.printMetadata("charType", N->getRawCharType());
   Out << ")";
 }
 
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 134f1382dfc25..44adf445e6301 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -954,15 +954,15 @@ DIStringType *DIStringType::getImpl(LLVMContext &Context, unsigned Tag,
                                     Metadata *StringLengthExp,
                                     Metadata *StringLocationExp,
                                     Metadata *SizeInBits, uint32_t AlignInBits,
-                                    unsigned Encoding, StorageType Storage,
-                                    bool ShouldCreate) {
+                                    unsigned Encoding, Metadata *CharType,
+                                    StorageType Storage, bool ShouldCreate) {
   assert(isCanonical(Name) && "Expected canonical MDString");
-  DEFINE_GETIMPL_LOOKUP(DIStringType,
-                        (Tag, Name, StringLength, StringLengthExp,
-                         StringLocationExp, SizeInBits, AlignInBits, Encoding));
-  Metadata *Ops[] = {nullptr,         nullptr,          Name,
-                     SizeInBits,      nullptr,          StringLength,
-                     StringLengthExp, StringLocationExp};
+  DEFINE_GETIMPL_LOOKUP(DIStringType, (Tag, Name, StringLength, StringLengthExp,
+                                       StringLocationExp, SizeInBits,
+                                       AlignInBits, Encoding, CharType));
+  Metadata *Ops[] = {nullptr,         nullptr,           Name,
+                     SizeInBits,      nullptr,           StringLength,
+                     StringLengthExp, StringLocationExp, CharType};
   DEFINE_GETIMPL_STORE(DIStringType, (Tag, AlignInBits, Encoding), Ops);
 }
 DIType *DIDerivedType::getClassType() const {
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 41c8a92c56eda..9b27eb1997792 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -543,20 +543,23 @@ template <> struct MDNodeKeyImpl<DIStringType> {
   Metadata *SizeInBits;
   uint32_t AlignInBits;
   unsigned Encoding;
+  Metadata *CharType;
 
   MDNodeKeyImpl(unsigned Tag, MDString *Name, Metadata *StringLength,
                 Metadata *StringLengthExp, Metadata *StringLocationExp,
-                Metadata *SizeInBits, uint32_t AlignInBits, unsigned Encoding)
+                Metadata *SizeInBits, uint32_t AlignInBits, unsigned Encoding,
+                Metadata *CharType)
       : Tag(Tag), Name(Name), StringLength(StringLength),
         StringLengthExp(StringLengthExp), StringLocationExp(StringLocationExp),
-        SizeInBits(SizeInBits), AlignInBits(AlignInBits), Encoding(Encoding) {}
+        SizeInBits(SizeInBits), AlignInBits(AlignInBits), Encoding(Encoding),
+        CharType(CharType) {}
   MDNodeKeyImpl(const DIStringType *N)
       : Tag(N->getTag()), Name(N->getRawName()),
         StringLength(N->getRawStringLength()),
         StringLengthExp(N->getRawStringLengthExp()),
         StringLocationExp(N->getRawStringLocationExp()),
         SizeInBits(N->getRawSizeInBits()), AlignInBits(N->getAlignInBits()),
-        Encoding(N->getEncoding()) {}
+        Encoding(N->getEncoding()), CharType(N->getRawCharType()) {}
 
   bool isKeyOf(const DIStringType *RHS) const {
     return Tag == RHS->getTag() && Name == RHS->getRawName() &&
@@ -565,7 +568,7 @@ template <> struct MDNodeKeyImpl<DIStringType> {
            StringLocationExp == RHS->getRawStringLocationExp() &&
            SizeInBits == RHS->getRawSizeInBits() &&
            AlignInBits == RHS->getAlignInBits() &&
-           Encoding == RHS->getEncoding();
+           Encoding == RHS->getEncoding() && CharType == RHS->getRawCharType();
   }
   unsigned getHashValue() const {
     // Intentionally computes the hash on a subset of the operands for
diff --git a/llvm/test/DebugInfo/fortran-string-type.ll b/llvm/test/DebugInfo/fortran-string-type.ll
index e6fe3c5096d34..ac8bb96e64f91 100644
--- a/llvm/test/DebugInfo/fortran-string-type.ll
+++ b/llvm/test/DebugInfo/fortran-string-type.ll
@@ -8,6 +8,8 @@
 ; CHECK: !DIStringType(name: "character(10)", size: 80, align: 8)
 ; CHECK: !DIBasicType(tag: DW_TAG_string_type
 ; CHECK: !DIStringType(name: ".str.DEFERRED", stringLengthExpression: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 8), stringLocationExpression: !DIExpression(DW_OP_push_object_address, DW_OP_deref))
+; CHECK: !DIStringType(name: "character(10)_typed", size: 80, align: 8, charType: ![[CHARTYPE:[0-9]+]])
+; CHECK: ![[CHARTYPE]] = !DIBasicType(name: "character", size: 8, encoding: DW_ATE_unsigned_char)
 
 !llvm.module.flags = !{!0, !1}
 !llvm.dbg.cu = !{!2}
@@ -17,7 +19,7 @@
 !2 = distinct !DICompileUnit(language: DW_LANG_Fortran90, file: !3, producer: "Flang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !4, imports: !4)
 !3 = !DIFile(filename: "fortran-string-type.f", directory: "/")
 !4 = !{}
-!5 = !{!6, !9, !12, !13, !14}
+!5 = !{!6, !9, !12, !13, !14, !15}
 !6 = !DIStringType(name: "character(*)", stringLength: !7, stringLengthExpression: !DIExpression(), size: 32)
 !7 = !DILocalVariable(arg: 2, scope: !8, file: !3, line: 256, type: !11, flags: DIFlagArtificial)
 !8 = distinct !DISubprogram(name: "subprgm", scope: !2, file: !3, line: 256, type: !9, isLocal: false, isDefinition: true, scopeLine: 256, isOptimized: false, unit: !2)
@@ -27,3 +29,5 @@
 !12 = !DIStringType(name: "character(10)", size: 80, align: 8)
 !13 = !DIBasicType(tag: DW_TAG_string_type, name: "character")
 !14 = !DIStringType(name: ".str.DEFERRED", stringLengthExpression: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 8), stringLocationExpression: !DIExpression(DW_OP_push_object_address, DW_OP_deref))
+!15 = !DIStringType(name: "character(10)_typed", size: 80, align: 8, charType: !16)
+!16 = !DIBasicType(name: "character", size: 8, encoding: DW_ATE_unsigned_char)

>From 7fb27bdfe651a3521f5018db69d42e63830d9aab Mon Sep 17 00:00:00 2001
From: Timothy Smith <timothy.smith at hpe.com>
Date: Thu, 17 Sep 2026 14:46:26 -0500
Subject: [PATCH 2/7] Implement code review feedback

---
 llvm/docs/LangRef.md                    | 4 +++-
 llvm/lib/IR/LLVMContextImpl.h           | 2 +-
 llvm/lib/IR/Verifier.cpp                | 2 ++
 llvm/test/DebugInfo/X86/distringtype.ll | 4 +++-
 4 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 4853b3466467c..20bb3f0fdbca2 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -7346,10 +7346,12 @@ The `name:` field is mandatory. The `configMacros:`, `includePath:`,
 dynamic length and location encoded as an expression.
 The `tag:` field is optional and defaults to `DW_TAG_string_type`. The `name:`,
 `stringLength:`, `stringLengthExpression`, `stringLocationExpression:`,
-`size:`, `align:`, and `encoding:` fields are optional.
+`size:`, `align:`, `encoding:`, and `charType:` fields are optional.
 
 If not present, the `size:` and `align:` fields default to the value zero.
 
+`charType:` allows representation of non-default character types.
+
 The length in bits of the string is specified by the first of the following
 fields present:
 
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 9b27eb1997792..249bcdc5912f4 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -575,7 +575,7 @@ template <> struct MDNodeKeyImpl<DIStringType> {
     // performance reason. The subset has to be significant enough to avoid
     // collision "most of the time". There is no correctness issue in case of
     // collision because of the full check above.
-    return hash_combine(Tag, Name, StringLength, Encoding);
+    return hash_combine(Tag, Name, StringLength, Encoding, CharType);
   }
 };
 
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 4e804425b751d..3fbd9b5147b8c 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1233,6 +1233,8 @@ void Verifier::visitDIStringType(const DIStringType &N) {
   CheckDI(N.getTag() == dwarf::DW_TAG_string_type, "invalid tag", &N);
   CheckDI(!(N.isBigEndian() && N.isLittleEndian()), "has conflicting flags",
           &N);
+  if (N.getCharType())
+    CheckDI(isa<DIType>(N.getCharType()), "invalid character type", &N);
 }
 
 void Verifier::visitDIDerivedType(const DIDerivedType &N) {
diff --git a/llvm/test/DebugInfo/X86/distringtype.ll b/llvm/test/DebugInfo/X86/distringtype.ll
index 2930c3aed0485..4c655c4853ade 100644
--- a/llvm/test/DebugInfo/X86/distringtype.ll
+++ b/llvm/test/DebugInfo/X86/distringtype.ll
@@ -15,6 +15,7 @@
 ; CHECK:                          DW_AT_name  (".str.DEFERRED")
 ; CHECK-NEXT:                     DW_AT_string_length (DW_OP_push_object_address, DW_OP_plus_uconst 0x8)
 ; CHECK-NEXT:                     DW_AT_data_location (DW_OP_push_object_address, DW_OP_deref)
+; CHECK-NEXT:                     DW_AT_type ({{.*}}"character")
 ; CHECK:       DW_TAG_string_type
 ; CHECK:                          DW_AT_name  ("character(*)!2")
 ; CHECK-NEXT:                     DW_AT_string_length
@@ -152,7 +153,7 @@ attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
 !6 = distinct !DICompileUnit(language: DW_LANG_Fortran95, file: !3, producer: "Intel(R) Fortran 21.0-2142", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !7, splitDebugInlining: false, nameTableKind: None)
 !7 = !{}
 !8 = !{!0}
-!9 = !DIStringType(name: ".str.DEFERRED", stringLengthExpression: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 8), stringLocationExpression: !DIExpression(DW_OP_push_object_address, DW_OP_deref))
+!9 = !DIStringType(name: ".str.DEFERRED", stringLengthExpression: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 8), stringLocationExpression: !DIExpression(DW_OP_push_object_address, DW_OP_deref), charType: !28)
 !10 = !{i32 2, !"Debug Info Version", i32 3}
 !11 = !{i32 2, !"Dwarf Version", i32 4}
 !12 = !DILocation(line: 1, column: 9, scope: !2)
@@ -171,3 +172,4 @@ attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
 !25 = !DILocation(line: 7, column: 14, scope: !17)
 !26 = !DILocation(line: 10, column: 11, scope: !17)
 !27 = !DILocation(line: 11, column: 3, scope: !17)
+!28 = !DIBasicType(name: "character", size: 8, encoding: DW_ATE_unsigned_char)

>From b6139b6122d4ad1bc3e0dc0e60c3d622cd3065b1 Mon Sep 17 00:00:00 2001
From: Timothy Smith <timothy.smith at hpe.com>
Date: Thu, 17 Sep 2026 15:46:26 -0500
Subject: [PATCH 3/7] Rerun checks


>From 83e5c550190b555ceeeb616ae84111028a8edbb9 Mon Sep 17 00:00:00 2001
From: Timothy Smith <timothy.smith at hpe.com>
Date: Thu, 24 Sep 2026 16:50:45 -0500
Subject: [PATCH 4/7] Add verifier check for character types in DIStringType

---
 llvm/lib/IR/Verifier.cpp                          |  5 +++--
 llvm/test/Verifier/distringtype-character-type.ll | 14 ++++++++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/Verifier/distringtype-character-type.ll

diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 3fbd9b5147b8c..05e7b9c8a4488 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1233,8 +1233,9 @@ void Verifier::visitDIStringType(const DIStringType &N) {
   CheckDI(N.getTag() == dwarf::DW_TAG_string_type, "invalid tag", &N);
   CheckDI(!(N.isBigEndian() && N.isLittleEndian()), "has conflicting flags",
           &N);
-  if (N.getCharType())
-    CheckDI(isa<DIType>(N.getCharType()), "invalid character type", &N);
+  if (N.getRawCharType())
+    CheckDI(isa<DIType>(N.getRawCharType()), "invalid character type", &N,
+            N.getRawCharType());
 }
 
 void Verifier::visitDIDerivedType(const DIDerivedType &N) {
diff --git a/llvm/test/Verifier/distringtype-character-type.ll b/llvm/test/Verifier/distringtype-character-type.ll
new file mode 100644
index 0000000000000..7dfd5d93fc938
--- /dev/null
+++ b/llvm/test/Verifier/distringtype-character-type.ll
@@ -0,0 +1,14 @@
+; Verify that DIStringType rejects a `charType:` that is not a DIType.
+
+; RUN: llvm-as -disable-output < %s 2>&1 | FileCheck %s
+
+; CHECK: invalid character type
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!1}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_Fortran95, file: !2, producer: "flang", emissionKind: FullDebug, retainedTypes: !3)
+!1 = !{i32 2, !"Debug Info Version", i32 3}
+!2 = !DIFile(filename: "string.f90", directory: "/")
+!3 = !{!4}
+!4 = !DIStringType(name: "character(4)", charType: !2, size: 32)

>From 9f30f718b1568d8900d90302f7de51185668b3c3 Mon Sep 17 00:00:00 2001
From: Timothy Smith <timothy.smith at hpe.com>
Date: Tue, 29 Sep 2026 16:27:13 -0500
Subject: [PATCH 5/7] Clarified documentation and re-implemented the MD loader
 for DIStringType in a way that makes the permitted record sizes and field
 definitions more clear.

---
 llvm/docs/LangRef.md                       |  2 +-
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 31 +++++++++++++---------
 2 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 20bb3f0fdbca2..99c98079f502d 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -7350,7 +7350,7 @@ The `tag:` field is optional and defaults to `DW_TAG_string_type`. The `name:`,
 
 If not present, the `size:` and `align:` fields default to the value zero.
 
-`charType:` allows representation of non-default character types.
+`charType:` specifies a non-default character types.
 
 The length in bits of the string is specified by the first of the following
 fields present:
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 21150835bebea..dddb19b8e32b1 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -1659,23 +1659,30 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
 
     IsDistinct = Record[0] & 1;
     bool SizeIsMetadata = Record[0] & 2;
-    bool SizeIs8 = Record.size() == 8;
-    // StringLocationExp (i.e. Record[5]) is added at a later time
-    // than the other fields. The code here enables backward compatibility.
-    Metadata *StringLocationExp = SizeIs8 ? nullptr : getMDOrNull(Record[5]);
-    unsigned Offset = SizeIs8 ? 5 : 6;
-    Metadata *SizeInBits =
-        getMetadataOrConstant(SizeIsMetadata, Record[Offset]);
-    Metadata *CharType = (Record.size() > Offset + 3)
-                             ? getMDOrNull(Record[Offset + 3])
-                             : nullptr;
+    // StringLocationExp (i.e. Record[5]) was added at a later time
+    // than most of the other fields, and CharType (Record[9]) was
+    // added even later.
+    // The code here enables backward compatibility.
+    Metadata *StringLocationExp = nullptr;
+    Metadata *CharType = nullptr;
+
+    bool StringLocPresent = Record.size() > 8;
+    size_t SizeOffset = StringLocPresent ? 6 : 5;
+
+    Metadata *SizeInBits = getMetadataOrConstant(SizeIsMetadata, Record[SizeOffset]);
+    if (StringLocPresent) {
+      StringLocationExp = getMDOrNull(Record[5]);
+    }
+    if (Record.size() == 10) {
+      CharType = getMDOrNull(Record[9]);
+    }
 
     MetadataList.assignValue(
         GET_OR_DISTINCT(DIStringType,
                         (Context, Record[1], getMDString(Record[2]),
                          getMDOrNull(Record[3]), getMDOrNull(Record[4]),
-                         StringLocationExp, SizeInBits, Record[Offset + 1],
-                         Record[Offset + 2], CharType)),
+                         StringLocationExp, SizeInBits, Record[SizeOffset + 1],
+                         Record[SizeOffset + 2], CharType)),
         NextMetadataNo);
     NextMetadataNo++;
     break;

>From 3c1ac8a9f1a774411c4001f8d3884f0944044fb5 Mon Sep 17 00:00:00 2001
From: Timothy Smith <timothy.smith at hpe.com>
Date: Tue, 29 Sep 2026 16:27:37 -0500
Subject: [PATCH 6/7] Fixed formatting

---
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index dddb19b8e32b1..24a4d7ae17983 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -1669,7 +1669,8 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
     bool StringLocPresent = Record.size() > 8;
     size_t SizeOffset = StringLocPresent ? 6 : 5;
 
-    Metadata *SizeInBits = getMetadataOrConstant(SizeIsMetadata, Record[SizeOffset]);
+    Metadata *SizeInBits =
+        getMetadataOrConstant(SizeIsMetadata, Record[SizeOffset]);
     if (StringLocPresent) {
       StringLocationExp = getMDOrNull(Record[5]);
     }

>From 0e4c8134fd8cdd7c3a68ed277ebcb502fdfa1a5f Mon Sep 17 00:00:00 2001
From: Timothy Smith <timothy.smith at hpe.com>
Date: Wed, 30 Sep 2026 14:46:23 -0500
Subject: [PATCH 7/7] Fix grammar error in docs

---
 llvm/docs/LangRef.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 99c98079f502d..6e8c950d795e1 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -7350,7 +7350,7 @@ The `tag:` field is optional and defaults to `DW_TAG_string_type`. The `name:`,
 
 If not present, the `size:` and `align:` fields default to the value zero.
 
-`charType:` specifies a non-default character types.
+`charType:` specifies a non-default character type.
 
 The length in bits of the string is specified by the first of the following
 fields present:



More information about the llvm-commits mailing list