[llvm] e08f205 - Reland (again): [DWARF] Allow cross-CU references of subprogram definitions
Vedant Kumar via llvm-commits
llvm-commits at lists.llvm.org
Mon Jan 27 10:52:42 PST 2020
Author: Vedant Kumar
Date: 2020-01-27T10:52:34-08:00
New Revision: e08f205f5c2cfdbec64ccd97f0ac271d17a1ba93
URL: https://github.com/llvm/llvm-project/commit/e08f205f5c2cfdbec64ccd97f0ac271d17a1ba93
DIFF: https://github.com/llvm/llvm-project/commit/e08f205f5c2cfdbec64ccd97f0ac271d17a1ba93.diff
LOG: Reland (again): [DWARF] Allow cross-CU references of subprogram definitions
This is a revert-of-revert (i.e. this reverts commit 802bec89, which
itself reverted fa4701e1 and 79daafc9) with a fix folded in. The problem
was that call site tags weren't emitted properly when LTO was enabled
along with split-dwarf. This required a minor fix. I've added a reduced
test case in test/DebugInfo/X86/fission-call-site.ll.
Original commit message:
This allows a call site tag in CU A to reference a callee DIE in CU B
without resorting to creating an incomplete duplicate DIE for the callee
inside of CU A.
We already allow cross-CU references of subprogram declarations, so it
doesn't seem like definitions ought to be special.
This improves entry value evaluation and tail call frame synthesis in
the LTO setting. During LTO, it's common for cross-module inlining to
produce a call in some CU A where the callee resides in a different CU,
and there is no declaration subprogram for the callee anywhere. In this
case llvm would (unnecessarily, I think) emit an empty DW_TAG_subprogram
in order to fill in the call site tag. That empty 'definition' defeats
entry value evaluation etc., because the debugger can't figure out what
it means.
As a follow-up, maybe we could add a DWARF verifier check that a
DW_TAG_subprogram at least has a DW_AT_name attribute.
Update #1:
Reland with a fix to create a declaration DIE when the declaration is
missing from the CU's retainedTypes list. The declaration is left out
of the retainedTypes list in two cases:
1) Re-compiling pre-r266445 bitcode (in which declarations weren't added
to the retainedTypes list), and
2) Doing LTO function importing (which doesn't update the retainedTypes
list).
It's possible to handle (1) and (2) by modifying the retainedTypes list
(in AutoUpgrade, or in the LTO importing logic resp.), but I don't see
an advantage to doing it this way, as it would cause more DWARF to be
emitted compared to creating the declaration DIEs lazily.
Update #2:
Fold in a fix for call site tag emission in the split-dwarf + LTO case.
Tested with a stage2 ThinLTO+RelWithDebInfo build of clang, and with a
ReleaseLTO-g build of the test suite.
rdar://46577651, rdar://57855316, rdar://57840415, rdar://58888440
Differential Revision: https://reviews.llvm.org/D70350
Added:
llvm/test/DebugInfo/AArch64/unretained-declaration-subprogram.ll
llvm/test/DebugInfo/X86/fission-call-site.ll
llvm/test/DebugInfo/X86/lto-cross-cu-call-origin-ref.ll
Modified:
llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-orr-moves.mir
llvm/test/DebugInfo/MIR/X86/debug-call-site-param.mir
Removed:
################################################################################
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 9b8d3121fe42..0d501eaa00cd 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -977,7 +977,7 @@ DwarfCompileUnit::getDwarf5OrGNULocationAtom(dwarf::LocationAtom Loc) const {
}
DIE &DwarfCompileUnit::constructCallSiteEntryDIE(DIE &ScopeDIE,
- const DISubprogram *CalleeSP,
+ DIE *CalleeDIE,
bool IsTail,
const MCSymbol *PCAddr,
unsigned CallReg) {
@@ -990,8 +990,7 @@ DIE &DwarfCompileUnit::constructCallSiteEntryDIE(DIE &ScopeDIE,
addAddress(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_target),
MachineLocation(CallReg));
} else {
- DIE *CalleeDIE = getOrCreateSubprogramDIE(CalleeSP);
- assert(CalleeDIE && "Could not create DIE for call site entry origin");
+ assert(CalleeDIE && "No DIE for call site entry origin");
addDIEEntry(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_origin),
*CalleeDIE);
}
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
index d659746ca4b7..52e1c71a8e65 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
@@ -240,14 +240,15 @@ class DwarfCompileUnit final : public DwarfUnit {
dwarf::LocationAtom getDwarf5OrGNULocationAtom(dwarf::LocationAtom Loc) const;
/// Construct a call site entry DIE describing a call within \p Scope to a
- /// callee described by \p CalleeSP.
+ /// callee described by \p CalleeDIE.
+ /// \p CalleeDIE is a declaration or definition subprogram DIE for the callee.
+ /// For indirect calls \p CalleeDIE is set to nullptr.
/// \p IsTail specifies whether the call is a tail call.
/// \p PCAddr points to the PC value after the call instruction.
/// \p CallReg is a register location for an indirect call. For direct calls
/// the \p CallReg is set to 0.
- DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram *CalleeSP,
- bool IsTail, const MCSymbol *PCAddr,
- unsigned CallReg);
+ DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, DIE *CalleeDIE, bool IsTail,
+ const MCSymbol *PCAddr, unsigned CallReg);
/// Construct call site parameter DIEs for the \p CallSiteDIE. The \p Params
/// were collected by the \ref collectCallSiteParameters.
/// Note: The order of parameters does not matter, since debuggers recognize
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 03511d4347ad..527250f72086 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -540,6 +540,14 @@ void DwarfDebug::constructAbstractSubprogramScopeDIE(DwarfCompileUnit &SrcCU,
}
}
+DIE &DwarfDebug::constructSubprogramDefinitionDIE(const DISubprogram *SP) {
+ DICompileUnit *Unit = SP->getUnit();
+ assert(SP->isDefinition() && "Subprogram not a definition");
+ assert(Unit && "Subprogram definition without parent unit");
+ auto &CU = getOrCreateDwarfCompileUnit(Unit);
+ return *CU.getOrCreateSubprogramDIE(SP);
+}
+
/// Try to interpret values loaded into registers that forward parameters
/// for \p CallMI. Store parameters with interpreted value into \p Params.
static void collectCallSiteParameters(const MachineInstr *CallMI,
@@ -771,7 +779,7 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
continue;
unsigned CallReg = 0;
- const DISubprogram *CalleeSP = nullptr;
+ DIE *CalleeDIE = nullptr;
const Function *CalleeDecl = nullptr;
if (CalleeOp.isReg()) {
CallReg = CalleeOp.getReg();
@@ -781,7 +789,19 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
CalleeDecl = dyn_cast<Function>(CalleeOp.getGlobal());
if (!CalleeDecl || !CalleeDecl->getSubprogram())
continue;
- CalleeSP = CalleeDecl->getSubprogram();
+ const DISubprogram *CalleeSP = CalleeDecl->getSubprogram();
+
+ if (CalleeSP->isDefinition()) {
+ // Ensure that a subprogram DIE for the callee is available in the
+ // appropriate CU.
+ CalleeDIE = &constructSubprogramDefinitionDIE(CalleeSP);
+ } else {
+ // Create the declaration DIE if it is missing. This is required to
+ // support compilation of old bitcode with an incomplete list of
+ // retained metadata.
+ CalleeDIE = CU.getOrCreateSubprogramDIE(CalleeSP);
+ }
+ assert(CalleeDIE && "Must have a DIE for the callee");
}
// TODO: Omit call site entries for runtime calls (objc_msgSend, etc).
@@ -812,7 +832,7 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
->getName(CallReg)))
<< (IsTail ? " [IsTail]" : "") << "\n");
- DIE &CallSiteDIE = CU.constructCallSiteEntryDIE(ScopeDIE, CalleeSP,
+ DIE &CallSiteDIE = CU.constructCallSiteEntryDIE(ScopeDIE, CalleeDIE,
IsTail, PCAddr, CallReg);
// GDB and LLDB support call site parameter debug info.
@@ -929,11 +949,6 @@ DwarfDebug::getOrCreateDwarfCompileUnit(const DICompileUnit *DIUnit) {
NewCU.setSection(Asm->getObjFileLowering().getDwarfInfoSection());
}
- // Create DIEs for function declarations used for call site debug info.
- for (auto Scope : DIUnit->getRetainedTypes())
- if (auto *SP = dyn_cast_or_null<DISubprogram>(Scope))
- NewCU.getOrCreateSubprogramDIE(SP);
-
CUMap.insert({DIUnit, &NewCU});
CUDieMap.insert({&NewCU.getUnitDie(), &NewCU});
return NewCU;
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
index dbb1e8311a48..a44960589d89 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
@@ -442,6 +442,9 @@ class DwarfDebug : public DebugHandlerBase {
/// Construct a DIE for this abstract scope.
void constructAbstractSubprogramScopeDIE(DwarfCompileUnit &SrcCU, LexicalScope *Scope);
+ /// Construct a DIE for the subprogram definition \p SP and return it.
+ DIE &constructSubprogramDefinitionDIE(const DISubprogram *SP);
+
/// Construct DIEs for call site entries describing the calls in \p MF.
void constructCallSiteEntryDIEs(const DISubprogram &SP, DwarfCompileUnit &CU,
DIE &ScopeDIE, const MachineFunction &MF);
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index b54ec0b52c82..76ac2e049fc1 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -188,8 +188,9 @@ int64_t DwarfUnit::getDefaultLowerBound() const {
/// Check whether the DIE for this MDNode can be shared across CUs.
bool DwarfUnit::isShareableAcrossCUs(const DINode *D) const {
- // When the MDNode can be part of the type system, the DIE can be shared
- // across CUs.
+ // When the MDNode can be part of the type system (this includes subprogram
+ // declarations *and* subprogram definitions, even local definitions), the
+ // DIE must be shared across CUs.
// Combining type units and cross-CU DIE sharing is lower value (since
// cross-CU DIE sharing is used in LTO and removes type redundancy at that
// level already) but may be implementable for some value in projects
@@ -197,9 +198,7 @@ bool DwarfUnit::isShareableAcrossCUs(const DINode *D) const {
// together.
if (isDwoUnit() && !DD->shareAcrossDWOCUs())
return false;
- return (isa<DIType>(D) ||
- (isa<DISubprogram>(D) && !cast<DISubprogram>(D)->isDefinition())) &&
- !DD->generateTypeUnits();
+ return (isa<DIType>(D) || isa<DISubprogram>(D)) && !DD->generateTypeUnits();
}
DIE *DwarfUnit::getDIE(const DINode *D) const {
diff --git a/llvm/test/DebugInfo/AArch64/unretained-declaration-subprogram.ll b/llvm/test/DebugInfo/AArch64/unretained-declaration-subprogram.ll
new file mode 100644
index 000000000000..17ead0e81242
--- /dev/null
+++ b/llvm/test/DebugInfo/AArch64/unretained-declaration-subprogram.ll
@@ -0,0 +1,44 @@
+; RUN: llc -mtriple=arm64-apple-ios -filetype=obj < %s -o %t.o
+; RUN: llvm-dwarfdump %t.o | FileCheck %s -implicit-check-not=DW_TAG_subprogram
+
+; The declaration subprogram for "function" is not in the CU's list of
+; retained types. Test that a DWARF call site entry can still be constructed.
+
+; CHECK: DW_TAG_subprogram
+; CHECK: DW_AT_name {{.*}}__hidden#3_
+; CHECK: DW_TAG_call_site
+; CHECK: DW_AT_call_origin (0x{{0+}}[[FUNCTION_DIE:.*]])
+
+; CHECK: 0x{{0+}}[[FUNCTION_DIE]]: DW_TAG_subprogram
+; CHECK: DW_AT_name {{.*}}function
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-ios9.0.0"
+
+define i32 @main() local_unnamed_addr !dbg !8 {
+ %1 = tail call [2 x i64] @function([2 x i64] zeroinitializer), !dbg !11
+ %2 = extractvalue [2 x i64] %1, 0, !dbg !11
+ %3 = trunc i64 %2 to i32, !dbg !11
+ ret i32 %3, !dbg !12
+}
+
+declare !dbg !13 [2 x i64] @function([2 x i64]) local_unnamed_addr
+
+!llvm.module.flags = !{!0, !1, !2, !3, !4}
+!llvm.dbg.cu = !{!5}
+!llvm.ident = !{!7}
+
+!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 13, i32 4]}
+!1 = !{i32 7, !"Dwarf Version", i32 4}
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = !{i32 1, !"wchar_size", i32 4}
+!4 = !{i32 7, !"PIC Level", i32 2}
+!5 = distinct !DICompileUnit(language: DW_LANG_C99, file: !6, producer: "__hidden#0_", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, nameTableKind: None)
+!6 = !DIFile(filename: "__hidden#1_", directory: "__hidden#2_")
+!7 = !{!"Apple clang version 11.0.0 (llvm-project fa407d93fd5e618d76378c1ce4e4f517e0563278) (+internal-os)"}
+!8 = distinct !DISubprogram(name: "__hidden#3_", scope: !6, file: !6, line: 9, type: !9, scopeLine: 9, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !5)
+!9 = !DISubroutineType(types: !10)
+!10 = !{}
+!11 = !DILocation(line: 12, column: 10, scope: !8)
+!12 = !DILocation(line: 13, column: 3, scope: !8)
+!13 = !DISubprogram(name: "function", scope: !6, file: !6, line: 7, type: !9, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
diff --git a/llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-orr-moves.mir b/llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-orr-moves.mir
index 685684de6e88..9d4feaccf9e7 100644
--- a/llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-orr-moves.mir
+++ b/llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-orr-moves.mir
@@ -159,7 +159,7 @@ body: |
...
# CHECK: DW_TAG_GNU_call_site
-# CHECK-NEXT: DW_AT_abstract_origin (0x0000002a "call_int")
+# CHECK-NEXT: DW_AT_abstract_origin ({{.*}} "call_int")
#
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg0 W0)
@@ -205,7 +205,7 @@ body: |
...
# CHECK: DW_TAG_GNU_call_site
-# CHECK-NEXT: DW_AT_abstract_origin (0x0000003e "call_long")
+# CHECK-NEXT: DW_AT_abstract_origin ({{.*}} "call_long")
#
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg0 W0)
@@ -265,7 +265,7 @@ body: |
...
# CHECK: DW_TAG_GNU_call_site
-# CHECK-NEXT: DW_AT_abstract_origin (0x00000052 "call_int_int")
+# CHECK-NEXT: DW_AT_abstract_origin ({{.*}} "call_int_int")
#
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg0 W0)
diff --git a/llvm/test/DebugInfo/MIR/X86/debug-call-site-param.mir b/llvm/test/DebugInfo/MIR/X86/debug-call-site-param.mir
index e79be66cd4e3..caee15c2a9e6 100644
--- a/llvm/test/DebugInfo/MIR/X86/debug-call-site-param.mir
+++ b/llvm/test/DebugInfo/MIR/X86/debug-call-site-param.mir
@@ -39,17 +39,11 @@
# CHECK-GNU-NEXT: DW_AT_location (DW_OP_reg8 R8)
# CHECK-GNU-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+3)
-# CHECK-DWARF5: [[getValue_SP:.*]]: DW_TAG_subprogram
-# CHECK-DWARF5-NEXT: DW_AT_name ("getVal")
-
-# CHECK-DWARF5: [[foo_SP:.*]]: DW_TAG_subprogram
-# CHECK-DWARF5-NEXT: DW_AT_name ("foo")
-
# CHECK-DWARF5: DW_TAG_call_site
-# CHECK-DWARF5: DW_AT_call_origin ([[getValue_SP]])
-#
+# CHECK-DWARF5: DW_AT_call_origin ([[getValue_SP:.*]])
+
# CHECK-DWARF5: DW_TAG_call_site
-# CHECK-DWARF5: DW_AT_call_origin ([[foo_SP]])
+# CHECK-DWARF5: DW_AT_call_origin ([[foo_SP:.*]])
# CHECK-DWARF5: DW_AT_call_return_pc {{.*}}
# CHECK-DWARF5-EMPTY:
# CHECK-DWARF5: DW_TAG_call_site_parameter
@@ -71,6 +65,12 @@
# CHECK-DWARF5-NEXT: DW_AT_location (DW_OP_reg8 R8)
# CHECK-DWARF5-NEXT: DW_AT_call_value (DW_OP_breg14 R14+3)
+# CHECK-DWARF5: [[getValue_SP]]: DW_TAG_subprogram
+# CHECK-DWARF5-NEXT: DW_AT_name ("getVal")
+
+# CHECK-DWARF5: [[foo_SP]]: DW_TAG_subprogram
+# CHECK-DWARF5-NEXT: DW_AT_name ("foo")
+
--- |
; ModuleID = 'test.c'
source_filename = "test.c"
diff --git a/llvm/test/DebugInfo/X86/fission-call-site.ll b/llvm/test/DebugInfo/X86/fission-call-site.ll
new file mode 100644
index 000000000000..65e2ef62dd71
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/fission-call-site.ll
@@ -0,0 +1,68 @@
+; Check that call site entries are emitted correctly when using split-dwarf
+; together with some form of LTO.
+
+; Original C source:
+;
+; // cu1.c
+; extern void callee(void);
+; void caller(void) {
+; callee();
+; }
+;
+; // cu2.c
+; __attribute__((optnone)) void callee(void) {}
+;
+; Steps to reproduce:
+;
+; (The -O1 here is needed to trigger call site entry emission, as these tags are
+; generally not emitted at -O0.)
+;
+; clang -target x86_64-unknown-linux-gnu -gsplit-dwarf=split -O1 ~/tmp/cu1.c -S -emit-llvm -o ~/tmp/cu1.ll
+; clang -target x86_64-unknown-linux-gnu -gsplit-dwarf=split -O1 ~/tmp/cu2.c -S -emit-llvm -o ~/tmp/cu2.ll
+; llvm-link -o ~/tmp/cu-merged.bc ~/tmp/cu1.ll ~/tmp/cu2.ll
+; llc -split-dwarf-file=foo.dwo -O0 -mtriple=x86_64-unknown-linux-gnu -filetype=obj -o - ~/tmp/cu-merged.bc
+
+; RUN: llc -split-dwarf-file=foo.dwo -O0 %s -mtriple=x86_64-unknown-linux-gnu -filetype=obj -o %t
+; RUN: llvm-dwarfdump %t | FileCheck %s
+
+; CHECK: DW_TAG_GNU_call_site
+; CHECK-NEXT: DW_AT_abstract_origin {{.*}} "callee"
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define dso_local void @caller() local_unnamed_addr !dbg !14 {
+entry:
+ call void @callee(), !dbg !15
+ ret void, !dbg !16
+}
+
+define dso_local void @callee() local_unnamed_addr noinline optnone !dbg !17 {
+entry:
+ ret void, !dbg !19
+}
+
+!llvm.dbg.cu = !{!0, !8}
+!llvm.ident = !{!10, !10}
+!llvm.module.flags = !{!11, !12, !13}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0 (git at github.com:llvm/llvm-project.git 170f4b972e7bcf1f2af98bdd7145954efd16e038)", isOptimized: true, runtimeVersion: 0, splitDebugFilename: "cu1.dwo", emissionKind: FullDebug, enums: !2, retainedTypes: !3, splitDebugInlining: false, nameTableKind: GNU)
+!1 = !DIFile(filename: "/Users/vsk/tmp/cu1.c", directory: "/Users/vsk/src/builds/llvm-project-master-RA")
+!2 = !{}
+!3 = !{!4}
+!4 = !DISubprogram(name: "callee", scope: !5, file: !5, line: 1, type: !6, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
+!5 = !DIFile(filename: "tmp/cu1.c", directory: "/Users/vsk")
+!6 = !DISubroutineType(types: !7)
+!7 = !{null}
+!8 = distinct !DICompileUnit(language: DW_LANG_C99, file: !9, producer: "clang version 11.0.0 (git at github.com:llvm/llvm-project.git 170f4b972e7bcf1f2af98bdd7145954efd16e038)", isOptimized: true, runtimeVersion: 0, splitDebugFilename: "cu2.dwo", emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: GNU)
+!9 = !DIFile(filename: "/Users/vsk/tmp/cu2.c", directory: "/Users/vsk/src/builds/llvm-project-master-RA")
+!10 = !{!"clang version 11.0.0 (git at github.com:llvm/llvm-project.git 170f4b972e7bcf1f2af98bdd7145954efd16e038)"}
+!11 = !{i32 7, !"Dwarf Version", i32 4}
+!12 = !{i32 2, !"Debug Info Version", i32 3}
+!13 = !{i32 1, !"wchar_size", i32 4}
+!14 = distinct !DISubprogram(name: "caller", scope: !5, file: !5, line: 2, type: !6, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!15 = !DILocation(line: 3, column: 3, scope: !14)
+!16 = !DILocation(line: 4, column: 1, scope: !14)
+!17 = distinct !DISubprogram(name: "callee", scope: !18, file: !18, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !2)
+!18 = !DIFile(filename: "tmp/cu2.c", directory: "/Users/vsk")
+!19 = !DILocation(line: 1, column: 45, scope: !17)
diff --git a/llvm/test/DebugInfo/X86/lto-cross-cu-call-origin-ref.ll b/llvm/test/DebugInfo/X86/lto-cross-cu-call-origin-ref.ll
new file mode 100644
index 000000000000..00715841ab5c
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/lto-cross-cu-call-origin-ref.ll
@@ -0,0 +1,211 @@
+; RUN: llc -mtriple=x86_64-apple-darwin -filetype=obj < %s -o %t.o
+; RUN: llvm-dwarfdump %t.o | FileCheck %s -implicit-check-not=DW_TAG_subprogram
+; RUN: llvm-dwarfdump --verify %t.o
+
+; This test checks that cross-CU references within call site tags to subprogram
+; definitions are well-formed. There are 5 cases checked in this test. Each set
+; of checks is numbered and has a brief summary.
+
+; Instructions to regenerate the IR:
+; clang -O1 -g -emit-llvm -o a.bc -c a.c
+; clang -O1 -g -emit-llvm -o b.bc -c b.c
+; llvm-link -o linked.bc a.bc b.bc
+; opt -O1 linked.bc -o merged.bc
+
+; Source:
+; // a.c
+; __attribute__((optnone)) void noinline_func_in_a() {}
+;
+; __attribute__((optnone)) static void foo() {}
+; __attribute__((always_inline)) void always_inline_helper_in_a_that_calls_foo() {
+; foo();
+; }
+;
+; extern void func_from_b();
+; void call_func_in_b_from_a() {
+; func_from_b();
+; }
+;
+; // b.c
+; extern void noinline_func_in_a();
+; void call_noinline_func_in_a_from_b() {
+; noinline_func_in_a();
+; }
+;
+; __attribute__((optnone)) void foo() {}
+; extern void always_inline_helper_in_a_that_calls_foo();
+; void call_both_foos_from_b() {
+; foo();
+; always_inline_helper_in_a_that_calls_foo();
+; }
+;
+; __attribute__((optnone)) void func_from_b() {}
+; void call_func_in_b_from_b() {
+; func_from_b();
+; }
+
+; === CU for a.c ===
+
+; CHECK: DW_TAG_compile_unit
+; CHECK: DW_AT_name ("a.c")
+
+; CHECK: 0x{{0+}}[[NOINLINE_FUNC_IN_A:.*]]: DW_TAG_subprogram
+; CHECK: DW_AT_name ("noinline_func_in_a")
+
+; 1) Check that "always_inline_helper_in_a_that_calls_foo" calls the "foo" in
+; a.c, and *not* the "foo" in b.c.
+; CHECK: 0x{{0+}}[[ALWAYS_INLINE_HELPER_IN_A:.*]]: DW_TAG_subprogram
+; CHECK: DW_AT_abstract_origin ({{.*}} "always_inline_helper_in_a_that_calls_foo")
+; CHECK: DW_TAG_call_site
+; CHECK-NEXT: DW_AT_call_origin (0x{{0+}}[[FOO_IN_A:.*]])
+
+; CHECK: 0x{{0+}}[[FOO_IN_A]]: DW_TAG_subprogram
+; CHECK: DW_AT_name ("foo")
+
+; 2) Check that "call_func_in_b_from_a" has a cross-CU ref into b.c.
+; CHECK: DW_TAG_subprogram
+; CHECK: DW_AT_name ("call_func_in_b_from_a")
+; CHECK: DW_TAG_call_site
+; CHECK-NEXT: DW_AT_call_origin (0x{{0+}}[[FUNC_FROM_B:.*]])
+
+; CHECK: DW_TAG_subprogram
+; CHECK: DW_AT_name ("always_inline_helper_in_a_that_calls_foo")
+; CHECK: DW_AT_inline (DW_INL_inlined)
+
+; === CU for b.c ===
+
+; CHECK: DW_TAG_compile_unit
+; CHECK: DW_AT_name ("b.c")
+
+; 3) Validate the cross-CU ref from "call_func_in_b_from_a" in a.c.
+; CHECK: 0x{{0+}}[[FUNC_FROM_B]]: DW_TAG_subprogram
+; CHECK: DW_AT_name ("func_from_b")
+
+; 4) Validate the cross-CU ref from "call_noinline_func_in_a_from_b" in b.c.
+; CHECK: DW_TAG_subprogram
+; CHECK: DW_AT_name ("call_noinline_func_in_a_from_b")
+; CHECK: DW_TAG_call_site
+; CHECK-NEXT: DW_AT_call_origin (0x{{0+}}[[NOINLINE_FUNC_IN_A]])
+
+; CHECK: 0x{{0+}}[[FOO_IN_B:.*]]: DW_TAG_subprogram
+; CHECK: DW_AT_name ("foo")
+
+; 5) Validate that we correctly emit a cross-CU ref when the call is inlined
+; from another CU.
+; CHECK: DW_TAG_subprogram
+; CHECK: DW_AT_name ("call_both_foos_from_b")
+; CHECK: DW_TAG_call_site
+; CHECK-NEXT: DW_AT_call_origin (0x{{0+}}[[FOO_IN_B]])
+; CHECK: DW_TAG_call_site
+; CHECK-NEXT: DW_AT_call_origin (0x{{0+}}[[FOO_IN_A]])
+
+; CHECK: DW_TAG_subprogram
+; CHECK: DW_AT_name ("call_func_in_b_from_b")
+; CHECK: DW_TAG_call_site
+; CHECK-NEXT: DW_AT_call_origin (0x{{0+}}[[FUNC_FROM_B]])
+
+target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-apple-macosx10.14.0"
+
+define void @noinline_func_in_a() local_unnamed_addr #0 !dbg !17 {
+entry:
+ ret void, !dbg !20
+}
+
+define void @always_inline_helper_in_a_that_calls_foo() local_unnamed_addr #1 !dbg !21 {
+entry:
+ tail call fastcc void @foo.2(), !dbg !22
+ ret void, !dbg !23
+}
+
+define internal fastcc void @foo.2() unnamed_addr #0 !dbg !24 {
+entry:
+ ret void, !dbg !25
+}
+
+define void @call_func_in_b_from_a() local_unnamed_addr !dbg !26 {
+entry:
+ tail call void @func_from_b() #3, !dbg !27
+ ret void, !dbg !28
+}
+
+define void @call_noinline_func_in_a_from_b() local_unnamed_addr !dbg !29 {
+entry:
+ tail call void @noinline_func_in_a() #3, !dbg !30
+ ret void, !dbg !31
+}
+
+define void @foo() local_unnamed_addr #0 !dbg !32 {
+entry:
+ ret void, !dbg !33
+}
+
+define void @call_both_foos_from_b() local_unnamed_addr !dbg !34 {
+entry:
+ tail call void @foo(), !dbg !35
+ tail call fastcc void @foo.2() #3, !dbg !36
+ ret void, !dbg !38
+}
+
+define void @func_from_b() local_unnamed_addr #0 !dbg !39 {
+entry:
+ ret void, !dbg !40
+}
+
+define void @call_func_in_b_from_b() local_unnamed_addr !dbg !41 {
+entry:
+ tail call void @func_from_b(), !dbg !42
+ ret void, !dbg !43
+}
+
+attributes #0 = { noinline }
+attributes #1 = { alwaysinline }
+
+!llvm.dbg.cu = !{!0, !7}
+!llvm.ident = !{!12, !12}
+!llvm.module.flags = !{!13, !14, !15, !16}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (git at github.com:llvm/llvm-project.git 310e85309f870ee7347ef979d7d8da9bf28e92ea)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None)
+!1 = !DIFile(filename: "a.c", directory: "/Users/vsk/tmp/lto-entry-vals")
+!2 = !{}
+!3 = !{!4}
+!4 = !DISubprogram(name: "func_from_b", scope: !1, file: !1, line: 8, type: !5, spFlags: DISPFlagOptimized, retainedNodes: !2)
+!5 = !DISubroutineType(types: !6)
+!6 = !{null, null}
+!7 = distinct !DICompileUnit(language: DW_LANG_C99, file: !8, producer: "clang version 10.0.0 (git at github.com:llvm/llvm-project.git 310e85309f870ee7347ef979d7d8da9bf28e92ea)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !9, nameTableKind: None)
+!8 = !DIFile(filename: "b.c", directory: "/Users/vsk/tmp/lto-entry-vals")
+!9 = !{!10, !11}
+!10 = !DISubprogram(name: "noinline_func_in_a", scope: !8, file: !8, line: 1, type: !5, spFlags: DISPFlagOptimized, retainedNodes: !2)
+!11 = !DISubprogram(name: "always_inline_helper_in_a_that_calls_foo", scope: !8, file: !8, line: 7, type: !5, spFlags: DISPFlagOptimized, retainedNodes: !2)
+!12 = !{!"clang version 10.0.0 (git at github.com:llvm/llvm-project.git 310e85309f870ee7347ef979d7d8da9bf28e92ea)"}
+!13 = !{i32 7, !"Dwarf Version", i32 4}
+!14 = !{i32 2, !"Debug Info Version", i32 3}
+!15 = !{i32 1, !"wchar_size", i32 4}
+!16 = !{i32 7, !"PIC Level", i32 2}
+!17 = distinct !DISubprogram(name: "noinline_func_in_a", scope: !1, file: !1, line: 1, type: !18, scopeLine: 1, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!18 = !DISubroutineType(types: !19)
+!19 = !{null}
+!20 = !DILocation(line: 1, column: 53, scope: !17)
+!21 = distinct !DISubprogram(name: "always_inline_helper_in_a_that_calls_foo", scope: !1, file: !1, line: 4, type: !18, scopeLine: 4, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!22 = !DILocation(line: 5, column: 3, scope: !21)
+!23 = !DILocation(line: 6, column: 1, scope: !21)
+!24 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !18, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!25 = !DILocation(line: 3, column: 45, scope: !24)
+!26 = distinct !DISubprogram(name: "call_func_in_b_from_a", scope: !1, file: !1, line: 9, type: !18, scopeLine: 9, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!27 = !DILocation(line: 10, column: 3, scope: !26)
+!28 = !DILocation(line: 11, column: 1, scope: !26)
+!29 = distinct !DISubprogram(name: "call_noinline_func_in_a_from_b", scope: !8, file: !8, line: 2, type: !18, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !7, retainedNodes: !2)
+!30 = !DILocation(line: 3, column: 3, scope: !29)
+!31 = !DILocation(line: 4, column: 1, scope: !29)
+!32 = distinct !DISubprogram(name: "foo", scope: !8, file: !8, line: 6, type: !18, scopeLine: 6, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !7, retainedNodes: !2)
+!33 = !DILocation(line: 6, column: 38, scope: !32)
+!34 = distinct !DISubprogram(name: "call_both_foos_from_b", scope: !8, file: !8, line: 8, type: !18, scopeLine: 8, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !7, retainedNodes: !2)
+!35 = !DILocation(line: 9, column: 3, scope: !34)
+!36 = !DILocation(line: 5, column: 3, scope: !21, inlinedAt: !37)
+!37 = distinct !DILocation(line: 10, column: 3, scope: !34)
+!38 = !DILocation(line: 11, column: 1, scope: !34)
+!39 = distinct !DISubprogram(name: "func_from_b", scope: !8, file: !8, line: 13, type: !18, scopeLine: 13, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !7, retainedNodes: !2)
+!40 = !DILocation(line: 13, column: 46, scope: !39)
+!41 = distinct !DISubprogram(name: "call_func_in_b_from_b", scope: !8, file: !8, line: 14, type: !18, scopeLine: 14, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !7, retainedNodes: !2)
+!42 = !DILocation(line: 15, column: 3, scope: !41)
+!43 = !DILocation(line: 16, column: 1, scope: !41)
More information about the llvm-commits
mailing list