Skip to content

Commit dd1bc59

Browse files
author
Djordje Todorovic
committed
[CSInfo][MIPS][DwarfDebug] Add support for delay slots
This adds call site info support for call instructions with delay slot. Search for instructions inside call delay slot, which load value into parameter forwarding registers. Return address of the call points to instruction after call delay slot, which is not the one, immediately after the call instruction. Patch by Nikola Tesic Differential revision: https://reviews.llvm.org/D78107
1 parent d7d5dd3 commit dd1bc59

File tree

4 files changed

+372
-4
lines changed

4 files changed

+372
-4
lines changed

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,10 @@ static bool interpretNextInstr(const MachineInstr *CurMI,
752752
if (ForwardedRegWorklist.empty())
753753
return false;
754754

755+
// Avoid NOP description.
756+
if (CurMI->getNumOperands() == 0)
757+
return true;
758+
755759
interpretValues(CurMI, ForwardedRegWorklist, Params);
756760

757761
return true;
@@ -798,6 +802,18 @@ static void collectCallSiteParameters(const MachineInstr *CallMI,
798802
// as the entry value within basic blocks other than the first one.
799803
bool ShouldTryEmitEntryVals = MBB->getIterator() == MF->begin();
800804

805+
// Search for a loading value in forwarding registers inside call delay slot.
806+
if (CallMI->hasDelaySlot()) {
807+
auto Suc = std::next(CallMI->getIterator());
808+
// Only one-instruction delay slot is supported.
809+
auto BundleEnd = llvm::getBundleEnd(CallMI->getIterator());
810+
assert(std::next(Suc) == BundleEnd &&
811+
"More than one instruction in call delay slot");
812+
// Try to interpret value loaded by instruction.
813+
if (!interpretNextInstr(&*Suc, ForwardedRegWorklist, Params))
814+
return;
815+
}
816+
801817
// Search for a loading value in forwarding registers.
802818
for (; I != MBB->rend(); ++I) {
803819
// Try to interpret values loaded by instruction.
@@ -834,6 +850,23 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
834850
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
835851
assert(TII && "TargetInstrInfo not found: cannot label tail calls");
836852

853+
// Delay slot support check.
854+
auto delaySlotSupported = [&](const MachineInstr &MI) {
855+
if (!MI.isBundledWithSucc())
856+
return false;
857+
auto Suc = std::next(MI.getIterator());
858+
auto CallInstrBundle = getBundleStart(MI.getIterator());
859+
auto DelaySlotBundle = getBundleStart(Suc);
860+
// Ensure that label after call is following delay slot instruction.
861+
// Ex. CALL_INSTRUCTION {
862+
// DELAY_SLOT_INSTRUCTION }
863+
// LABEL_AFTER_CALL
864+
assert(getLabelAfterInsn(&*CallInstrBundle) ==
865+
getLabelAfterInsn(&*DelaySlotBundle) &&
866+
"Call and its successor instruction don't have same label after.");
867+
return true;
868+
};
869+
837870
// Emit call site entries for each call or tail call in the function.
838871
for (const MachineBasicBlock &MBB : MF) {
839872
for (const MachineInstr &MI : MBB.instrs()) {
@@ -853,8 +886,8 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
853886
if (MI.getFlag(MachineInstr::FrameSetup))
854887
continue;
855888

856-
// TODO: Add support for targets with delay slots (see: beginInstruction).
857-
if (MI.hasDelaySlot())
889+
// Check if delay slot support is enabled.
890+
if (MI.hasDelaySlot() && !delaySlotSupported(*&MI))
858891
return;
859892

860893
// If this is a direct call, find the callee's subprogram.
@@ -1855,11 +1888,23 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
18551888
bool NoDebug =
18561889
!SP || SP->getUnit()->getEmissionKind() == DICompileUnit::NoDebug;
18571890

1891+
// Delay slot support check.
1892+
auto delaySlotSupported = [](const MachineInstr &MI) {
1893+
if (!MI.isBundledWithSucc())
1894+
return false;
1895+
auto Suc = std::next(MI.getIterator());
1896+
// Ensure that delay slot instruction is successor of the call instruction.
1897+
// Ex. CALL_INSTRUCTION {
1898+
// DELAY_SLOT_INSTRUCTION }
1899+
assert(Suc->isBundledWithPred() &&
1900+
"Call bundle instructions are out of order");
1901+
return true;
1902+
};
1903+
18581904
// When describing calls, we need a label for the call instruction.
1859-
// TODO: Add support for targets with delay slots.
18601905
if (!NoDebug && SP->areAllCallsDescribed() &&
18611906
MI->isCandidateForCallSiteEntry(MachineInstr::AnyInBundle) &&
1862-
!MI->hasDelaySlot()) {
1907+
(!MI->hasDelaySlot() || delaySlotSupported(*MI))) {
18631908
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
18641909
bool IsTail = TII->isTailCall(*MI);
18651910
// For tail calls, we need the address of the branch instruction for
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
## Test mips64:
2+
# RUN: llc -emit-call-site-info -start-after=machineverifier -filetype=obj -mtriple=mips64-linux-gnu %s -o -| llvm-dwarfdump -| FileCheck %s
3+
## Test mips64el:
4+
# RUN: llc -emit-call-site-info -start-after=machineverifier -filetype=obj -mtriple=mips64el-linux-gnu %s -o -| llvm-dwarfdump -| FileCheck %s
5+
6+
## Built from source:
7+
## extern int __attribute__((noinline)) sum(int a, int b);
8+
## void __attribute__((noinline)) set(int *adr, int val) {
9+
## val++;
10+
## *adr = val + sum(val, val);
11+
## }
12+
## Using command:
13+
## clang -g -O2 -target mips64-linux-gnu m.c -c -mllvm -stop-before=machineverifier
14+
## Check that call site interpretation analysis can interpret calls with delay slot and
15+
## parameters set outside and inside of the call delay slot.
16+
17+
## Test mips64:
18+
# CHECK: DW_TAG_GNU_call_site
19+
# CHECK-NEXT: DW_AT_abstract_origin {{.*}} "sum"
20+
# CHECK-NEXT: DW_AT_low_pc
21+
# CHECK-EMPTY:
22+
## Parameter forwarding register A1_64 is set in call delay slot.
23+
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
24+
# CHECK-NEXT: DW_AT_location (DW_OP_reg5 A1_64)
25+
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg17 S1_64+0)
26+
# CHECK-EMPTY:
27+
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
28+
# CHECK-NEXT: DW_AT_location (DW_OP_reg4 A0_64)
29+
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg17 S1_64+0)
30+
31+
--- |
32+
; ModuleID = 'm.ll'
33+
source_filename = "m.c"
34+
target datalayout = "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128"
35+
target triple = "mips64-unknown-linux-gnu"
36+
; Function Attrs: noinline nounwind
37+
define void @set(i32* nocapture %adr, i32 signext %val) local_unnamed_addr !dbg !13 {
38+
entry:
39+
call void @llvm.dbg.value(metadata i32* %adr, metadata !18, metadata !DIExpression()), !dbg !20
40+
call void @llvm.dbg.value(metadata i32 %val, metadata !19, metadata !DIExpression()), !dbg !20
41+
%inc = add nsw i32 %val, 1, !dbg !20
42+
call void @llvm.dbg.value(metadata i32 %inc, metadata !19, metadata !DIExpression()), !dbg !20
43+
%call = tail call signext i32 @sum(i32 signext %inc, i32 signext %inc), !dbg !20
44+
%add = add nsw i32 %call, %inc, !dbg !20
45+
store i32 %add, i32* %adr, align 4, !dbg !20
46+
ret void
47+
}
48+
49+
declare !dbg !4 signext i32 @sum(i32 signext, i32 signext) local_unnamed_addr
50+
51+
; Function Attrs: nounwind readnone speculatable willreturn
52+
declare void @llvm.dbg.value(metadata, metadata, metadata)
53+
54+
!llvm.dbg.cu = !{!0}
55+
!llvm.module.flags = !{!8, !9, !10, !11}
56+
!llvm.ident = !{!12}
57+
58+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, splitDebugInlining: false, nameTableKind: None)
59+
!1 = !DIFile(filename: "m.c", directory: "/dir")
60+
!2 = !{}
61+
!3 = !{!4}
62+
!4 = !DISubprogram(name: "sum", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
63+
!5 = !DISubroutineType(types: !6)
64+
!6 = !{!7, !7, !7}
65+
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
66+
!8 = !{i32 7, !"Dwarf Version", i32 4}
67+
!9 = !{i32 2, !"Debug Info Version", i32 3}
68+
!10 = !{i32 1, !"wchar_size", i32 4}
69+
!11 = !{i32 7, !"PIC Level", i32 1}
70+
!12 = !{!"clang version 11.0.0"}
71+
!13 = distinct !DISubprogram(name: "set", scope: !1, file: !1, line: 2, type: !14, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !17)
72+
!14 = !DISubroutineType(types: !15)
73+
!15 = !{null, !16, !7}
74+
!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
75+
!17 = !{!18, !19}
76+
!18 = !DILocalVariable(name: "adr", arg: 1, scope: !13, file: !1, line: 2, type: !16)
77+
!19 = !DILocalVariable(name: "val", arg: 2, scope: !13, file: !1, line: 2, type: !7)
78+
!20 = !DILocation(line: 0, scope: !13)
79+
80+
...
81+
---
82+
name: set
83+
alignment: 8
84+
stack:
85+
- { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8,
86+
stack-id: default, callee-saved-register: '$ra_64', callee-saved-restored: true,
87+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
88+
- { id: 1, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8,
89+
stack-id: default, callee-saved-register: '$s1_64', callee-saved-restored: true,
90+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
91+
- { id: 2, name: '', type: spill-slot, offset: -24, size: 8, alignment: 8,
92+
stack-id: default, callee-saved-register: '$s0_64', callee-saved-restored: true,
93+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
94+
callSites:
95+
- { bb: 0, offset: 17, fwdArgRegs:
96+
- { arg: 0, reg: '$a0_64' }
97+
- { arg: 1, reg: '$a1_64' } }
98+
body: |
99+
bb.0.entry:
100+
DBG_VALUE $a0_64, $noreg, !18, !DIExpression(), debug-location !20
101+
DBG_VALUE $a1_64, $noreg, !19, !DIExpression(), debug-location !20
102+
DBG_VALUE $a1_64, $noreg, !19, !DIExpression(), debug-location !20
103+
$sp_64 = DADDiu $sp_64, -32
104+
CFI_INSTRUCTION def_cfa_offset 32
105+
SD killed $ra_64, $sp_64, 24 :: (store 8 into %stack.0)
106+
SD killed $s1_64, $sp_64, 16 :: (store 8 into %stack.1)
107+
SD killed $s0_64, $sp_64, 8 :: (store 8 into %stack.2)
108+
CFI_INSTRUCTION offset $ra_64, -8
109+
CFI_INSTRUCTION offset $s1_64, -16
110+
CFI_INSTRUCTION offset $s0_64, -24
111+
$s0_64 = OR64 $a0_64, $zero_64
112+
DBG_VALUE $a1, $noreg, !19, !DIExpression(), debug-location !20
113+
DBG_VALUE $s0_64, $noreg, !18, !DIExpression(), debug-location !20
114+
renamable $s1 = ADDiu renamable $a1, 1, implicit killed $a1_64, implicit-def $s1_64, debug-location !20
115+
DBG_VALUE $s1, $noreg, !19, !DIExpression(), debug-location !20
116+
$a0_64 = OR64 $s1_64, $zero_64, debug-location !20
117+
JAL @sum, csr_n64, implicit-def dead $ra, implicit $a0_64, implicit $a1_64, implicit-def $sp, implicit-def $v0, debug-location !20 {
118+
$a1_64 = OR64 $s1_64, $zero_64, debug-location !20
119+
}
120+
renamable $at = nsw ADDu killed renamable $v0, renamable $s1, implicit killed $s1_64, debug-location !20
121+
SW killed renamable $at, killed renamable $s0_64, 0
122+
$s0_64 = LD $sp_64, 8
123+
$s1_64 = LD $sp_64, 16
124+
$ra_64 = LD $sp_64, 24
125+
PseudoReturn64 undef $ra_64 {
126+
$sp_64 = DADDiu $sp_64, 32
127+
}
128+
129+
...
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
## Test mips32:
2+
# RUN: llc -emit-call-site-info -start-after=machineverifier -filetype=obj -mtriple=mips-linux-gnu %s -o -| llvm-dwarfdump -| FileCheck %s
3+
## Test mipsel:
4+
# RUN: llc -emit-call-site-info -start-after=machineverifier -filetype=obj -mtriple=mipsel-linux-gnu %s -o -| llvm-dwarfdump -| FileCheck %s
5+
6+
## Built from source:
7+
## extern int __attribute__((noinline)) sum(int a, int b);
8+
## void __attribute__((noinline)) set(int *adr, int val) {
9+
## val++;
10+
## *adr = val + sum(val, val);
11+
## }
12+
## Using command:
13+
## clang -g -O2 -target mips-linux-gnu m.c -c -mllvm -stop-before=machineverifier
14+
## Check that call site interpretation analysis can interpret calls with delay slot and
15+
## parameters set outside and inside of the call delay slot.
16+
17+
## Test mips32:
18+
# CHECK: DW_TAG_GNU_call_site
19+
# CHECK-NEXT: DW_AT_abstract_origin {{.*}} "sum"
20+
# CHECK-NEXT: DW_AT_low_pc
21+
# CHECK-EMPTY:
22+
## Parameter forwarding register A1_64 is set in call delay slot.
23+
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
24+
# CHECK-NEXT: DW_AT_location (DW_OP_reg5 A1_64)
25+
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg17 S1_64+0)
26+
# CHECK-EMPTY:
27+
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
28+
# CHECK-NEXT: DW_AT_location (DW_OP_reg4 A0_64)
29+
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg17 S1_64+0)
30+
31+
--- |
32+
; ModuleID = 'm.ll'
33+
source_filename = "m.c"
34+
target datalayout = "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64"
35+
target triple = "mips-unknown-linux-gnu"
36+
; Function Attrs: noinline nounwind
37+
define dso_local void @set(i32* nocapture %adr, i32 signext %val) local_unnamed_addr !dbg !12 {
38+
entry:
39+
call void @llvm.dbg.value(metadata i32* %adr, metadata !17, metadata !DIExpression()), !dbg !19
40+
call void @llvm.dbg.value(metadata i32 %val, metadata !18, metadata !DIExpression()), !dbg !19
41+
%inc = add nsw i32 %val, 1, !dbg !19
42+
call void @llvm.dbg.value(metadata i32 %inc, metadata !18, metadata !DIExpression()), !dbg !19
43+
%call = tail call i32 @sum(i32 signext %inc, i32 signext %inc), !dbg !19
44+
%add = add nsw i32 %call, %inc, !dbg !19
45+
store i32 %add, i32* %adr, align 4, !dbg !19
46+
ret void
47+
}
48+
declare !dbg !4 dso_local i32 @sum(i32 signext, i32 signext) local_unnamed_addr
49+
; Function Attrs: nounwind readnone speculatable willreturn
50+
declare void @llvm.dbg.value(metadata, metadata, metadata)
51+
52+
!llvm.dbg.cu = !{!0}
53+
!llvm.module.flags = !{!8, !9, !10}
54+
!llvm.ident = !{!11}
55+
56+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, splitDebugInlining: false, nameTableKind: None)
57+
!1 = !DIFile(filename: "m.c", directory: "/dir")
58+
!2 = !{}
59+
!3 = !{!4}
60+
!4 = !DISubprogram(name: "sum", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
61+
!5 = !DISubroutineType(types: !6)
62+
!6 = !{!7, !7, !7}
63+
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
64+
!8 = !{i32 7, !"Dwarf Version", i32 4}
65+
!9 = !{i32 2, !"Debug Info Version", i32 3}
66+
!10 = !{i32 1, !"wchar_size", i32 4}
67+
!11 = !{!"clang version 11.0.0"}
68+
!12 = distinct !DISubprogram(name: "set", scope: !1, file: !1, line: 2, type: !13, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !16)
69+
!13 = !DISubroutineType(types: !14)
70+
!14 = !{null, !15, !7}
71+
!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 32)
72+
!16 = !{!17, !18}
73+
!17 = !DILocalVariable(name: "adr", arg: 1, scope: !12, file: !1, line: 2, type: !15)
74+
!18 = !DILocalVariable(name: "val", arg: 2, scope: !12, file: !1, line: 2, type: !7)
75+
!19 = !DILocation(line: 0, scope: !12)
76+
77+
...
78+
---
79+
name: set
80+
alignment: 4
81+
stack:
82+
- { id: 0, name: '', type: spill-slot, offset: -4, size: 4, alignment: 4,
83+
stack-id: default, callee-saved-register: '$ra', callee-saved-restored: true,
84+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
85+
- { id: 1, name: '', type: spill-slot, offset: -8, size: 4, alignment: 4,
86+
stack-id: default, callee-saved-register: '$s1', callee-saved-restored: true,
87+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
88+
- { id: 2, name: '', type: spill-slot, offset: -12, size: 4, alignment: 4,
89+
stack-id: default, callee-saved-register: '$s0', callee-saved-restored: true,
90+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
91+
callSites:
92+
- { bb: 0, offset: 16, fwdArgRegs:
93+
- { arg: 0, reg: '$a0' }
94+
- { arg: 1, reg: '$a1' } }
95+
body: |
96+
bb.0.entry:
97+
DBG_VALUE $a0, $noreg, !17, !DIExpression(), debug-location !19
98+
DBG_VALUE $a1, $noreg, !18, !DIExpression(), debug-location !19
99+
DBG_VALUE $a1, $noreg, !18, !DIExpression(), debug-location !19
100+
$sp = ADDiu $sp, -32
101+
CFI_INSTRUCTION def_cfa_offset 32
102+
SW killed $ra, $sp, 28 :: (store 4 into %stack.0)
103+
SW killed $s1, $sp, 24 :: (store 4 into %stack.1)
104+
SW killed $s0, $sp, 20 :: (store 4 into %stack.2)
105+
CFI_INSTRUCTION offset $ra_64, -4
106+
CFI_INSTRUCTION offset $s1_64, -8
107+
CFI_INSTRUCTION offset $s0_64, -12
108+
$s0 = OR $a0, $zero
109+
DBG_VALUE $s0, $noreg, !17, !DIExpression(), debug-location !19
110+
renamable $s1 = nsw ADDiu killed renamable $a1, 1, debug-location !19
111+
DBG_VALUE $s1, $noreg, !18, !DIExpression(), debug-location !19
112+
$a0 = OR $s1, $zero, debug-location !19
113+
JAL @sum, csr_o32, implicit-def dead $ra, implicit $a0, implicit $a1, implicit-def $sp, implicit-def $v0, debug-location !19 {
114+
$a1 = OR $s1, $zero, debug-location !19
115+
}
116+
renamable $at = nsw ADDu killed renamable $v0, killed renamable $s1, debug-location !19
117+
SW killed renamable $at, killed renamable $s0, 0, debug-location !19 :: (store 4 into %ir.adr)
118+
$s0 = LW $sp, 20
119+
DBG_VALUE $a0, $noreg, !17, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !19
120+
$s1 = LW $sp, 24
121+
$ra = LW $sp, 28
122+
PseudoReturn undef $ra {
123+
$sp = ADDiu $sp, 32
124+
}
125+
126+
...
127+

0 commit comments

Comments
 (0)