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.
256 lines
10 KiB
YAML
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
|
|
|
|
...
|