Skip to content

Conversation

@OCHyams
Copy link
Contributor

@OCHyams OCHyams commented Dec 13, 2025

Without this patch DW_AT_call_target is used for all indirect call address location expressions. The DWARF spec says:

For indirect calls or jumps where the address is not computable without use
of registers or memory locations that might be clobbered by the call the
DW_AT_call_target_clobbered attribute is used instead of the
DW_AT_call_target attribute.

This patch implements that behaviour.

…atile regs

Without this patch DW_AT_call_target is used for all indirect call address
location expressions. The DWARF spec says:

    For indirect calls or jumps where the address is not computable without use
    of registers or memory locations that might be clobbered by the call the
    DW_AT_call_target_clobbered attribute is used instead of the
    DW_AT_call_target attribute.

This patch implements that behaviour.
@llvmbot
Copy link
Member

llvmbot commented Dec 13, 2025

@llvm/pr-subscribers-debuginfo

Author: Orlando Cazalet-Hyams (OCHyams)

Changes

Without this patch DW_AT_call_target is used for all indirect call address location expressions. The DWARF spec says:

For indirect calls or jumps where the address is not computable without use
of registers or memory locations that might be clobbered by the call the
DW_AT_call_target_clobbered attribute is used instead of the
DW_AT_call_target attribute.

This patch implements that behaviour.


Full diff: https://github.com/llvm/llvm-project/pull/172167.diff

4 Files Affected:

  • (modified) llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp (+12-5)
  • (added) llvm/test/DebugInfo/X86/dwarf-call-target-clobbered.mir (+96)
  • (modified) llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir (+3-2)
  • (modified) llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll (+2-2)
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

Copy link
Member

@Michael137 Michael137 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

@OCHyams OCHyams merged commit 7927040 into llvm:main Dec 15, 2025
13 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Dec 15, 2025

LLVM Buildbot has detected a new failure on builder llvm-nvptx64-nvidia-win running on as-builder-8 while building llvm at step 7 "test-build-unified-tree-check-llvm".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/155/builds/15726

Here is the relevant piece of the build log for the reference
Step 7 (test-build-unified-tree-check-llvm) failure: test (failure)
******************** TEST 'LLVM-Unit :: Support/./SupportTests.exe/86/209' FAILED ********************
Script(shard):
--
GTEST_OUTPUT=json:C:\buildbot\as-builder-8\llvm-nvptx64-nvidia-win\build\unittests\Support\.\SupportTests.exe-LLVM-Unit-12848-86-209.json GTEST_SHUFFLE=0 GTEST_TOTAL_SHARDS=209 GTEST_SHARD_INDEX=86 C:\buildbot\as-builder-8\llvm-nvptx64-nvidia-win\build\unittests\Support\.\SupportTests.exe
--


Note: This is test shard 87 of 209.

[==========] Running 8 tests from 8 test suites.

[----------] Global test environment set-up.

[----------] 1 test from BinaryStreamTest

[ RUN      ] BinaryStreamTest.StreamReaderIntegers

[       OK ] BinaryStreamTest.StreamReaderIntegers (0 ms)

[----------] 1 test from BinaryStreamTest (0 ms total)



[----------] 1 test from DataExtractorTest

[ RUN      ] DataExtractorTest.getU24

[       OK ] DataExtractorTest.getU24 (0 ms)

[----------] 1 test from DataExtractorTest (0 ms total)



[----------] 1 test from HashBuilderTest/3, where TypeParam = <type>

[ RUN      ] HashBuilderTest/3.HashStringRef

[       OK ] HashBuilderTest/3.HashStringRef (0 ms)

[----------] 1 test from HashBuilderTest/3 (0 ms total)



[----------] 1 test from OverflowTest/2, where TypeParam = <type>

[ RUN      ] OverflowTest/2.MulOverflowToMin

[       OK ] OverflowTest/2.MulOverflowToMin (0 ms)

[----------] 1 test from OverflowTest/2 (0 ms total)
...

@OCHyams
Copy link
Contributor Author

OCHyams commented Dec 15, 2025

Full details are available at: https://lab.llvm.org/buildbot/#/builders/155/builds/15726

Looks like an intermittent failure that has righted itself since.

@zmodem
Copy link
Collaborator

zmodem commented Dec 15, 2025

We're seeing compiler crashes with

DWARF5 attribute with no GNU analog
UNREACHABLE executed at llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp:1305!

after this.

I have an unreduced repro at https://issues.chromium.org/u/1/issues/468825583#comment2

Can you take a look?

@zmodem
Copy link
Collaborator

zmodem commented Dec 15, 2025

I guess reducing was fast :)

$ cat reduced.ll 
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define void @_ZL12_ISO2022OpenP10UConverterP18UConverterLoadArgsP10UErrorCode() !dbg !4 {
entry:
  %call = tail call ptr null(i64 0)
  ret void
}

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3}

!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, isOptimized: true, runtimeVersion: 0, splitDebugFilename: "ucnv2022.dwo", emissionKind: FullDebug, enums: !2, retainedTypes: !2, globals: !2, imports: !2, splitDebugInlining: false, nameTableKind: GNU)
!1 = !DIFile(filename: "/tmp/ucnv2022.cpp", directory: ".")
!2 = !{}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = distinct !DISubprogram(name: "_ISO2022Open", linkageName: "_ZL12_ISO2022OpenP10UConverterP18UConverterLoadArgsP10UErrorCode", scope: !5, file: !5, line: 479, type: !6, scopeLine: 479, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2, keyInstructions: true)
!5 = !DIFile(filename: "../../third_party/icu/source/common/ucnv2022.cpp", directory: ".")
!6 = distinct !DISubroutineType(types: !7)
!7 = !{null}

$ build/bin/llc -o /dev/null reduced.ll 
DWARF5 attribute with no GNU analog
UNREACHABLE executed at /work/llvm-project/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp:1305!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0.      Program arguments: build/bin/llc -o /dev/null reduced.ll
1.      Running pass 'Function Pass Manager' on module 'reduced.ll'.
2.      Running pass 'X86 Assembly Printer' on function '@_ZL12_ISO2022OpenP10UConverterP18UConverterLoadArgsP10UErrorCode'
 #0 0x00005651b7ec3f68 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (build/bin/llc+0x23bbf68)
 #1 0x00005651b7ec1695 llvm::sys::RunSignalHandlers() (build/bin/llc+0x23b9695)
 #2 0x00005651b7ec4d81 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x00007fce79818df0 (/lib/x86_64-linux-gnu/libc.so.6+0x3fdf0)
 #4 0x00007fce7986d95c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x00007fce79818cc2 raise ./signal/../sysdeps/posix/raise.c:27:6
 #6 0x00007fce798014ac abort ./stdlib/abort.c:81:3
 #7 0x00005651b7e3086f (build/bin/llc+0x232886f)
 #8 0x00005651b6dc43d9 llvm::DwarfCompileUnit::constructCallSiteEntryDIE(llvm::DIE&, llvm::DISubprogram const*, llvm::Function const*, bool, llvm::MCSymbol const*, llvm::MCSymbol const*, llvm::MachineLocation, long, llvm::DIType*) (build/bin/llc+0x12bc3d9)
 #9 0x00005651b6d390c0 llvm::DwarfDebug::constructCallSiteEntryDIEs(llvm::DISubprogram const&, llvm::DwarfCompileUnit&, llvm::DIE&, llvm::MachineFunction const&) (build/bin/llc+0x12310c0)
#10 0x00005651b6d47814 llvm::DwarfDebug::endFunctionImpl(llvm::MachineFunction const*) (build/bin/llc+0x123f814)
#11 0x00005651b6db7600 llvm::DebugHandlerBase::endFunction(llvm::MachineFunction const*) (build/bin/llc+0x12af600)
#12 0x00005651b6d1a01f llvm::AsmPrinter::emitFunctionBody() (build/bin/llc+0x121201f)
#13 0x00005651b6158d4f llvm::X86AsmPrinter::runOnMachineFunction(llvm::MachineFunction&) (build/bin/llc+0x650d4f)
#14 0x00005651b6facb93 llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (build/bin/llc+0x14a4b93)
#15 0x00005651b7540835 llvm::FPPassManager::runOnFunction(llvm::Function&) (build/bin/llc+0x1a38835)
#16 0x00005651b75486f2 llvm::FPPassManager::runOnModule(llvm::Module&) (build/bin/llc+0x1a406f2)
#17 0x00005651b7541296 llvm::legacy::PassManagerImpl::run(llvm::Module&) (build/bin/llc+0x1a39296)
#18 0x00005651b60b71d3 compileModule(char**, llvm::LLVMContext&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) llc.cpp:0:0
#19 0x00005651b60b4610 main (build/bin/llc+0x5ac610)
#20 0x00007fce79802ca8 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#21 0x00007fce79802d65 call_init ./csu/../csu/libc-start.c:128:20
#22 0x00007fce79802d65 __libc_start_main ./csu/../csu/libc-start.c:347:5
#23 0x00005651b60b0091 _start (build/bin/llc+0x5a8091)
Aborted

@OCHyams
Copy link
Contributor Author

OCHyams commented Dec 15, 2025

Thanks for the reduced reproducer, taking a look!

OCHyams added a commit to OCHyams/llvm-project that referenced this pull request Dec 15, 2025
@OCHyams
Copy link
Contributor Author

OCHyams commented Dec 15, 2025

Just missed a simple lookup used for some configurations - fix here #172336

OCHyams added a commit that referenced this pull request Dec 17, 2025
)

Not sure whether this comes up in practice, but I noticed a switch block
I missed in #172167
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants