llvm-project/llvm/test/CodeGen/MIR/X86/diexpr-win32.mir
Nerixyz 91b90652bb
Reland "[CodeView] Generate S_DEFRANGE_REGISTER_REL_INDIR" (#189401)
Initially added in #187709. It was reverted in #188833, because
[llvm-clang-x86_64-sie-win](https://lab.llvm.org/buildbot/#/builders/46/builds/32873)
was failing in
`cross-project-tests/debuginfo-tests/dexter-tests/nrvo.cpp`.

The test passed for me locally. After checking on another machine, I
found that `S_DEFRANGE_REGISTER_REL_INDIR` is only supported by
dbgeng/WinDbg from Windows 10.0 Build 19041 (released 2020) onwards.
SDKs before this will fail to read the value. That buildbot is on
Windows 10.0 Build 17763.

I'm not sure if we should make the generation of that record
conditional. Debuggers that can't read the record will skip it. They'll
still see that there's some local variable, but won't be able to display
the value.

As far as I know, users of older Windows 10 builds should be able to
install a newer Windows SDK and use the WinDbg from that version. But I
haven't tested that.
2026-04-02 12:15:11 +02:00

256 lines
10 KiB
YAML

# RUN: llc -start-after=prologepilog -filetype=obj -O0 %s -o - -experimental-debug-variable-locations=true | llvm-readobj --codeview - | FileCheck %s
# Offsets are now CFA, or VFRAME, relative. Both the NRVO sret pointer and the
# string* parameter are on the stack, NRVO at offset 4 (after RA), and Str at
# offset 8 (next slot). The stack size is 4, so the DW_OP_plus_uconst math
# works out.
# (DW_OP_plus_uconst 12)
# CHECK: LocalSym {
# CHECK-NEXT: Kind: S_LOCAL (0x113E)
# CHECK-NEXT: Type: string* (0x
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: VarName: Str
# CHECK-NEXT: }
# CHECK-NEXT: DefRangeFramePointerRelSym {
# CHECK-NEXT: Kind: S_DEFRANGE_FRAMEPOINTER_REL (0x1142)
# CHECK-NEXT: Offset: 12
# CHECK-NEXT: LocalVariableAddrRange {
# CHECK-NEXT: OffsetStart:
# CHECK-NEXT: ISectStart:
# CHECK-NEXT: Range:
# CHECK-NEXT: }
# CHECK-NEXT: }
# (DW_OP_plus_uconst, 8, DW_OP_deref)
# CHECK: LocalSym {
# CHECK-NEXT: Kind: S_LOCAL (0x113E)
# CHECK-NEXT: Type: string (0x
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: VarName: Result
# CHECK-NEXT: }
# CHECK-NEXT: DefRangeRegisterRelIndirSym {
# CHECK-NEXT: Kind: S_DEFRANGE_REGISTER_REL_INDIR (0x1177)
# CHECK-NEXT: BaseRegister: VFRAME (0x
# CHECK-NEXT: HasSpilledUDTMember: No
# CHECK-NEXT: OffsetInParent: 0
# CHECK-NEXT: BasePointerOffset: 8
# CHECK-NEXT: OffsetInUDT: 0
# CHECK-NEXT: LocalVariableAddrRange {
# CHECK-NEXT: OffsetStart: .text+0x5
# CHECK-NEXT: ISectStart: 0x0
# CHECK-NEXT: Range: 0x18
# CHECK-NEXT: }
# (DW_OP_constu, 4, DW_OP_minus)
# CHECK: LocalSym {
# CHECK-NEXT: Kind: S_LOCAL (0x113E)
# CHECK-NEXT: Type: long (0x12)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: VarName: Bytes
# CHECK-NEXT: }
# CHECK-NEXT: DefRangeRegisterRelSym {
# CHECK-NEXT: Kind: S_DEFRANGE_REGISTER_REL (0x1145)
# CHECK-NEXT: BaseRegister:
# CHECK-NEXT: HasSpilledUDTMember: No
# CHECK-NEXT: OffsetInParent: 0
# CHECK-NEXT: BasePointerOffset: -4
# CHECK-NEXT: LocalVariableAddrRange {
# CHECK-NEXT: OffsetStart:
# CHECK-NEXT: ISectStart:
# CHECK-NEXT: Range:
# CHECK-NEXT: }
# CHECK-NEXT: }
--- |
; ModuleID = '<stdin>'
source_filename = "<stdin>"
target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
target triple = "i386-pc-windows-msvc19.0.24215"
%struct.string = type { i32, i32, ptr }
define void @fun(ptr noalias sret(%struct.string) %agg.result, ptr noalias %str) !dbg !12 {
entry:
call void @llvm.dbg.value(metadata ptr %agg.result, metadata !23, metadata !24), !dbg !25
call void @llvm.dbg.value(metadata ptr %str, metadata !26, metadata !28), !dbg !25
%call = call dereferenceable(12) ptr @getString(), !dbg !29
%0 = bitcast ptr %agg.result to ptr, !dbg !29
%1 = bitcast ptr %call to ptr, !dbg !29
call void @llvm.memcpy.p0.p0.i32(ptr %0, ptr %1, i32 12, i32 4, i1 false), !dbg !29
ret void, !dbg !30
}
define i32 @len(ptr %s, i32 %acc) !dbg !31 {
entry:
%0 = bitcast ptr %s to ptr
%bytes = load i32, ptr %0, !dbg !34
call void @llvm.dbg.value(metadata i32 %bytes, metadata !35, metadata !28), !dbg !34
%1 = add i32 %bytes, %acc, !dbg !36
ret i32 %1, !dbg !36
}
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
declare dereferenceable(12) ptr @getString()
; Function Attrs: argmemonly nounwind
declare void @llvm.memcpy.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i1) #1
; Function Attrs: nounwind
declare void @llvm.stackprotector(ptr, ptr) #2
attributes #0 = { nounwind readnone speculatable }
attributes #1 = { argmemonly nounwind }
attributes #2 = { nounwind }
!llvm.dbg.cu = !{!0}
!llvm.linker.options = !{!3, !4}
!llvm.module.flags = !{!5, !6, !7, !8}
!llvm.ident = !{!9}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 6.0.0 ", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "diexpr.ll", directory: "C:\5Csrc", checksumkind: CSK_MD5, checksum: "c547c362c610fa79e7abaddc76e1efe7")
!2 = !{}
!3 = !{!"/DEFAULTLIB:libcmt.lib"}
!4 = !{!"/DEFAULTLIB:oldnames.lib"}
!5 = !{i32 1, !"NumRegisterParameters", i32 0}
!6 = !{i32 2, !"CodeView", i32 1}
!7 = !{i32 2, !"Debug Info Version", i32 3}
!8 = !{i32 1, !"wchar_size", i32 2}
!9 = !{!"clang version 6.0.0 "}
!10 = !DIExpression(DW_OP_plus_uconst, 12)
!11 = !DIExpression(DW_OP_plus_uconst, 8, DW_OP_deref)
!12 = distinct !DISubprogram(name: "fun", linkageName: "fun", scope: !1, file: !1, line: 9, type: !13, isLocal: false, isDefinition: true, scopeLine: 9, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2)
!13 = !DISubroutineType(types: !14)
!14 = !{!15}
!15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "string", file: !1, line: 1, size: 96, elements: !16, identifier: ".?AUstring@@")
!16 = !{!17, !19, !20}
!17 = !DIDerivedType(tag: DW_TAG_member, name: "length", scope: !15, file: !1, line: 2, baseType: !18, size: 32)
!18 = !DIBasicType(name: "long", size: 32, encoding: DW_ATE_signed)
!19 = !DIDerivedType(tag: DW_TAG_member, name: "size", scope: !15, file: !1, line: 3, baseType: !18, size: 32, offset: 32)
!20 = !DIDerivedType(tag: DW_TAG_member, name: "data", scope: !15, file: !1, line: 4, baseType: !21, size: 32, offset: 64)
!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 32)
!22 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
!23 = !DILocalVariable(name: "Result", scope: !12, file: !1, line: 10, type: !15)
!24 = !DIExpression(DW_OP_deref)
!25 = !DILocation(line: 10, scope: !12)
!26 = !DILocalVariable(name: "Str", scope: !12, file: !1, line: 10, type: !27)
!27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !15, size: 32)
!28 = !DIExpression(DW_OP_constu, 4, DW_OP_minus)
!29 = !DILocation(line: 11, scope: !12)
!30 = !DILocation(line: 12, scope: !12)
!31 = distinct !DISubprogram(name: "len", linkageName: "len", scope: !1, file: !1, line: 14, type: !32, isLocal: false, isDefinition: true, scopeLine: 14, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2)
!32 = !DISubroutineType(types: !33)
!33 = !{!18}
!34 = !DILocation(line: 15, scope: !31)
!35 = !DILocalVariable(name: "Bytes", scope: !31, file: !1, line: 15, type: !18)
!36 = !DILocation(line: 16, scope: !31)
...
---
name: fun
alignment: 16
exposesReturnsTwice: false
legalized: false
regBankSelected: false
selected: false
tracksRegLiveness: true
registers:
liveins:
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 4
offsetAdjustment: 0
maxAlignment: 4
adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
savePoint: []
restorePoint: []
fixedStack:
- { id: 0, type: spill-slot, offset: -8, size: 4, alignment: 4, stack-id: default,
callee-saved-register: '$esi' }
- { id: 1, type: default, offset: 4, size: 4, alignment: 4, stack-id: default,
isImmutable: true, isAliased: false, callee-saved-register: '' }
- { id: 2, type: default, offset: 0, size: 4, alignment: 4, stack-id: default,
isImmutable: true, isAliased: false, callee-saved-register: '' }
stack:
constants:
body: |
bb.0.entry:
liveins: $esi
frame-setup PUSH32r killed $esi, implicit-def $esp, implicit $esp
CFI_INSTRUCTION def_cfa_offset 8
CFI_INSTRUCTION offset $esi, -8
$esi = MOV32rm $esp, 1, _, 8, _ :: (load (s32) from %fixed-stack.2)
DBG_VALUE $esp, 0, !26, !10, debug-location !25
DBG_VALUE $esp, 0, !23, !DIExpression(DW_OP_plus_uconst, 8, DW_OP_deref), debug-location !25
CALLpcrel32 @getString, csr_32, implicit $esp, implicit-def $esp, implicit-def $eax, debug-location !29
$ecx = MOV32rm $eax, 1, _, 0, _, debug-location !29 :: (dereferenceable load (s32) from %ir.1)
$edx = MOV32rm $eax, 1, _, 4, _, debug-location !29 :: (dereferenceable load (s32) from %ir.1 + 4)
MOV32mr $esi, 1, _, 0, _, killed $ecx, debug-location !29 :: (store (s32) into %ir.0)
MOV32mr $esi, 1, _, 4, _, killed $edx, debug-location !29 :: (store (s32) into %ir.0 + 4)
$eax = MOV32rm killed $eax, 1, _, 8, _, debug-location !29 :: (dereferenceable load (s32) from %ir.1 + 8)
MOV32mr $esi, 1, _, 8, _, killed $eax, debug-location !29 :: (store (s32) into %ir.0 + 8)
$eax = COPY killed $esi, debug-location !30
$esi = POP32r implicit-def $esp, implicit $esp, debug-location !30
RET 0, $eax, debug-location !30
...
---
name: len
alignment: 16
exposesReturnsTwice: false
legalized: false
regBankSelected: false
selected: false
tracksRegLiveness: true
registers:
liveins:
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 0
offsetAdjustment: 0
maxAlignment: 4
adjustsStack: false
hasCalls: false
stackProtector: ''
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
savePoint: []
restorePoint: []
fixedStack:
- { id: 0, type: default, offset: 4, size: 4, alignment: 4, stack-id: default,
isImmutable: true, isAliased: false, callee-saved-register: '' }
- { id: 1, type: default, offset: 0, size: 4, alignment: 4, stack-id: default,
isImmutable: true, isAliased: false, callee-saved-register: '' }
stack:
constants:
body: |
bb.0.entry:
$eax = MOV32rm $esp, 1, _, 4, _ :: (load (s32) from %fixed-stack.1)
$eax = MOV32rm killed $eax, 1, _, 0, _, debug-location !34 :: (load (s32) from %ir.0)
DBG_VALUE $eax, 0, !35, !DIExpression(DW_OP_constu, 4, DW_OP_minus), debug-location !34
$eax = ADD32rm killed $eax, $esp, 1, _, 8, _, implicit-def dead $eflags, debug-location !36 :: (load (s32) from %fixed-stack.0)
RET 0, $eax, debug-location !36
...