diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index d19de7f8000ec..c0421e628c7e9 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -1328,15 +1328,22 @@ DIE &DwarfCompileUnit::constructCallSiteEntryDIE( // A valid register in CallTarget indicates an indirect call. if (CallTarget.getReg()) { + // Add a DW_AT_call_target location expression describing the location of + // the address of the target function. If any register in the expression + // (i.e., the single register we currently handle) is volatile we must use + // DW_AT_call_target_clobbered instead. + const TargetRegisterInfo &TRI = *Asm->MF->getSubtarget().getRegisterInfo(); + dwarf::Attribute Attribute = getDwarf5OrGNUAttr( + TRI.isCalleeSavedPhysReg(CallTarget.getReg(), *Asm->MF) + ? dwarf::DW_AT_call_target + : dwarf::DW_AT_call_target_clobbered); + // CallTarget is the location of the address of an indirect call. The // location may be indirect, modified by Offset. if (CallTarget.isIndirect()) - addMemoryLocation(CallSiteDIE, - getDwarf5OrGNUAttr(dwarf::DW_AT_call_target), - CallTarget, Offset); + addMemoryLocation(CallSiteDIE, Attribute, CallTarget, Offset); else - addAddress(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_target), - CallTarget); + addAddress(CallSiteDIE, Attribute, CallTarget); } else if (CalleeSP) { DIE *CalleeDIE = getOrCreateSubprogramDIE(CalleeSP, CalleeF); assert(CalleeDIE && "Could not create DIE for call site entry origin"); diff --git a/llvm/test/DebugInfo/X86/dwarf-call-target-clobbered.mir b/llvm/test/DebugInfo/X86/dwarf-call-target-clobbered.mir new file mode 100644 index 0000000000000..ff5c78d7ad1cf --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarf-call-target-clobbered.mir @@ -0,0 +1,96 @@ +# RUN: llc %s --start-after=livedebugvalues -o - --filetype=obj | llvm-dwarfdump - | FileCheck %s + +## Check that DW_AT_call_target_clobbered is used for a location expression +## using a volatile register, otherwise DW_AT_call_target is used. + +## Generated from this C++ with llc -stop-after=livedebugvalues -simplify-mir: +## __attribute__((disable_tail_calls)) void call_mem(void (**f)()) { +## (*f)(); +## (*f)(); +## } + +## Which disassembles to - +## 0000000000000000 <_Z8call_memPPFvvE>: +## 0: 53 pushq %rbx +## 1: 48 89 fb movq %rdi, %rbx +## 4: ff 17 callq *(%rdi) +## 6: ff 13 callq *(%rbx) +## 8: 5b popq %rbx +## 9: c3 retq + +# CHECK: DW_TAG_call_site +# CHECK-NEXT: DW_AT_call_target_clobbered (DW_OP_breg5 RDI+0) +# CHECK: DW_TAG_call_site +# CHECK-NEXT: DW_AT_call_target (DW_OP_breg3 RBX+0) + +--- | + target triple = "x86_64-unknown-linux-gnu" + + define dso_local void @_Z8call_memPPFvvE(ptr noundef readonly captures(none) %f) local_unnamed_addr !dbg !5 { + entry: + %0 = load ptr, ptr %f, align 8, !dbg !13 + call void %0(), !dbg !13 + %1 = load ptr, ptr %f, align 8, !dbg !14 + call void %1(), !dbg !14 + ret void + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2, !3} + !llvm.ident = !{!4} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 22.0.0git", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{i32 7, !"Dwarf Version", i32 5} + !3 = !{i32 2, !"Debug Info Version", i32 3} + !4 = !{!"clang version 22.0.0git"} + !5 = distinct !DISubprogram(name: "call_mem", linkageName: "_Z8call_memPPFvvE", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12) + !6 = !DISubroutineType(types: !7) + !7 = !{null, !8} + !8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64) + !9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) + !10 = !DISubroutineType(types: !11) + !11 = !{null} + !12 = !{} + !13 = !DILocation(line: 2, scope: !5) + !14 = !DILocation(line: 3, scope: !5) +... +--- +name: _Z8call_memPPFvvE +alignment: 16 +tracksRegLiveness: true +noPhis: true +isSSA: false +noVRegs: true +hasFakeUses: false +debugInstrRef: true +tracksDebugUserValues: true +liveins: + - { reg: '$rdi' } +frameInfo: + stackSize: 8 + offsetAdjustment: -8 + maxAlignment: 1 + adjustsStack: true + hasCalls: true + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 8 + isCalleeSavedInfoValid: true +fixedStack: + - { id: 0, type: spill-slot, offset: -16, size: 8, alignment: 16, callee-saved-register: '$rbx' } +machineFunctionInfo: + amxProgModel: None +body: | + bb.0.entry: + liveins: $rdi, $rbx + + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + frame-setup CFI_INSTRUCTION def_cfa_offset 16 + CFI_INSTRUCTION offset $rbx, -16 + $rbx = MOV64rr $rdi + CALL64m $rdi, 1, $noreg, 0, $noreg, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !13 :: (load (s64) from %ir.f) + CALL64m killed renamable $rbx, 1, $noreg, 0, $noreg, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !14 :: (load (s64) from %ir.f) + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp + frame-destroy CFI_INSTRUCTION def_cfa_offset 8 + RET64 +... diff --git a/llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir b/llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir index ef8a080cebaae..1923baf9fa173 100644 --- a/llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir +++ b/llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir @@ -1,10 +1,11 @@ # RUN: llc %s --start-after=livedebugvalues -o - --filetype=obj | llvm-dwarfdump - | FileCheck %s ## Check the memory location of the target address for the indirect call -## (virtual in this case) is described by a DW_AT_call_target expression. +## (virtual in this case) is described by a DW_AT_call_target_clobbered +## expression. # CHECK: DW_TAG_call_site -# CHECK-NEXT: DW_AT_call_target (DW_OP_breg0 RAX+8) +# CHECK-NEXT: DW_AT_call_target_clobbered (DW_OP_breg0 RAX+8) ## Generated from this C++ with llc -stop-after=livedebugvalues -simplify-mir: ## struct Base { diff --git a/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll b/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll index f64b78f5820e4..70f91c66d4e58 100644 --- a/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll +++ b/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll @@ -18,7 +18,7 @@ entry: #dbg_value(ptr %f, !17, !DIExpression(), !18) ; OBJ: DW_TAG_call_site -; OBJ: DW_AT_call_target (DW_OP_reg[[#]] {{.*}}) +; OBJ: DW_AT_call_target{{(_clobbered)?}} (DW_OP_reg[[#]] {{.*}}) ; OBJ: DW_AT_call_return_pc call void (...) %f() #1, !dbg !19 ret void, !dbg !20 @@ -33,7 +33,7 @@ entry: %0 = load ptr, ptr %f, align 8, !dbg !28, !tbaa !29 ; OBJ: DW_TAG_call_site -; OBJ: DW_AT_call_target (DW_OP_breg[[#]] {{.*}}) +; OBJ: DW_AT_call_target{{(_clobbered)?}} (DW_OP_breg[[#]] {{.*}}) ; OBJ: DW_AT_call_return_pc call void (...) %0() #1, !dbg !28 ret void, !dbg !33