Skip to content

Commit f36db0f

Browse files
committed
[DebugInfo] Fully implement DWARF issue 180201.1
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 c372a2c commit f36db0f

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
@@ -4783,9 +4783,13 @@ struct MDField : public MDFieldImpl<Metadata *> {
47834783
};
47844784

47854785
struct MDStringField : public MDFieldImpl<MDString *> {
4786-
bool AllowEmpty;
4787-
MDStringField(bool AllowEmpty = true)
4788-
: ImplTy(nullptr), AllowEmpty(AllowEmpty) {}
4786+
enum class EmptyIs {
4787+
Null, //< Allow empty input string, map to nullptr
4788+
Empty, //< Allow empty input string, map to an empty MDString
4789+
Error, //< Disallow empty string, map to an error
4790+
} EmptyIs;
4791+
MDStringField(enum EmptyIs EmptyIs = EmptyIs::Null)
4792+
: ImplTy(nullptr), EmptyIs(EmptyIs) {}
47894793
};
47904794

47914795
struct MDFieldList : public MDFieldImpl<SmallVector<Metadata *, 4>> {
@@ -5257,10 +5261,19 @@ bool LLParser::parseMDField(LocTy Loc, StringRef Name, MDStringField &Result) {
52575261
if (parseStringConstant(S))
52585262
return true;
52595263

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

5263-
Result.assign(S.empty() ? nullptr : MDString::get(Context, S));
5276+
Result.assign(MDString::get(Context, S));
52645277
return false;
52655278
}
52665279

@@ -5778,7 +5791,7 @@ bool LLParser::parseDIFile(MDNode *&Result, bool IsDistinct) {
57785791
REQUIRED(directory, MDStringField, ); \
57795792
OPTIONAL(checksumkind, ChecksumKindField, (DIFile::CSK_MD5)); \
57805793
OPTIONAL(checksum, MDStringField, ); \
5781-
OPTIONAL(source, MDStringField, );
5794+
OPTIONAL(source, MDStringField, (MDStringField::EmptyIs::Empty));
57825795
PARSE_MD_FIELDS();
57835796
#undef VISIT_MD_FIELDS
57845797

@@ -6062,7 +6075,7 @@ bool LLParser::parseDITemplateValueParameter(MDNode *&Result, bool IsDistinct) {
60626075
/// declaration: !4, align: 8)
60636076
bool LLParser::parseDIGlobalVariable(MDNode *&Result, bool IsDistinct) {
60646077
#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \
6065-
OPTIONAL(name, MDStringField, (/* AllowEmpty */ false)); \
6078+
OPTIONAL(name, MDStringField, (MDStringField::EmptyIs::Error)); \
60666079
OPTIONAL(scope, MDField, ); \
60676080
OPTIONAL(linkageName, MDStringField, ); \
60686081
OPTIONAL(file, MDField, ); \

llvm/lib/IR/AsmWriter.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,8 +2398,9 @@ static void writeDIFile(raw_ostream &Out, const DIFile *N, AsmWriterContext &) {
23982398
// Print all values for checksum together, or not at all.
23992399
if (N->getChecksum())
24002400
Printer.printChecksum(*N->getChecksum());
2401-
Printer.printString("source", N->getSource().value_or(StringRef()),
2402-
/* ShouldSkipEmpty */ true);
2401+
if (N->getSource())
2402+
Printer.printString("source", *N->getSource(),
2403+
/* ShouldSkipEmpty */ false);
24032404
Out << ")";
24042405
}
24052406

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)