1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 03:02:36 +01:00

[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
This commit is contained in:
Djordje Todorovic 2020-06-03 10:37:41 +02:00
parent 14dd772f1b
commit 2533162bf0
4 changed files with 372 additions and 4 deletions

View File

@ -752,6 +752,10 @@ static bool interpretNextInstr(const MachineInstr *CurMI,
if (ForwardedRegWorklist.empty())
return false;
// Avoid NOP description.
if (CurMI->getNumOperands() == 0)
return true;
interpretValues(CurMI, ForwardedRegWorklist, Params);
return true;
@ -798,6 +802,18 @@ static void collectCallSiteParameters(const MachineInstr *CallMI,
// as the entry value within basic blocks other than the first one.
bool ShouldTryEmitEntryVals = MBB->getIterator() == MF->begin();
// Search for a loading value in forwarding registers inside call delay slot.
if (CallMI->hasDelaySlot()) {
auto Suc = std::next(CallMI->getIterator());
// Only one-instruction delay slot is supported.
auto BundleEnd = llvm::getBundleEnd(CallMI->getIterator());
assert(std::next(Suc) == BundleEnd &&
"More than one instruction in call delay slot");
// Try to interpret value loaded by instruction.
if (!interpretNextInstr(&*Suc, ForwardedRegWorklist, Params))
return;
}
// Search for a loading value in forwarding registers.
for (; I != MBB->rend(); ++I) {
// Try to interpret values loaded by instruction.
@ -834,6 +850,23 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
assert(TII && "TargetInstrInfo not found: cannot label tail calls");
// Delay slot support check.
auto delaySlotSupported = [&](const MachineInstr &MI) {
if (!MI.isBundledWithSucc())
return false;
auto Suc = std::next(MI.getIterator());
auto CallInstrBundle = getBundleStart(MI.getIterator());
auto DelaySlotBundle = getBundleStart(Suc);
// Ensure that label after call is following delay slot instruction.
// Ex. CALL_INSTRUCTION {
// DELAY_SLOT_INSTRUCTION }
// LABEL_AFTER_CALL
assert(getLabelAfterInsn(&*CallInstrBundle) ==
getLabelAfterInsn(&*DelaySlotBundle) &&
"Call and its successor instruction don't have same label after.");
return true;
};
// Emit call site entries for each call or tail call in the function.
for (const MachineBasicBlock &MBB : MF) {
for (const MachineInstr &MI : MBB.instrs()) {
@ -853,8 +886,8 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
if (MI.getFlag(MachineInstr::FrameSetup))
continue;
// TODO: Add support for targets with delay slots (see: beginInstruction).
if (MI.hasDelaySlot())
// Check if delay slot support is enabled.
if (MI.hasDelaySlot() && !delaySlotSupported(*&MI))
return;
// If this is a direct call, find the callee's subprogram.
@ -1855,11 +1888,23 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
bool NoDebug =
!SP || SP->getUnit()->getEmissionKind() == DICompileUnit::NoDebug;
// Delay slot support check.
auto delaySlotSupported = [](const MachineInstr &MI) {
if (!MI.isBundledWithSucc())
return false;
auto Suc = std::next(MI.getIterator());
// Ensure that delay slot instruction is successor of the call instruction.
// Ex. CALL_INSTRUCTION {
// DELAY_SLOT_INSTRUCTION }
assert(Suc->isBundledWithPred() &&
"Call bundle instructions are out of order");
return true;
};
// When describing calls, we need a label for the call instruction.
// TODO: Add support for targets with delay slots.
if (!NoDebug && SP->areAllCallsDescribed() &&
MI->isCandidateForCallSiteEntry(MachineInstr::AnyInBundle) &&
!MI->hasDelaySlot()) {
(!MI->hasDelaySlot() || delaySlotSupported(*MI))) {
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
bool IsTail = TII->isTailCall(*MI);
// For tail calls, we need the address of the branch instruction for

View File

@ -0,0 +1,129 @@
## Test mips64:
# RUN: llc -emit-call-site-info -start-after=machineverifier -filetype=obj -mtriple=mips64-linux-gnu %s -o -| llvm-dwarfdump -| FileCheck %s
## Test mips64el:
# RUN: llc -emit-call-site-info -start-after=machineverifier -filetype=obj -mtriple=mips64el-linux-gnu %s -o -| llvm-dwarfdump -| FileCheck %s
## Built from source:
## extern int __attribute__((noinline)) sum(int a, int b);
## void __attribute__((noinline)) set(int *adr, int val) {
## val++;
## *adr = val + sum(val, val);
## }
## Using command:
## clang -g -O2 -target mips64-linux-gnu m.c -c -mllvm -stop-before=machineverifier
## Check that call site interpretation analysis can interpret calls with delay slot and
## parameters set outside and inside of the call delay slot.
## Test mips64:
# CHECK: DW_TAG_GNU_call_site
# CHECK-NEXT: DW_AT_abstract_origin {{.*}} "sum"
# CHECK-NEXT: DW_AT_low_pc
# CHECK-EMPTY:
## Parameter forwarding register A1_64 is set in call delay slot.
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg5 A1_64)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg17 S1_64+0)
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg4 A0_64)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg17 S1_64+0)
--- |
; ModuleID = 'm.ll'
source_filename = "m.c"
target datalayout = "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128"
target triple = "mips64-unknown-linux-gnu"
; Function Attrs: noinline nounwind
define void @set(i32* nocapture %adr, i32 signext %val) local_unnamed_addr !dbg !13 {
entry:
call void @llvm.dbg.value(metadata i32* %adr, metadata !18, metadata !DIExpression()), !dbg !20
call void @llvm.dbg.value(metadata i32 %val, metadata !19, metadata !DIExpression()), !dbg !20
%inc = add nsw i32 %val, 1, !dbg !20
call void @llvm.dbg.value(metadata i32 %inc, metadata !19, metadata !DIExpression()), !dbg !20
%call = tail call signext i32 @sum(i32 signext %inc, i32 signext %inc), !dbg !20
%add = add nsw i32 %call, %inc, !dbg !20
store i32 %add, i32* %adr, align 4, !dbg !20
ret void
}
declare !dbg !4 signext i32 @sum(i32 signext, i32 signext) local_unnamed_addr
; Function Attrs: nounwind readnone speculatable willreturn
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!8, !9, !10, !11}
!llvm.ident = !{!12}
!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)
!1 = !DIFile(filename: "m.c", directory: "/dir")
!2 = !{}
!3 = !{!4}
!4 = !DISubprogram(name: "sum", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!5 = !DISubroutineType(types: !6)
!6 = !{!7, !7, !7}
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!8 = !{i32 7, !"Dwarf Version", i32 4}
!9 = !{i32 2, !"Debug Info Version", i32 3}
!10 = !{i32 1, !"wchar_size", i32 4}
!11 = !{i32 7, !"PIC Level", i32 1}
!12 = !{!"clang version 11.0.0"}
!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)
!14 = !DISubroutineType(types: !15)
!15 = !{null, !16, !7}
!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
!17 = !{!18, !19}
!18 = !DILocalVariable(name: "adr", arg: 1, scope: !13, file: !1, line: 2, type: !16)
!19 = !DILocalVariable(name: "val", arg: 2, scope: !13, file: !1, line: 2, type: !7)
!20 = !DILocation(line: 0, scope: !13)
...
---
name: set
alignment: 8
stack:
- { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '$ra_64', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 1, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '$s1_64', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 2, name: '', type: spill-slot, offset: -24, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '$s0_64', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
callSites:
- { bb: 0, offset: 17, fwdArgRegs:
- { arg: 0, reg: '$a0_64' }
- { arg: 1, reg: '$a1_64' } }
body: |
bb.0.entry:
DBG_VALUE $a0_64, $noreg, !18, !DIExpression(), debug-location !20
DBG_VALUE $a1_64, $noreg, !19, !DIExpression(), debug-location !20
DBG_VALUE $a1_64, $noreg, !19, !DIExpression(), debug-location !20
$sp_64 = DADDiu $sp_64, -32
CFI_INSTRUCTION def_cfa_offset 32
SD killed $ra_64, $sp_64, 24 :: (store 8 into %stack.0)
SD killed $s1_64, $sp_64, 16 :: (store 8 into %stack.1)
SD killed $s0_64, $sp_64, 8 :: (store 8 into %stack.2)
CFI_INSTRUCTION offset $ra_64, -8
CFI_INSTRUCTION offset $s1_64, -16
CFI_INSTRUCTION offset $s0_64, -24
$s0_64 = OR64 $a0_64, $zero_64
DBG_VALUE $a1, $noreg, !19, !DIExpression(), debug-location !20
DBG_VALUE $s0_64, $noreg, !18, !DIExpression(), debug-location !20
renamable $s1 = ADDiu renamable $a1, 1, implicit killed $a1_64, implicit-def $s1_64, debug-location !20
DBG_VALUE $s1, $noreg, !19, !DIExpression(), debug-location !20
$a0_64 = OR64 $s1_64, $zero_64, debug-location !20
JAL @sum, csr_n64, implicit-def dead $ra, implicit $a0_64, implicit $a1_64, implicit-def $sp, implicit-def $v0, debug-location !20 {
$a1_64 = OR64 $s1_64, $zero_64, debug-location !20
}
renamable $at = nsw ADDu killed renamable $v0, renamable $s1, implicit killed $s1_64, debug-location !20
SW killed renamable $at, killed renamable $s0_64, 0
$s0_64 = LD $sp_64, 8
$s1_64 = LD $sp_64, 16
$ra_64 = LD $sp_64, 24
PseudoReturn64 undef $ra_64 {
$sp_64 = DADDiu $sp_64, 32
}
...

View File

@ -0,0 +1,127 @@
## Test mips32:
# RUN: llc -emit-call-site-info -start-after=machineverifier -filetype=obj -mtriple=mips-linux-gnu %s -o -| llvm-dwarfdump -| FileCheck %s
## Test mipsel:
# RUN: llc -emit-call-site-info -start-after=machineverifier -filetype=obj -mtriple=mipsel-linux-gnu %s -o -| llvm-dwarfdump -| FileCheck %s
## Built from source:
## extern int __attribute__((noinline)) sum(int a, int b);
## void __attribute__((noinline)) set(int *adr, int val) {
## val++;
## *adr = val + sum(val, val);
## }
## Using command:
## clang -g -O2 -target mips-linux-gnu m.c -c -mllvm -stop-before=machineverifier
## Check that call site interpretation analysis can interpret calls with delay slot and
## parameters set outside and inside of the call delay slot.
## Test mips32:
# CHECK: DW_TAG_GNU_call_site
# CHECK-NEXT: DW_AT_abstract_origin {{.*}} "sum"
# CHECK-NEXT: DW_AT_low_pc
# CHECK-EMPTY:
## Parameter forwarding register A1_64 is set in call delay slot.
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg5 A1_64)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg17 S1_64+0)
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg4 A0_64)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg17 S1_64+0)
--- |
; ModuleID = 'm.ll'
source_filename = "m.c"
target datalayout = "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64"
target triple = "mips-unknown-linux-gnu"
; Function Attrs: noinline nounwind
define dso_local void @set(i32* nocapture %adr, i32 signext %val) local_unnamed_addr !dbg !12 {
entry:
call void @llvm.dbg.value(metadata i32* %adr, metadata !17, metadata !DIExpression()), !dbg !19
call void @llvm.dbg.value(metadata i32 %val, metadata !18, metadata !DIExpression()), !dbg !19
%inc = add nsw i32 %val, 1, !dbg !19
call void @llvm.dbg.value(metadata i32 %inc, metadata !18, metadata !DIExpression()), !dbg !19
%call = tail call i32 @sum(i32 signext %inc, i32 signext %inc), !dbg !19
%add = add nsw i32 %call, %inc, !dbg !19
store i32 %add, i32* %adr, align 4, !dbg !19
ret void
}
declare !dbg !4 dso_local i32 @sum(i32 signext, i32 signext) local_unnamed_addr
; Function Attrs: nounwind readnone speculatable willreturn
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!8, !9, !10}
!llvm.ident = !{!11}
!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)
!1 = !DIFile(filename: "m.c", directory: "/dir")
!2 = !{}
!3 = !{!4}
!4 = !DISubprogram(name: "sum", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!5 = !DISubroutineType(types: !6)
!6 = !{!7, !7, !7}
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!8 = !{i32 7, !"Dwarf Version", i32 4}
!9 = !{i32 2, !"Debug Info Version", i32 3}
!10 = !{i32 1, !"wchar_size", i32 4}
!11 = !{!"clang version 11.0.0"}
!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)
!13 = !DISubroutineType(types: !14)
!14 = !{null, !15, !7}
!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 32)
!16 = !{!17, !18}
!17 = !DILocalVariable(name: "adr", arg: 1, scope: !12, file: !1, line: 2, type: !15)
!18 = !DILocalVariable(name: "val", arg: 2, scope: !12, file: !1, line: 2, type: !7)
!19 = !DILocation(line: 0, scope: !12)
...
---
name: set
alignment: 4
stack:
- { id: 0, name: '', type: spill-slot, offset: -4, size: 4, alignment: 4,
stack-id: default, callee-saved-register: '$ra', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 1, name: '', type: spill-slot, offset: -8, size: 4, alignment: 4,
stack-id: default, callee-saved-register: '$s1', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 2, name: '', type: spill-slot, offset: -12, size: 4, alignment: 4,
stack-id: default, callee-saved-register: '$s0', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
callSites:
- { bb: 0, offset: 16, fwdArgRegs:
- { arg: 0, reg: '$a0' }
- { arg: 1, reg: '$a1' } }
body: |
bb.0.entry:
DBG_VALUE $a0, $noreg, !17, !DIExpression(), debug-location !19
DBG_VALUE $a1, $noreg, !18, !DIExpression(), debug-location !19
DBG_VALUE $a1, $noreg, !18, !DIExpression(), debug-location !19
$sp = ADDiu $sp, -32
CFI_INSTRUCTION def_cfa_offset 32
SW killed $ra, $sp, 28 :: (store 4 into %stack.0)
SW killed $s1, $sp, 24 :: (store 4 into %stack.1)
SW killed $s0, $sp, 20 :: (store 4 into %stack.2)
CFI_INSTRUCTION offset $ra_64, -4
CFI_INSTRUCTION offset $s1_64, -8
CFI_INSTRUCTION offset $s0_64, -12
$s0 = OR $a0, $zero
DBG_VALUE $s0, $noreg, !17, !DIExpression(), debug-location !19
renamable $s1 = nsw ADDiu killed renamable $a1, 1, debug-location !19
DBG_VALUE $s1, $noreg, !18, !DIExpression(), debug-location !19
$a0 = OR $s1, $zero, debug-location !19
JAL @sum, csr_o32, implicit-def dead $ra, implicit $a0, implicit $a1, implicit-def $sp, implicit-def $v0, debug-location !19 {
$a1 = OR $s1, $zero, debug-location !19
}
renamable $at = nsw ADDu killed renamable $v0, killed renamable $s1, debug-location !19
SW killed renamable $at, killed renamable $s0, 0, debug-location !19 :: (store 4 into %ir.adr)
$s0 = LW $sp, 20
DBG_VALUE $a0, $noreg, !17, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !19
$s1 = LW $sp, 24
$ra = LW $sp, 28
PseudoReturn undef $ra {
$sp = ADDiu $sp, 32
}
...

View File

@ -0,0 +1,67 @@
;; Test mips32:
; RUN: llc -emit-call-site-info %s -mtriple=mips -filetype=obj -o -| llvm-dwarfdump -| FileCheck %s
;; Test mipsel:
; RUN: llc -emit-call-site-info %s -mtriple=mipsel -filetype=obj -o -| llvm-dwarfdump -| FileCheck %s
;; Test mips64:
; RUN: llc -emit-call-site-info %s -mtriple=mips64 -filetype=obj -o -| llvm-dwarfdump -| FileCheck %s
;; Test mips64el:
; RUN: llc -emit-call-site-info %s -mtriple=mips64el -filetype=obj -o -| llvm-dwarfdump -| FileCheck %s
;; Source:
;; __attribute__((noinline))
;; extern void f1(int a);
;; __attribute__((noinline))
;; int main(){
;; int x = 10;
;; f1(x);
;; return ++x;
;; }
;; Command: clang -g -O2 -target mips-linux-gnu -S -emit-llvm m.c -c
;; Confirm that DW_AT_low_pc (call return address) points to instruction after call delay slot.
;; Test mips, mipsel, mips64, mips64el:
; CHECK: DW_TAG_GNU_call_site
; CHECK-NEXT: DW_AT_abstract_origin {{.*}} "f1"
; CHECK-NEXT: DW_AT_low_pc (0x0000000000000010)
; ModuleID = 'm.c'
source_filename = "m.c"
target datalayout = "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64"
target triple = "mips-unknown-linux-gnu"
; Function Attrs: noinline nounwind
define dso_local i32 @main() local_unnamed_addr !dbg !12 {
entry:
call void @llvm.dbg.value(metadata i32 10, metadata !16, metadata !DIExpression()), !dbg !17
tail call void @f1(i32 signext 10), !dbg !17
call void @llvm.dbg.value(metadata i32 11, metadata !16, metadata !DIExpression()), !dbg !17
ret i32 11, !dbg !17
}
declare !dbg !4 dso_local void @f1(i32 signext) local_unnamed_addr
; Function Attrs: nounwind readnone speculatable willreturn
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!8, !9, !10}
!llvm.ident = !{!11}
!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)
!1 = !DIFile(filename: "m.c", directory: "/dir")
!2 = !{}
!3 = !{!4}
!4 = !DISubprogram(name: "f1", scope: !1, file: !1, line: 2, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!5 = !DISubroutineType(types: !6)
!6 = !{null, !7}
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!8 = !{i32 7, !"Dwarf Version", i32 4}
!9 = !{i32 2, !"Debug Info Version", i32 3}
!10 = !{i32 1, !"wchar_size", i32 4}
!11 = !{!"clang version 11.0.0"}
!12 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 5, type: !13, scopeLine: 5, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15)
!13 = !DISubroutineType(types: !14)
!14 = !{!7}
!15 = !{!16}
!16 = !DILocalVariable(name: "x", scope: !12, file: !1, line: 6, type: !7)
!17 = !DILocation(line: 0, scope: !12)