Issue 176444
Summary Debuginfo lost for local variables in nested lexical blocks on rust generated code for powerpc64le
Labels new issue
Assignees
Reporter jchecahi
    
Local variables defined in nested lexical blocks lose their debug info on powerpc64le but not on x86_64 when compiling identical LLVM IR, when using any level of optimization.

The problematic LLVM IR is generated from this Rust code (https://rust.godbolt.org/z/5rP4Kj6nj):

```llvm
; ModuleID = 'example.806d4a770b6a1559-cgu.0'
source_filename = "example.806d4a770b6a1559-cgu.0"
target datalayout = "e-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512"
target triple = "powerpc64le-unknown-linux-gnu"

; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: read) uwtable
define noundef i32 @test_ref(ptr noalias noundef readonly align 8 captures(none) dereferenceable(16) %ref_foo) unnamed_addr #0 !dbg !6 {
start:
    #dbg_value(ptr %ref_foo, !22, !DIExpression(), !31)
 #dbg_value(ptr %ref_foo, !23, !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value), !32)
    #dbg_value(ptr %ref_foo, !26, !DIExpression(), !33)
    #dbg_value(ptr %ref_foo, !29, !DIExpression(DW_OP_plus_uconst, 12, DW_OP_stack_value), !34)
  %0 = getelementptr inbounds nuw i8, ptr %ref_foo, i64 8, !dbg !35
  %_0 = load i32, ptr %0, align 8, !dbg !35, !noundef !20
 ret i32 %_0, !dbg !36
}

attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: read) uwtable "probe-stack"="inline-asm" "target-cpu"="ppc64le" }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}
!llvm.dbg.cu = !{!4}

!0 = !{i32 8, !"PIC Level", i32 2}
!1 = !{i32 7, !"Dwarf Version", i32 4}
!2 = !{i32 2, !"Debug Info Version", i32 3}
!3 = !{!"rustc version 1.92.0 (ded5c06cf 2025-12-08)"}
!4 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !5, producer: "clang LLVM (rustc version 1.92.0 (ded5c06cf 2025-12-08))", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!5 = !DIFile(filename: "/app/example.rs/@/example.806d4a770b6a1559-cgu.0", directory: "/app")
!6 = distinct !DISubprogram(name: "test_ref", scope: !8, file: !7, line: 9, type: !9, scopeLine: 9, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, templateParams: !20, retainedNodes: !21)
!7 = !DIFile(filename: "example.rs", directory: "/app", checksumkind: CSK_MD5, checksum: "708e0ef884439ceded7480119c12616a")
!8 = !DINamespace(name: "example", scope: null)
!9 = !DISubroutineType(types: !10)
!10 = !{!11, !12}
!11 = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed)
!12 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "&example::Foo", baseType: !13, size: 64, align: 64, dwarfAddressSpace: 0)
!13 = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", scope: !8, file: !14, size: 128, align: 64, flags: DIFlagPublic, elements: !15, templateParams: !20, identifier: "2473027896704476d31bc2782fb3cf7")
!14 = !DIFile(filename: "<unknown>", directory: "")
!15 = !{!16, !17, !19}
!16 = !DIDerivedType(tag: DW_TAG_member, name: "__0", scope: !13, file: !14, baseType: !11, size: 32, align: 32, offset: 64, flags: DIFlagPrivate)
!17 = !DIDerivedType(tag: DW_TAG_member, name: "__1", scope: !13, file: !14, baseType: !18, size: 64, align: 64, flags: DIFlagPrivate)
!18 = !DIBasicType(name: "i64", size: 64, encoding: DW_ATE_signed)
!19 = !DIDerivedType(tag: DW_TAG_member, name: "__2", scope: !13, file: !14, baseType: !11, size: 32, align: 32, offset: 96, flags: DIFlagPrivate)
!20 = !{}
!21 = !{!22, !23, !26, !29}
!22 = !DILocalVariable(name: "ref_foo", arg: 1, scope: !6, file: !7, line: 9, type: !12)
!23 = !DILocalVariable(name: "ref_v0", scope: !24, file: !7, line: 10, type: !25, align: 64)
!24 = distinct !DILexicalBlock(scope: !6, file: !7, line: 10, column: 5)
!25 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "&i32", baseType: !11, size: 64, align: 64, dwarfAddressSpace: 0)
!26 = !DILocalVariable(name: "ref_v1", scope: !27, file: !7, line: 11, type: !28, align: 64)
!27 = distinct !DILexicalBlock(scope: !24, file: !7, line: 11, column: 5)
!28 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "&i64", baseType: !18, size: 64, align: 64, dwarfAddressSpace: 0)
!29 = !DILocalVariable(name: "ref_v2", scope: !30, file: !7, line: 12, type: !25, align: 64)
!30 = distinct !DILexicalBlock(scope: !27, file: !7, line: 12, column: 5)
!31 = !DILocation(line: 0, scope: !6)
!32 = !DILocation(line: 10, column: 9, scope: !24)
!33 = !DILocation(line: 11, column: 9, scope: !27)
!34 = !DILocation(line: 12, column: 9, scope: !30)
!35 = !DILocation(line: 13, column: 5, scope: !30)
!36 = !DILocation(line: 14, column: 2, scope: !6)
```

The generated LLVM IR is identical for both architectures. When inspecting the resulting object files:
- x86_64: All 4 variables (ref_foo, ref_v0, ref_v1, ref_v2) appear in DWARF
- PowerPC64LE: Only ref_foo appears; ref_v0, ref_v1, ref_v2 are missing
(Full debuginfo seen in https://rust.godbolt.org/z/eEnEhdEG8)

The missing variables are defined in nested `DILexicalBlock` scopes and have `dbg.value` with expressions like `DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value)`.

This issue seems to be related to how PowerPC's optimization passes handle lexical scopes that contain only debug intrinsics with no executable code.

I attempted some investigation, but I'm no expert in the matter. This C equivalent reproducer with nested blocks (https://godbolt.org/z/cnGx7bT5x) preserves the variables as expected in both x86_64 and ppc64le.  The LLVM IR is almost identical to the one produced from rust, yet in this case the debuginfo is not dropped.

### Environment

- LLVM: llvm-20.1.8-4.fc42, llvm-21.1.8-1.fc43
- Target: powerpc64le-unknown-linux-gnu

_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to