Skip to content

Commit 354944d

Browse files
authored
[DebugInfo] Fully implement DWARF issue 180201.1 (#149226)
Finish making LLVM's implementation of `DW_LNCT_LLVM_source` conform to the final accepted version of `DW_LNCT_source` from https://dwarfstd.org/issues/180201.1.html This is effectively a continuation of a few commits which have moved in this direction already, including: * c9cb4fc [DebugInfo] Store optional DIFile::Source as pointer * 87e22bd Allow for mixing source/no-source DIFiles in one CU This patch: * Teaches LLParser that there is a distinction between an empty and an absent "source:" field on DIFile. * Makes printing the "source:" field in AsmWriter conditional on it being present, instead of being conditional on it being non-empty. * Teaches MC to map an empty-but-present source field to "\n" (which is ambiguous, making the source strings "" and "\n" indistinguishable, but that's what the DWARF issue specifies). Add a test for round-tripping an empty source field through assembler/bitcode. Extend the test for the actual DWARF generation so it covers all of the cases (absent, present-but-empty, present-and-ambiguously-single-newline, present).
1 parent 97a66a8 commit 354944d

File tree

5 files changed

+89
-26
lines changed

5 files changed

+89
-26
lines changed

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4786,9 +4786,13 @@ struct MDField : public MDFieldImpl<Metadata *> {
47864786
};
47874787

47884788
struct MDStringField : public MDFieldImpl<MDString *> {
4789-
bool AllowEmpty;
4790-
MDStringField(bool AllowEmpty = true)
4791-
: ImplTy(nullptr), AllowEmpty(AllowEmpty) {}
4789+
enum class EmptyIs {
4790+
Null, //< Allow empty input string, map to nullptr
4791+
Empty, //< Allow empty input string, map to an empty MDString
4792+
Error, //< Disallow empty string, map to an error
4793+
} EmptyIs;
4794+
MDStringField(enum EmptyIs EmptyIs = EmptyIs::Null)
4795+
: ImplTy(nullptr), EmptyIs(EmptyIs) {}
47924796
};
47934797

47944798
struct MDFieldList : public MDFieldImpl<SmallVector<Metadata *, 4>> {
@@ -5260,10 +5264,19 @@ bool LLParser::parseMDField(LocTy Loc, StringRef Name, MDStringField &Result) {
52605264
if (parseStringConstant(S))
52615265
return true;
52625266

5263-
if (!Result.AllowEmpty && S.empty())
5264-
return error(ValueLoc, "'" + Name + "' cannot be empty");
5267+
if (S.empty()) {
5268+
switch (Result.EmptyIs) {
5269+
case MDStringField::EmptyIs::Null:
5270+
Result.assign(nullptr);
5271+
return false;
5272+
case MDStringField::EmptyIs::Empty:
5273+
break;
5274+
case MDStringField::EmptyIs::Error:
5275+
return error(ValueLoc, "'" + Name + "' cannot be empty");
5276+
}
5277+
}
52655278

5266-
Result.assign(S.empty() ? nullptr : MDString::get(Context, S));
5279+
Result.assign(MDString::get(Context, S));
52675280
return false;
52685281
}
52695282

@@ -5781,7 +5794,7 @@ bool LLParser::parseDIFile(MDNode *&Result, bool IsDistinct) {
57815794
REQUIRED(directory, MDStringField, ); \
57825795
OPTIONAL(checksumkind, ChecksumKindField, (DIFile::CSK_MD5)); \
57835796
OPTIONAL(checksum, MDStringField, ); \
5784-
OPTIONAL(source, MDStringField, );
5797+
OPTIONAL(source, MDStringField, (MDStringField::EmptyIs::Empty));
57855798
PARSE_MD_FIELDS();
57865799
#undef VISIT_MD_FIELDS
57875800

@@ -6065,7 +6078,7 @@ bool LLParser::parseDITemplateValueParameter(MDNode *&Result, bool IsDistinct) {
60656078
/// declaration: !4, align: 8)
60666079
bool LLParser::parseDIGlobalVariable(MDNode *&Result, bool IsDistinct) {
60676080
#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \
6068-
OPTIONAL(name, MDStringField, (/* AllowEmpty */ false)); \
6081+
OPTIONAL(name, MDStringField, (MDStringField::EmptyIs::Error)); \
60696082
OPTIONAL(scope, MDField, ); \
60706083
OPTIONAL(linkageName, MDStringField, ); \
60716084
OPTIONAL(file, MDField, ); \

llvm/lib/IR/AsmWriter.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,8 +2401,9 @@ static void writeDIFile(raw_ostream &Out, const DIFile *N, AsmWriterContext &) {
24012401
// Print all values for checksum together, or not at all.
24022402
if (N->getChecksum())
24032403
Printer.printChecksum(*N->getChecksum());
2404-
Printer.printString("source", N->getSource().value_or(StringRef()),
2405-
/* ShouldSkipEmpty */ true);
2404+
if (N->getSource())
2405+
Printer.printString("source", *N->getSource(),
2406+
/* ShouldSkipEmpty */ false);
24062407
Out << ")";
24072408
}
24082409

llvm/lib/MC/MCDwarf.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,10 +447,17 @@ static void emitOneV5FileEntry(MCStreamer *MCOS, const MCDwarfFile &DwarfFile,
447447
StringRef(reinterpret_cast<const char *>(Cksum.data()), Cksum.size()));
448448
}
449449
if (HasAnySource) {
450+
// From https://dwarfstd.org/issues/180201.1.html
451+
// * The value is an empty null-terminated string if no source is available
452+
StringRef Source = DwarfFile.Source.value_or(StringRef());
453+
// * If the source is available but is an empty file then the value is a
454+
// null-terminated single "\n".
455+
if (DwarfFile.Source && DwarfFile.Source->empty())
456+
Source = "\n";
450457
if (LineStr)
451-
LineStr->emitRef(MCOS, DwarfFile.Source.value_or(StringRef()));
458+
LineStr->emitRef(MCOS, Source);
452459
else {
453-
MCOS->emitBytes(DwarfFile.Source.value_or(StringRef())); // Source and...
460+
MCOS->emitBytes(Source); // Source and...
454461
MCOS->emitBytes(StringRef("\0", 1)); // its null terminator.
455462
}
456463
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s
2+
; RUN: verify-uselistorder
3+
4+
; CHECK: !DIFile({{.*}}, source: "")
5+
6+
!llvm.dbg.cu = !{!0}
7+
!llvm.module.flags = !{!2, !3}
8+
9+
!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, emissionKind: FullDebug)
10+
!1 = !DIFile(filename: "-", directory: "/", checksumkind: CSK_MD5, checksum: "d41d8cd98f00b204e9800998ecf8427e", source: "")
11+
!2 = !{i32 7, !"Dwarf Version", i32 5}
12+
!3 = !{i32 2, !"Debug Info Version", i32 3}

llvm/test/DebugInfo/Generic/mixed-source.ll

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,66 @@
55

66
; CHECK: include_directories[ 0] = "dir"
77
; CHECK-NEXT: file_names[ 0]:
8+
; CHECK-NEXT: name: "main.c"
9+
; CHECK-NEXT: dir_index: 0
10+
; CHECK-NOT: source:
11+
; CHECK-NEXT: file_names[ 1]:
812
; CHECK-NEXT: name: "foo.c"
913
; CHECK-NEXT: dir_index: 0
1014
; CHECK-NEXT: source: "void foo() { }\n"
11-
; CHECK-NEXT: file_names[ 1]:
12-
; CHECK-NEXT: name: "bar.h"
15+
; CHECK-NEXT: file_names[ 2]:
16+
; CHECK-NEXT: name: "newline.h"
17+
; CHECK-NEXT: dir_index: 0
18+
; CHECK-NEXT: source: "\n"
19+
; CHECK-NEXT: file_names[ 3]:
20+
; CHECK-NEXT: name: "empty.h"
21+
; CHECK-NEXT: dir_index: 0
22+
; CHECK-NEXT: source: "\n"
23+
; CHECK-NEXT: file_names[ 4]:
24+
; CHECK-NEXT: name: "absent.h"
1325
; CHECK-NEXT: dir_index: 0
1426
; CHECK-NOT: source:
1527

1628
; Test that DIFiles mixing source and no-source within a DICompileUnit works.
1729

18-
define dso_local void @foo() !dbg !5 {
30+
define dso_local void @foo() !dbg !6 {
1931
ret void, !dbg !7
2032
}
2133

22-
define dso_local void @bar() !dbg !6 {
23-
ret void, !dbg !8
34+
define dso_local void @newline() !dbg !9 {
35+
ret void, !dbg !10
2436
}
2537

26-
!llvm.dbg.cu = !{!4}
38+
define dso_local void @empty() !dbg !12 {
39+
ret void, !dbg !13
40+
}
41+
42+
define dso_local void @absent() !dbg !15 {
43+
ret void, !dbg !16
44+
}
45+
46+
!llvm.dbg.cu = !{!2}
2747
!llvm.module.flags = !{!0, !1}
2848

2949
!0 = !{i32 2, !"Dwarf Version", i32 5}
3050
!1 = !{i32 2, !"Debug Info Version", i32 3}
3151

32-
!2 = !DIFile(filename: "foo.c", directory: "dir", source: "void foo() { }\0A")
33-
!3 = !DIFile(filename: "bar.h", directory: "dir")
52+
!2 = distinct !DICompileUnit(language: DW_LANG_C99, emissionKind: FullDebug, file: !4)
53+
!3 = !DISubroutineType(types: !{})
54+
!4 = !DIFile(filename: "main.c", directory: "dir")
55+
56+
!5 = !DIFile(filename: "foo.c", directory: "dir", source: "void foo() { }\0A")
57+
!6 = distinct !DISubprogram(name: "foo", file: !5, line: 1, type: !3, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2)
58+
!7 = !DILocation(line: 1, scope: !6)
59+
60+
!8 = !DIFile(filename: "newline.h", directory: "dir", source: "\0A")
61+
!9 = distinct !DISubprogram(name: "newline", file: !8, line: 1, type: !3, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2)
62+
!10 = !DILocation(line: 1, scope: !9)
63+
64+
!11 = !DIFile(filename: "empty.h", directory: "dir", source: "")
65+
!12 = distinct !DISubprogram(name: "empty", file: !11, line: 1, type: !3, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2)
66+
!13 = !DILocation(line: 1, scope: !12)
3467

35-
!4 = distinct !DICompileUnit(language: DW_LANG_C99, emissionKind: FullDebug, file: !2)
36-
!5 = distinct !DISubprogram(name: "foo", file: !2, line: 1, type: !9, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !4)
37-
!6 = distinct !DISubprogram(name: "bar", file: !3, line: 1, type: !9, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !4)
38-
!7 = !DILocation(line: 1, scope: !5)
39-
!8 = !DILocation(line: 1, scope: !6)
40-
!9 = !DISubroutineType(types: !{})
68+
!14 = !DIFile(filename: "absent.h", directory: "dir")
69+
!15 = distinct !DISubprogram(name: "absent", file: !14, line: 1, type: !3, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2)
70+
!16 = !DILocation(line: 1, scope: !15)

0 commit comments

Comments
 (0)