diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -752,6 +752,10 @@ if (ForwardedRegWorklist.empty()) return false; + // Avoid NOP description. + if (CurMI->getNumOperands() == 0) + return true; + interpretValues(CurMI, ForwardedRegWorklist, Params); return true; @@ -798,6 +802,18 @@ // 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 @@ 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 @@ 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 @@ 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 diff --git a/llvm/test/DebugInfo/MIR/Mips/dbg-call-site-delay-slot-interpretation-64bit.mir b/llvm/test/DebugInfo/MIR/Mips/dbg-call-site-delay-slot-interpretation-64bit.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/Mips/dbg-call-site-delay-slot-interpretation-64bit.mir @@ -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 + } + +... diff --git a/llvm/test/DebugInfo/MIR/Mips/dbg-call-site-delay-slot-interpretation.mir b/llvm/test/DebugInfo/MIR/Mips/dbg-call-site-delay-slot-interpretation.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/Mips/dbg-call-site-delay-slot-interpretation.mir @@ -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 + } + +... + diff --git a/llvm/test/DebugInfo/Mips/dbg-call-site-low-pc.ll b/llvm/test/DebugInfo/Mips/dbg-call-site-low-pc.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Mips/dbg-call-site-low-pc.ll @@ -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)