Skip to content

Commit e731a26

Browse files
committed
[DebugInfo][Split DWARF][LTO]: Ensure only a single CU is emitted
Split DWARF doesn't handle LTO of any form (roughly there's an assumption that each dwo file will have one CU - it's not explicitly documented, nor explicitly handled, so the ecosystem isn't really well understood/tested/etc). This had previously been handled by implementing (& disabling by default) the `-split-dwarf-cross-cu-references` flag, which would disable use of ref_addr across two dwo CUs. This worked for a while, at least in LTO (it didn't address Split DWARF+Full LTO, but that's an unlikely combination, as the benefits of Split DWARF are more limited in a full LTO build) - because the only source of cross-CU references was inlined functions, so by making those non-cross-CU (by moving the referenced inlined function DWARF description into the referencing CU) the result was one CU per dwo. But recently the Function Specialization pass was added to the ThinLTO pipeline, which caused imported functions that may not be inlined to be emitted by a backend compile. This meant foreign CU entities (not just abstract origins/cross-CU referenced entities)/standalone foreign CUs could be emitted by a backend compile. The end result was, due to a bug* in binutils dwp (I think basically it saw two CUs in a single dwo and reprocessed the offsets in the shared debug_str_offsets.dwo section) this situation lead to corrupted strings. So to make this more robust, I've generalized the definition of the `-split-dwarf-cross-cu-references` flag (perhaps it should be renamed at this point, but it's /really/ niche, doubt anyone's using it - more or less there for experimentation when we get around to figuring out spec'ing LTO+Split DWARF) to mean "single CU in a dwo file" and added more general handling for this. There's certainly some weird corner cases that could come up in terms of "how do we choose which CU to put everything in" - for now it's "first come, first served" which is probably going to be OK for ThinLTO - the base module will have the first functions and first CU, imported fragments will come after that. For LTO the choice will be fairly arbitrary - but, again, essentially whichever module comes first. * Arguably a bug in binutils dwp, but since the feature isn't well specified, I'd rather avoid dabbling in this uncertain area and ensure LLVM doesn't produce especially novel DWARF (dwos with multiple CUs) regardless of whether binutils dwp would/should be fixed. I'm not confident debuggers could read such a dwo file well, etc.
1 parent 0038d6c commit e731a26

File tree

5 files changed

+48
-47
lines changed

5 files changed

+48
-47
lines changed

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp

+14-1
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,13 @@ DwarfDebug::getOrCreateDwarfCompileUnit(const DICompileUnit *DIUnit) {
10941094
if (auto *CU = CUMap.lookup(DIUnit))
10951095
return *CU;
10961096

1097+
if (useSplitDwarf() &&
1098+
!shareAcrossDWOCUs() &&
1099+
(!DIUnit->getSplitDebugInlining() ||
1100+
DIUnit->getEmissionKind() == DICompileUnit::FullDebug) &&
1101+
!CUMap.empty()) {
1102+
return *CUMap.begin()->second;
1103+
}
10971104
CompilationDir = DIUnit->getDirectory();
10981105

10991106
auto OwnedUnit = std::make_unique<DwarfCompileUnit>(
@@ -1297,6 +1304,8 @@ void DwarfDebug::finalizeModuleInfo() {
12971304
if (CUMap.size() > 1)
12981305
DWOName = Asm->TM.Options.MCOptions.SplitDwarfFile;
12991306

1307+
bool HasEmittedSplitCU = false;
1308+
13001309
// Handle anything that needs to be done on a per-unit basis after
13011310
// all other generation.
13021311
for (const auto &P : CUMap) {
@@ -1315,6 +1324,10 @@ void DwarfDebug::finalizeModuleInfo() {
13151324
bool HasSplitUnit = SkCU && !TheCU.getUnitDie().children().empty();
13161325

13171326
if (HasSplitUnit) {
1327+
(void)HasEmittedSplitCU;
1328+
assert((shareAcrossDWOCUs() || !HasEmittedSplitCU) &&
1329+
"Multiple CUs emitted into a single dwo file");
1330+
HasEmittedSplitCU = true;
13181331
dwarf::Attribute attrDWOName = getDwarfVersion() >= 5
13191332
? dwarf::DW_AT_dwo_name
13201333
: dwarf::DW_AT_GNU_dwo_name;
@@ -2267,7 +2280,7 @@ void DwarfDebug::endFunctionImpl(const MachineFunction *MF) {
22672280

22682281
LexicalScope *FnScope = LScopes.getCurrentFunctionScope();
22692282
assert(!FnScope || SP == FnScope->getScopeNode());
2270-
DwarfCompileUnit &TheCU = *CUMap.lookup(SP->getUnit());
2283+
DwarfCompileUnit &TheCU = getOrCreateDwarfCompileUnit(SP->getUnit());
22712284
if (TheCU.getCUNode()->isDebugDirectivesOnly()) {
22722285
PrevLabel = nullptr;
22732286
CurFn = nullptr;

llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ void DwarfUnit::addAccess(DIE &Die, DINode::DIFlags Flags) {
543543
}
544544

545545
DIE *DwarfUnit::getOrCreateContextDIE(const DIScope *Context) {
546-
if (!Context || isa<DIFile>(Context))
546+
if (!Context || isa<DIFile>(Context) || isa<DICompileUnit>(Context))
547547
return &getUnitDie();
548548
if (auto *T = dyn_cast<DIType>(Context))
549549
return getOrCreateTypeDIE(T);

llvm/test/DebugInfo/X86/split-dwarf-cross-cu-gmlt-g.ll

+11-11
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
; CHECK-NEXT: DW_AT_decl_file (0x02)
99
; CHECK-NEXT: DW_AT_decl_line (4)
1010

11-
; Function Attrs: noinline nounwind optnone uwtable mustprogress
12-
define dso_local void @_Z2f1v() local_unnamed_addr #0 !dbg !12 {
11+
; Function Attrs: norecurse nounwind uwtable mustprogress
12+
define dso_local i32 @main() local_unnamed_addr #2 !dbg !26 {
1313
entry:
14-
ret void, !dbg !15
14+
tail call void @_Z2f1v() #3, !dbg !28
15+
tail call void @_Z2f1v() #3, !dbg !30
16+
ret i32 0, !dbg !32
1517
}
1618

1719
; Function Attrs: nounwind uwtable mustprogress
@@ -21,21 +23,19 @@ entry:
2123
ret void, !dbg !22
2224
}
2325

26+
; Function Attrs: noinline nounwind optnone uwtable mustprogress
27+
define dso_local void @_Z2f1v() local_unnamed_addr #0 !dbg !12 {
28+
entry:
29+
ret void, !dbg !15
30+
}
31+
2432
; Function Attrs: nounwind uwtable mustprogress
2533
define dso_local void @_Z2f2v() local_unnamed_addr #1 !dbg !23 {
2634
entry:
2735
tail call void @_Z2f1v(), !dbg !24
2836
ret void, !dbg !25
2937
}
3038

31-
; Function Attrs: norecurse nounwind uwtable mustprogress
32-
define dso_local i32 @main() local_unnamed_addr #2 !dbg !26 {
33-
entry:
34-
tail call void @_Z2f1v() #3, !dbg !28
35-
tail call void @_Z2f1v() #3, !dbg !30
36-
ret i32 0, !dbg !32
37-
}
38-
3939
attributes #0 = { noinline nounwind optnone uwtable mustprogress "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
4040
attributes #1 = { nounwind uwtable mustprogress "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
4141
attributes #2 = { norecurse nounwind uwtable mustprogress "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

llvm/test/DebugInfo/X86/split-dwarf-cross-unit-reference.ll

+15-22
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
; RUN: llc -mtriple=x86_64-linux -split-dwarf-cross-cu-references -split-dwarf-file=foo.dwo -filetype=obj -o %t < %s
2-
; RUN: llvm-objdump -r %t | FileCheck %s
3-
; RUN: llvm-dwarfdump -v -debug-info %t | FileCheck --check-prefix=ALL --check-prefix=INFO --check-prefix=DWO --check-prefix=CROSS %s
4-
; RUN: llvm-dwarfdump -v -debug-info %t | FileCheck --check-prefix=ALL --check-prefix=INFO %s
2+
; RUN: llvm-objdump -r %t | FileCheck --check-prefix=CHECK --check-prefix=RELO_CROSS %s
3+
; RUN: llvm-dwarfdump -v -debug-info %t | FileCheck --check-prefix=ALL --check-prefix=DWO --check-prefix=CROSS %s
4+
; RUN: llvm-dwarfdump -v -debug-info %t | FileCheck --check-prefix=ALL %s
55

66
; RUN: llc -mtriple=x86_64-linux -split-dwarf-file=foo.dwo -filetype=obj -o %t < %s
7-
; RUN: llvm-objdump -r %t | FileCheck %s
7+
; RUN: llvm-objdump -r %t | FileCheck --check-prefix=CHECK %s
88
; RUN: llvm-dwarfdump -v -debug-info %t | FileCheck --check-prefix=ALL --check-prefix=DWO --check-prefix=NOCROSS %s
9-
; RUN: llvm-dwarfdump -v -debug-info %t | FileCheck --check-prefix=ALL --check-prefix=INFO %s
9+
; RUN: llvm-dwarfdump -v -debug-info %t | FileCheck --check-prefix=ALL %s
1010

1111
; Testing cross-CU references for types, subprograms, and variables
1212
; Built from code something like this:
@@ -48,8 +48,8 @@
4848
; CHECK-NOT: RELOCATION RECORDS
4949
; Expect one relocation in debug_info, from the inlined f1 in foo to its
5050
; abstract origin in bar
51-
; CHECK: R_X86_64_32 .debug_info
52-
; CHECK-NOT: RELOCATION RECORDS
51+
; RELO_CROSS: R_X86_64_32 .debug_info
52+
; Expect no relocations in debug_info when disabling multiple CUs in Split DWARF
5353
; CHECK-NOT: .debug_info
5454
; CHECK: RELOCATION RECORDS
5555
; CHECK-NOT: .rel{{a?}}.debug_info.dwo
@@ -75,29 +75,22 @@
7575
; DWO: DW_TAG_formal_parameter
7676
; DWO: DW_AT_abstract_origin [DW_FORM_ref4] {{.*}}{0x[[F1T]]}
7777

78-
; ALL: Compile Unit
79-
; ALL: DW_TAG_compile_unit
80-
; DWO: DW_AT_name {{.*}} "bar.cpp"
81-
; NOCROSS: 0x[[BAR_F1:.*]]: DW_TAG_subprogram
82-
; NOCROSS: DW_AT_name {{.*}} "f1"
83-
; NOCROSS: 0x[[BAR_F1T:.*]]: DW_TAG_formal_parameter
84-
; NOCROSS: DW_AT_name {{.*}} "t"
85-
; NOCROSS: DW_AT_type [DW_FORM_ref4] {{.*}}{0x[[BAR_T1:.*]]}
86-
; NOCROSS: NULL
87-
; NOCROSS: 0x[[BAR_T1]]: DW_TAG_structure_type
88-
; NOCROSS: DW_AT_name {{.*}} "t1"
78+
; NOCROSS-NOT: DW_TAG_compile_unit
79+
; CROSS: Compile Unit
80+
; CROSS: DW_TAG_compile_unit
81+
; CROSS: DW_AT_name {{.*}} "bar.cpp"
8982
; ALL: DW_TAG_subprogram
9083
; ALL: DW_AT_name {{.*}} "bar"
9184
; DWO: DW_TAG_formal_parameter
9285
; DWO: DW_AT_name {{.*}} "t"
9386
; CROSS: DW_AT_type [DW_FORM_ref_addr] (0x00000000[[T1]]
94-
; NOCROSS: DW_AT_type [DW_FORM_ref4] {{.*}}{0x[[BAR_T1]]}
87+
; NOCROSS: DW_AT_type [DW_FORM_ref4] {{.*}}{0x[[T1]]}
9588
; ALL: DW_TAG_inlined_subroutine
96-
; INFO: DW_AT_abstract_origin [DW_FORM_ref_addr] (0x00000000[[F1]]
97-
; NOCROSS: DW_AT_abstract_origin [DW_FORM_ref4] {{.*}}{0x[[BAR_F1]]}
89+
; CROSS: DW_AT_abstract_origin [DW_FORM_ref_addr] (0x00000000[[F1]]
90+
; NOCROSS: DW_AT_abstract_origin [DW_FORM_ref4] {{.*}}{0x[[F1]]}
9891
; DWO: DW_TAG_formal_parameter
9992
; CROSS: DW_AT_abstract_origin [DW_FORM_ref_addr] (0x00000000[[F1T]]
100-
; NOCROSS: DW_AT_abstract_origin [DW_FORM_ref4] {{.*}}{0x[[BAR_F1T]]
93+
; NOCROSS: DW_AT_abstract_origin [DW_FORM_ref4] {{.*}}{0x[[F1T]]
10194

10295
%struct.t1 = type { i32 }
10396

llvm/test/DebugInfo/X86/string-offsets-table-order.ll

+7-12
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,10 @@
1111
; in different order.
1212

1313
; CHECK: .debug_info contents:
14-
; CHECK: DW_TAG_skeleton_unit
15-
; CHECK: DW_AT_comp_dir [DW_FORM_strx1] (indexed (00000000) string = "X3")
16-
; CHECK: DW_TAG_skeleton_unit
17-
; CHECK: DW_AT_comp_dir [DW_FORM_strx1] (indexed (00000001) string = "X2")
18-
; CHECK: DW_TAG_skeleton_unit
19-
; CHECK: DW_AT_comp_dir [DW_FORM_strx1] (indexed (00000002) string = "X1")
20-
; CHECK: .debug_info.dwo contents:
14+
; CHECK: DW_TAG_compile_unit
15+
; CHECK: DW_AT_name [DW_FORM_strx1] (indexed (00000000) string = "X1")
16+
; CHECK: DW_AT_name [DW_FORM_strx1] (indexed (00000002) string = "X2")
17+
; CHECK: DW_AT_name [DW_FORM_strx1] (indexed (00000003) string = "X3")
2118

2219
; CHECK: .debug_str contents:
2320
; CHECK: 0x[[X3:[0-9a-f]*]]: "X3"
@@ -26,11 +23,9 @@
2623

2724
; CHECK: .debug_str_offsets contents:
2825
; CHECK: Format = DWARF32, Version = 5
29-
; CHECK-NEXT: [[X3]] "X3"
30-
; CHECK-NEXT: [[X2]] "X2"
31-
; CHECK-NEXT: [[X1]] "X1"
32-
; CHECK-NEXT: "foo.dwo"
33-
; CHECK-EMPTY:
26+
; CHECK: [[X3]] "X3"
27+
; CHECK: [[X1]] "X1"
28+
; CHECK: [[X2]] "X2"
3429

3530

3631

0 commit comments

Comments
 (0)