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 @@ -597,6 +597,9 @@ /// Register worklist for finding call site values. using FwdRegWorklist = MapVector>; +/// Container for the set of registers known to be clobbered on the path to a +/// call site. +using ClobberedRegSet = SmallSet; /// Append the expression \p Addition to \p Original and return the result. static const DIExpression *combineDIExpressions(const DIExpression *Original, @@ -668,7 +671,8 @@ /// Interpret values loaded into registers by \p CurMI. static void interpretValues(const MachineInstr *CurMI, FwdRegWorklist &ForwardedRegWorklist, - ParamSet &Params) { + ParamSet &Params, + ClobberedRegSet &ClobberedRegUnits) { const MachineFunction *MF = CurMI->getMF(); const DIExpression *EmptyExpr = @@ -700,6 +704,7 @@ // If the MI is an instruction defining one or more parameters' forwarding // registers, add those defines. + ClobberedRegSet NewClobberedRegUnits; auto getForwardingRegsDefinedByMI = [&](const MachineInstr &MI, SmallSetVector &Defs) { if (MI.isDebugInstr()) @@ -710,6 +715,8 @@ for (auto &FwdReg : ForwardedRegWorklist) if (TRI.regsOverlap(FwdReg.first, MO.getReg())) Defs.insert(FwdReg.first); + for (MCRegUnitIterator Units(MO.getReg(), &TRI); Units.isValid(); ++Units) + NewClobberedRegUnits.insert(*Units); } } }; @@ -718,8 +725,22 @@ SmallSetVector FwdRegDefs; getForwardingRegsDefinedByMI(*CurMI, FwdRegDefs); - if (FwdRegDefs.empty()) + if (FwdRegDefs.empty()) { + // Any definitions by this instruction will clobber earlier reg movements. + ClobberedRegUnits.insert(NewClobberedRegUnits.begin(), + NewClobberedRegUnits.end()); return; + } + + // It's possible that we find a copy from a non-volatile register to the param + // register, which is clobbered in the meantime. Test for clobbered reg unit + // overlaps before completing. + auto IsRegClobberedInMeantime = [&](Register Reg) -> bool { + for (auto &RegUnit : ClobberedRegUnits) + if (TRI.hasRegUnit(Reg, RegUnit)) + return true; + return false; + }; for (auto ParamFwdReg : FwdRegDefs) { if (auto ParamValue = TII.describeLoadedValue(*CurMI, ParamFwdReg)) { @@ -732,7 +753,8 @@ Register SP = TLI.getStackPointerRegisterToSaveRestore(); Register FP = TRI.getFrameRegister(*MF); bool IsSPorFP = (RegLoc == SP) || (RegLoc == FP); - if (TRI.isCalleeSavedPhysReg(RegLoc, *MF) || IsSPorFP) { + if (!IsRegClobberedInMeantime(RegLoc) && + (TRI.isCalleeSavedPhysReg(RegLoc, *MF) || IsSPorFP)) { MachineLocation MLoc(RegLoc, /*Indirect=*/IsSPorFP); finishCallSiteParams(MLoc, ParamValue->second, ForwardedRegWorklist[ParamFwdReg], Params); @@ -754,6 +776,10 @@ for (auto ParamFwdReg : FwdRegDefs) ForwardedRegWorklist.erase(ParamFwdReg); + // Any definitions by this instruction will clobber earlier reg movements. + ClobberedRegUnits.insert(NewClobberedRegUnits.begin(), + NewClobberedRegUnits.end()); + // Now that we are done handling this instruction, add items from the // temporary worklist to the real one. for (auto &New : TmpWorklistItems) @@ -763,7 +789,8 @@ static bool interpretNextInstr(const MachineInstr *CurMI, FwdRegWorklist &ForwardedRegWorklist, - ParamSet &Params) { + ParamSet &Params, + ClobberedRegSet &ClobberedRegUnits) { // Skip bundle headers. if (CurMI->isBundle()) return true; @@ -781,7 +808,7 @@ if (CurMI->getNumOperands() == 0) return true; - interpretValues(CurMI, ForwardedRegWorklist, Params); + interpretValues(CurMI, ForwardedRegWorklist, Params, ClobberedRegUnits); return true; } @@ -833,6 +860,7 @@ bool ShouldTryEmitEntryVals = MBB->getIterator() == MF->begin(); // Search for a loading value in forwarding registers inside call delay slot. + ClobberedRegSet ClobberedRegUnits; if (CallMI->hasDelaySlot()) { auto Suc = std::next(CallMI->getIterator()); // Only one-instruction delay slot is supported. @@ -841,14 +869,14 @@ 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)) + if (!interpretNextInstr(&*Suc, ForwardedRegWorklist, Params, ClobberedRegUnits)) return; } // Search for a loading value in forwarding registers. for (; I != MBB->rend(); ++I) { // Try to interpret values loaded by instruction. - if (!interpretNextInstr(&*I, ForwardedRegWorklist, Params)) + if (!interpretNextInstr(&*I, ForwardedRegWorklist, Params, ClobberedRegUnits)) return; } diff --git a/llvm/test/DebugInfo/MIR/X86/dbgcall-site-clobbered-regs.mir b/llvm/test/DebugInfo/MIR/X86/dbgcall-site-clobbered-regs.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dbgcall-site-clobbered-regs.mir @@ -0,0 +1,158 @@ +# RUN: llc %s -o - -filetype=obj -start-after=livedebugvalues \ +# RUN: -emit-call-site-info | \ +# RUN: llvm-dwarfdump - | FileCheck %s --implicit-check-not=call_value +# +## Test that call-site information is produced for this input, but use the +## implicit not above to check that no call value entries are produced for +## the tail call. They're clobbered by the restoration of r14 and rbx. +## +## Addresses https://github.com/llvm/llvm-project/issues/57444 +# +# CHECK: DW_TAG_call_site +# CHECK-NEXT: DW_AT_call_origin +# CHECK-NEXT: DW_AT_call_return_pc +# +# CHECK: DW_TAG_call_site +# CHECK-NEXT: DW_AT_call_origin +# CHECK-NEXT: DW_AT_call_tail_call (true) +# CHECK-NEXT: DW_AT_call_pc + +--- | + ; ModuleID = 'out.ll' + source_filename = "test.c" + 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" + + @str = private unnamed_addr constant [5 x i8] c"ohai\00", align 1 + + ; Function Attrs: nounwind uwtable + define dso_local i32 @foo(i64 noundef %a, i64 noundef %b) local_unnamed_addr #0 !dbg !16 { + entry: + call void @llvm.dbg.value(metadata i64 %a, metadata !22, metadata !DIExpression()), !dbg !24 + call void @llvm.dbg.value(metadata i64 %b, metadata !23, metadata !DIExpression()), !dbg !24 + %call = tail call i32 (...) @baz() #5, !dbg !25 + %conv = trunc i64 %a to i32, !dbg !26 + %conv1 = trunc i64 %b to i32, !dbg !27 + %call2 = tail call i32 @bar(i32 noundef %conv, i32 noundef %conv1) #5, !dbg !28 + ret i32 %call2, !dbg !29 + } + + declare !dbg !30 i32 @baz(...) local_unnamed_addr #1 + + declare !dbg !34 i32 @bar(i32 noundef, i32 noundef) local_unnamed_addr #1 + + ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) + declare void @llvm.dbg.value(metadata, metadata, metadata) #3 + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!9, !10, !11, !12, !13, !14} + !llvm.ident = !{!15} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.c", directory: ".", checksumkind: CSK_MD5, checksum: "98e51a9cf3bc6e1c3cc216ca4c82353a") + !2 = !{!3} + !3 = !DIGlobalVariableExpression(var: !4, expr: !DIExpression()) + !4 = distinct !DIGlobalVariable(scope: null, file: !1, line: 11, type: !5, isLocal: true, isDefinition: true) + !5 = !DICompositeType(tag: DW_TAG_array_type, baseType: !6, size: 48, elements: !7) + !6 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) + !7 = !{!8} + !8 = !DISubrange(count: 6) + !9 = !{i32 7, !"Dwarf Version", i32 5} + !10 = !{i32 2, !"Debug Info Version", i32 3} + !11 = !{i32 1, !"wchar_size", i32 4} + !12 = !{i32 8, !"PIC Level", i32 2} + !13 = !{i32 7, !"PIE Level", i32 2} + !14 = !{i32 7, !"uwtable", i32 2} + !15 = !{!"clang"} + !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) + !17 = !DISubroutineType(types: !18) + !18 = !{!19, !20, !20} + !19 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !20 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) + !21 = !{!22, !23} + !22 = !DILocalVariable(name: "a", arg: 1, scope: !16, file: !1, line: 5, type: !20) + !23 = !DILocalVariable(name: "b", arg: 2, scope: !16, file: !1, line: 5, type: !20) + !24 = !DILocation(line: 0, scope: !16) + !25 = !DILocation(line: 6, column: 3, scope: !16) + !26 = !DILocation(line: 7, column: 14, scope: !16) + !27 = !DILocation(line: 7, column: 17, scope: !16) + !28 = !DILocation(line: 7, column: 10, scope: !16) + !29 = !DILocation(line: 7, column: 3, scope: !16) + !30 = !DISubprogram(name: "baz", scope: !1, file: !1, line: 4, type: !31, spFlags: DISPFlagOptimized, retainedNodes: !33) + !31 = !DISubroutineType(types: !32) + !32 = !{!19} + !33 = !{} + !34 = !DISubprogram(name: "bar", scope: !1, file: !1, line: 3, type: !35, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !33) + !35 = !DISubroutineType(types: !36) + !36 = !{!19, !19, !19} + !37 = distinct !DISubprogram(name: "qux", scope: !1, file: !1, line: 10, type: !31, scopeLine: 10, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !33) + !38 = !DILocation(line: 11, column: 3, scope: !37) + !39 = !DILocation(line: 12, column: 3, scope: !37) + +... +--- +name: foo +alignment: 16 +tracksRegLiveness: true +tracksDebugUserValues: true +liveins: + - { reg: '$rdi' } + - { reg: '$rsi' } +frameInfo: + stackSize: 24 + offsetAdjustment: -24 + maxAlignment: 1 + adjustsStack: true + hasCalls: true + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 16 + hasTailCall: true +fixedStack: + - { id: 0, type: spill-slot, offset: -24, size: 8, alignment: 8, callee-saved-register: '$rbx' } + - { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 16, callee-saved-register: '$r14' } +callSites: + - { bb: 0, offset: 15 } + - { bb: 0, offset: 28, fwdArgRegs: + - { arg: 0, reg: '$edi' } + - { arg: 1, reg: '$esi' } } +debugValueSubstitutions: + - { srcinst: 2, srcop: 0, dstinst: 1, dstop: 0, subreg: 6 } + - { srcinst: 4, srcop: 0, dstinst: 3, dstop: 0, subreg: 6 } +machineFunctionInfo: {} +body: | + bb.0.entry: + liveins: $rdi, $rsi, $r14, $rbx + + DBG_VALUE $rdi, $noreg, !22, !DIExpression(), debug-location !24 + DBG_VALUE $rsi, $noreg, !23, !DIExpression(), debug-location !24 + frame-setup PUSH64r killed $r14, implicit-def $rsp, implicit $rsp + frame-setup CFI_INSTRUCTION def_cfa_offset 16 + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + frame-setup CFI_INSTRUCTION def_cfa_offset 24 + frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp + frame-setup CFI_INSTRUCTION def_cfa_offset 32 + CFI_INSTRUCTION offset $rbx, -24 + CFI_INSTRUCTION offset $r14, -16 + DBG_PHI $rsi, 3 + DBG_PHI $rdi, 1 + $rbx = MOV64rr $rsi + $r14 = MOV64rr $rdi + dead $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, implicit-def $al, debug-location !25 + 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 + DBG_VALUE $rbx, $noreg, !23, !DIExpression(), debug-location !24 + DBG_VALUE $r14, $noreg, !22, !DIExpression(), debug-location !24 + $edi = MOV32rr $r14d, implicit killed $r14, debug-location !28 + $esi = MOV32rr $ebx, implicit killed $rbx, debug-location !28 + $rsp = frame-destroy ADD64ri8 $rsp, 8, implicit-def dead $eflags, debug-location !28 + frame-destroy CFI_INSTRUCTION def_cfa_offset 24, debug-location !28 + ;; This tail-call popping of rbx clobbers the call site information + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28 + DBG_VALUE $rsi, $noreg, !23, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !24 + frame-destroy CFI_INSTRUCTION def_cfa_offset 16, debug-location !28 + ;; This tail-call popping of r14 clobbers the call site information + $r14 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28 + DBG_VALUE $rdi, $noreg, !22, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !24 + frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !28 + TAILJMPd64 target-flags(x86-plt) @bar, csr_64, implicit $rsp, implicit $ssp, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, debug-location !28 + +...