Skip to content

Commit b2cf024

Browse files
committed
[DebugInfo][CSInfo] Don't use clobbered registers as locations
When finding call-site argument locations, don't consider registers to be location candidates if they will be clobbered between the copy to/from them and call site. Doing so would present overwritten register values as entry values in called functions. This patch adds a collection of register units defined as we walk back from the call site, and prevents the acceptance of a call-site parameter location if it will be clobbered on that path. Fixes #57444 Differential Revision: https://reviews.llvm.org/D141279
1 parent eb8fcc1 commit b2cf024

File tree

2 files changed

+193
-7
lines changed

2 files changed

+193
-7
lines changed

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,9 @@ struct FwdRegParamInfo {
597597

598598
/// Register worklist for finding call site values.
599599
using FwdRegWorklist = MapVector<unsigned, SmallVector<FwdRegParamInfo, 2>>;
600+
/// Container for the set of registers known to be clobbered on the path to a
601+
/// call site.
602+
using ClobberedRegSet = SmallSet<Register, 16>;
600603

601604
/// Append the expression \p Addition to \p Original and return the result.
602605
static const DIExpression *combineDIExpressions(const DIExpression *Original,
@@ -668,7 +671,8 @@ static void addToFwdRegWorklist(FwdRegWorklist &Worklist, unsigned Reg,
668671
/// Interpret values loaded into registers by \p CurMI.
669672
static void interpretValues(const MachineInstr *CurMI,
670673
FwdRegWorklist &ForwardedRegWorklist,
671-
ParamSet &Params) {
674+
ParamSet &Params,
675+
ClobberedRegSet &ClobberedRegUnits) {
672676

673677
const MachineFunction *MF = CurMI->getMF();
674678
const DIExpression *EmptyExpr =
@@ -700,6 +704,7 @@ static void interpretValues(const MachineInstr *CurMI,
700704

701705
// If the MI is an instruction defining one or more parameters' forwarding
702706
// registers, add those defines.
707+
ClobberedRegSet NewClobberedRegUnits;
703708
auto getForwardingRegsDefinedByMI = [&](const MachineInstr &MI,
704709
SmallSetVector<unsigned, 4> &Defs) {
705710
if (MI.isDebugInstr())
@@ -710,6 +715,8 @@ static void interpretValues(const MachineInstr *CurMI,
710715
for (auto &FwdReg : ForwardedRegWorklist)
711716
if (TRI.regsOverlap(FwdReg.first, MO.getReg()))
712717
Defs.insert(FwdReg.first);
718+
for (MCRegUnitIterator Units(MO.getReg(), &TRI); Units.isValid(); ++Units)
719+
NewClobberedRegUnits.insert(*Units);
713720
}
714721
}
715722
};
@@ -718,8 +725,22 @@ static void interpretValues(const MachineInstr *CurMI,
718725
SmallSetVector<unsigned, 4> FwdRegDefs;
719726

720727
getForwardingRegsDefinedByMI(*CurMI, FwdRegDefs);
721-
if (FwdRegDefs.empty())
728+
if (FwdRegDefs.empty()) {
729+
// Any definitions by this instruction will clobber earlier reg movements.
730+
ClobberedRegUnits.insert(NewClobberedRegUnits.begin(),
731+
NewClobberedRegUnits.end());
722732
return;
733+
}
734+
735+
// It's possible that we find a copy from a non-volatile register to the param
736+
// register, which is clobbered in the meantime. Test for clobbered reg unit
737+
// overlaps before completing.
738+
auto IsRegClobberedInMeantime = [&](Register Reg) -> bool {
739+
for (auto &RegUnit : ClobberedRegUnits)
740+
if (TRI.hasRegUnit(Reg, RegUnit))
741+
return true;
742+
return false;
743+
};
723744

724745
for (auto ParamFwdReg : FwdRegDefs) {
725746
if (auto ParamValue = TII.describeLoadedValue(*CurMI, ParamFwdReg)) {
@@ -732,7 +753,8 @@ static void interpretValues(const MachineInstr *CurMI,
732753
Register SP = TLI.getStackPointerRegisterToSaveRestore();
733754
Register FP = TRI.getFrameRegister(*MF);
734755
bool IsSPorFP = (RegLoc == SP) || (RegLoc == FP);
735-
if (TRI.isCalleeSavedPhysReg(RegLoc, *MF) || IsSPorFP) {
756+
if (!IsRegClobberedInMeantime(RegLoc) &&
757+
(TRI.isCalleeSavedPhysReg(RegLoc, *MF) || IsSPorFP)) {
736758
MachineLocation MLoc(RegLoc, /*Indirect=*/IsSPorFP);
737759
finishCallSiteParams(MLoc, ParamValue->second,
738760
ForwardedRegWorklist[ParamFwdReg], Params);
@@ -754,6 +776,10 @@ static void interpretValues(const MachineInstr *CurMI,
754776
for (auto ParamFwdReg : FwdRegDefs)
755777
ForwardedRegWorklist.erase(ParamFwdReg);
756778

779+
// Any definitions by this instruction will clobber earlier reg movements.
780+
ClobberedRegUnits.insert(NewClobberedRegUnits.begin(),
781+
NewClobberedRegUnits.end());
782+
757783
// Now that we are done handling this instruction, add items from the
758784
// temporary worklist to the real one.
759785
for (auto &New : TmpWorklistItems)
@@ -763,7 +789,8 @@ static void interpretValues(const MachineInstr *CurMI,
763789

764790
static bool interpretNextInstr(const MachineInstr *CurMI,
765791
FwdRegWorklist &ForwardedRegWorklist,
766-
ParamSet &Params) {
792+
ParamSet &Params,
793+
ClobberedRegSet &ClobberedRegUnits) {
767794
// Skip bundle headers.
768795
if (CurMI->isBundle())
769796
return true;
@@ -781,7 +808,7 @@ static bool interpretNextInstr(const MachineInstr *CurMI,
781808
if (CurMI->getNumOperands() == 0)
782809
return true;
783810

784-
interpretValues(CurMI, ForwardedRegWorklist, Params);
811+
interpretValues(CurMI, ForwardedRegWorklist, Params, ClobberedRegUnits);
785812

786813
return true;
787814
}
@@ -833,6 +860,7 @@ static void collectCallSiteParameters(const MachineInstr *CallMI,
833860
bool ShouldTryEmitEntryVals = MBB->getIterator() == MF->begin();
834861

835862
// Search for a loading value in forwarding registers inside call delay slot.
863+
ClobberedRegSet ClobberedRegUnits;
836864
if (CallMI->hasDelaySlot()) {
837865
auto Suc = std::next(CallMI->getIterator());
838866
// Only one-instruction delay slot is supported.
@@ -841,14 +869,14 @@ static void collectCallSiteParameters(const MachineInstr *CallMI,
841869
assert(std::next(Suc) == BundleEnd &&
842870
"More than one instruction in call delay slot");
843871
// Try to interpret value loaded by instruction.
844-
if (!interpretNextInstr(&*Suc, ForwardedRegWorklist, Params))
872+
if (!interpretNextInstr(&*Suc, ForwardedRegWorklist, Params, ClobberedRegUnits))
845873
return;
846874
}
847875

848876
// Search for a loading value in forwarding registers.
849877
for (; I != MBB->rend(); ++I) {
850878
// Try to interpret values loaded by instruction.
851-
if (!interpretNextInstr(&*I, ForwardedRegWorklist, Params))
879+
if (!interpretNextInstr(&*I, ForwardedRegWorklist, Params, ClobberedRegUnits))
852880
return;
853881
}
854882

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# RUN: llc %s -o - -filetype=obj -start-after=livedebugvalues \
2+
# RUN: -emit-call-site-info | \
3+
# RUN: llvm-dwarfdump - | FileCheck %s --implicit-check-not=call_value
4+
#
5+
## Test that call-site information is produced for this input, but use the
6+
## implicit not above to check that no call value entries are produced for
7+
## the tail call. They're clobbered by the restoration of r14 and rbx.
8+
##
9+
## Addresses https://github.com/llvm/llvm-project/issues/57444
10+
#
11+
# CHECK: DW_TAG_call_site
12+
# CHECK-NEXT: DW_AT_call_origin
13+
# CHECK-NEXT: DW_AT_call_return_pc
14+
#
15+
# CHECK: DW_TAG_call_site
16+
# CHECK-NEXT: DW_AT_call_origin
17+
# CHECK-NEXT: DW_AT_call_tail_call (true)
18+
# CHECK-NEXT: DW_AT_call_pc
19+
20+
--- |
21+
; ModuleID = 'out.ll'
22+
source_filename = "test.c"
23+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
24+
target triple = "x86_64-unknown-linux-gnu"
25+
26+
@str = private unnamed_addr constant [5 x i8] c"ohai\00", align 1
27+
28+
; Function Attrs: nounwind uwtable
29+
define dso_local i32 @foo(i64 noundef %a, i64 noundef %b) local_unnamed_addr #0 !dbg !16 {
30+
entry:
31+
call void @llvm.dbg.value(metadata i64 %a, metadata !22, metadata !DIExpression()), !dbg !24
32+
call void @llvm.dbg.value(metadata i64 %b, metadata !23, metadata !DIExpression()), !dbg !24
33+
%call = tail call i32 (...) @baz() #5, !dbg !25
34+
%conv = trunc i64 %a to i32, !dbg !26
35+
%conv1 = trunc i64 %b to i32, !dbg !27
36+
%call2 = tail call i32 @bar(i32 noundef %conv, i32 noundef %conv1) #5, !dbg !28
37+
ret i32 %call2, !dbg !29
38+
}
39+
40+
declare !dbg !30 i32 @baz(...) local_unnamed_addr #1
41+
42+
declare !dbg !34 i32 @bar(i32 noundef, i32 noundef) local_unnamed_addr #1
43+
44+
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
45+
declare void @llvm.dbg.value(metadata, metadata, metadata) #3
46+
47+
!llvm.dbg.cu = !{!0}
48+
!llvm.module.flags = !{!9, !10, !11, !12, !13, !14}
49+
!llvm.ident = !{!15}
50+
51+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !2, splitDebugInlining: false, nameTableKind: None)
52+
!1 = !DIFile(filename: "test.c", directory: ".", checksumkind: CSK_MD5, checksum: "98e51a9cf3bc6e1c3cc216ca4c82353a")
53+
!2 = !{!3}
54+
!3 = !DIGlobalVariableExpression(var: !4, expr: !DIExpression())
55+
!4 = distinct !DIGlobalVariable(scope: null, file: !1, line: 11, type: !5, isLocal: true, isDefinition: true)
56+
!5 = !DICompositeType(tag: DW_TAG_array_type, baseType: !6, size: 48, elements: !7)
57+
!6 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
58+
!7 = !{!8}
59+
!8 = !DISubrange(count: 6)
60+
!9 = !{i32 7, !"Dwarf Version", i32 5}
61+
!10 = !{i32 2, !"Debug Info Version", i32 3}
62+
!11 = !{i32 1, !"wchar_size", i32 4}
63+
!12 = !{i32 8, !"PIC Level", i32 2}
64+
!13 = !{i32 7, !"PIE Level", i32 2}
65+
!14 = !{i32 7, !"uwtable", i32 2}
66+
!15 = !{!"clang"}
67+
!16 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 5, type: !17, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !21)
68+
!17 = !DISubroutineType(types: !18)
69+
!18 = !{!19, !20, !20}
70+
!19 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
71+
!20 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed)
72+
!21 = !{!22, !23}
73+
!22 = !DILocalVariable(name: "a", arg: 1, scope: !16, file: !1, line: 5, type: !20)
74+
!23 = !DILocalVariable(name: "b", arg: 2, scope: !16, file: !1, line: 5, type: !20)
75+
!24 = !DILocation(line: 0, scope: !16)
76+
!25 = !DILocation(line: 6, column: 3, scope: !16)
77+
!26 = !DILocation(line: 7, column: 14, scope: !16)
78+
!27 = !DILocation(line: 7, column: 17, scope: !16)
79+
!28 = !DILocation(line: 7, column: 10, scope: !16)
80+
!29 = !DILocation(line: 7, column: 3, scope: !16)
81+
!30 = !DISubprogram(name: "baz", scope: !1, file: !1, line: 4, type: !31, spFlags: DISPFlagOptimized, retainedNodes: !33)
82+
!31 = !DISubroutineType(types: !32)
83+
!32 = !{!19}
84+
!33 = !{}
85+
!34 = !DISubprogram(name: "bar", scope: !1, file: !1, line: 3, type: !35, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !33)
86+
!35 = !DISubroutineType(types: !36)
87+
!36 = !{!19, !19, !19}
88+
!37 = distinct !DISubprogram(name: "qux", scope: !1, file: !1, line: 10, type: !31, scopeLine: 10, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !33)
89+
!38 = !DILocation(line: 11, column: 3, scope: !37)
90+
!39 = !DILocation(line: 12, column: 3, scope: !37)
91+
92+
...
93+
---
94+
name: foo
95+
alignment: 16
96+
tracksRegLiveness: true
97+
tracksDebugUserValues: true
98+
liveins:
99+
- { reg: '$rdi' }
100+
- { reg: '$rsi' }
101+
frameInfo:
102+
stackSize: 24
103+
offsetAdjustment: -24
104+
maxAlignment: 1
105+
adjustsStack: true
106+
hasCalls: true
107+
maxCallFrameSize: 0
108+
cvBytesOfCalleeSavedRegisters: 16
109+
hasTailCall: true
110+
fixedStack:
111+
- { id: 0, type: spill-slot, offset: -24, size: 8, alignment: 8, callee-saved-register: '$rbx' }
112+
- { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 16, callee-saved-register: '$r14' }
113+
callSites:
114+
- { bb: 0, offset: 15 }
115+
- { bb: 0, offset: 28, fwdArgRegs:
116+
- { arg: 0, reg: '$edi' }
117+
- { arg: 1, reg: '$esi' } }
118+
debugValueSubstitutions:
119+
- { srcinst: 2, srcop: 0, dstinst: 1, dstop: 0, subreg: 6 }
120+
- { srcinst: 4, srcop: 0, dstinst: 3, dstop: 0, subreg: 6 }
121+
machineFunctionInfo: {}
122+
body: |
123+
bb.0.entry:
124+
liveins: $rdi, $rsi, $r14, $rbx
125+
126+
DBG_VALUE $rdi, $noreg, !22, !DIExpression(), debug-location !24
127+
DBG_VALUE $rsi, $noreg, !23, !DIExpression(), debug-location !24
128+
frame-setup PUSH64r killed $r14, implicit-def $rsp, implicit $rsp
129+
frame-setup CFI_INSTRUCTION def_cfa_offset 16
130+
frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp
131+
frame-setup CFI_INSTRUCTION def_cfa_offset 24
132+
frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp
133+
frame-setup CFI_INSTRUCTION def_cfa_offset 32
134+
CFI_INSTRUCTION offset $rbx, -24
135+
CFI_INSTRUCTION offset $r14, -16
136+
DBG_PHI $rsi, 3
137+
DBG_PHI $rdi, 1
138+
$rbx = MOV64rr $rsi
139+
$r14 = MOV64rr $rdi
140+
dead $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, implicit-def $al, debug-location !25
141+
CALL64pcrel32 target-flags(x86-plt) @baz, csr_64, implicit $rsp, implicit $ssp, implicit killed $al, implicit-def $rsp, implicit-def $ssp, implicit-def dead $eax, debug-location !25
142+
DBG_VALUE $rbx, $noreg, !23, !DIExpression(), debug-location !24
143+
DBG_VALUE $r14, $noreg, !22, !DIExpression(), debug-location !24
144+
$edi = MOV32rr $r14d, implicit killed $r14, debug-location !28
145+
$esi = MOV32rr $ebx, implicit killed $rbx, debug-location !28
146+
$rsp = frame-destroy ADD64ri8 $rsp, 8, implicit-def dead $eflags, debug-location !28
147+
frame-destroy CFI_INSTRUCTION def_cfa_offset 24, debug-location !28
148+
;; This tail-call popping of rbx clobbers the call site information
149+
$rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28
150+
DBG_VALUE $rsi, $noreg, !23, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !24
151+
frame-destroy CFI_INSTRUCTION def_cfa_offset 16, debug-location !28
152+
;; This tail-call popping of r14 clobbers the call site information
153+
$r14 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28
154+
DBG_VALUE $rdi, $noreg, !22, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !24
155+
frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !28
156+
TAILJMPd64 target-flags(x86-plt) @bar, csr_64, implicit $rsp, implicit $ssp, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, debug-location !28
157+
158+
...

0 commit comments

Comments
 (0)