-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[DebugInfo] Use DW_op_bit_piece for structured bindings of bitfields #85665
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
665d403
[DebugInfo] Use DW_op_bit_piece for structured bindings of bitfields
john-brawn-arm 5d35b0f
Add DW_OP_bit_piece test, and adjust DwarfExpression.cpp so dbg.value…
john-brawn-arm c36cdf0
Add DW_OP_bit_piece to LangRef, and adjust DW_OP_LLVM_fragment so it'…
john-brawn-arm 841d8d0
Check that DW_OP_bit_piece is the last expression, and add test
john-brawn-arm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 56 additions & 62 deletions
118
clang/test/CodeGenCXX/debug-info-structured-binding-bitfield.cpp
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
; RUN: llc -mtriple=x86_64-unknown-linux-gnu %s -o %t -filetype=obj | ||
; RUN: llvm-dwarfdump --debug-info %t | FileCheck %s | ||
|
||
%struct.struct_t = type { i8 } | ||
|
||
@g = dso_local global %struct.struct_t zeroinitializer, align 1, !dbg !0 | ||
|
||
; CHECK-LABEL: DW_TAG_subprogram | ||
; CHECK: DW_AT_name ("test1") | ||
; CHECK: DW_TAG_variable | ||
; CHECK: DW_AT_location (DW_OP_fbreg -1, DW_OP_bit_piece 0x3 0x0) | ||
; CHECK: DW_AT_name ("x") | ||
; CHECK: DW_TAG_variable | ||
; CHECK: DW_AT_location (DW_OP_fbreg -1, DW_OP_bit_piece 0x4 0x3) | ||
; CHECK: DW_AT_name ("y") | ||
|
||
define i32 @test1() !dbg !13 { | ||
entry: | ||
%0 = alloca %struct.struct_t, align 1 | ||
tail call void @llvm.dbg.declare(metadata ptr %0, metadata !17, metadata !DIExpression(DW_OP_bit_piece, 3, 0)), !dbg !18 | ||
tail call void @llvm.dbg.declare(metadata ptr %0, metadata !19, metadata !DIExpression(DW_OP_bit_piece, 4, 3)), !dbg !20 | ||
ret i32 0, !dbg !21 | ||
} | ||
|
||
; CHECK-LABEL: DW_TAG_subprogram | ||
; CHECK: DW_AT_name ("test2") | ||
; CHECK: DW_TAG_variable | ||
; CHECK: DW_AT_location (DW_OP_reg0 RAX, DW_OP_bit_piece 0x3 0x0) | ||
; CHECK: DW_AT_name ("x") | ||
; CHECK: DW_TAG_variable | ||
; CHECK: DW_AT_location (DW_OP_reg0 RAX, DW_OP_bit_piece 0x4 0x3) | ||
; CHECK: DW_AT_name ("y") | ||
|
||
define i8 @test2() !dbg !22 { | ||
entry: | ||
%0 = load i8, ptr @g, align 1 | ||
tail call void @llvm.dbg.value(metadata i8 %0, metadata !23, metadata !DIExpression(DW_OP_bit_piece, 3, 0)), !dbg !24 | ||
tail call void @llvm.dbg.value(metadata i8 %0, metadata !25, metadata !DIExpression(DW_OP_bit_piece, 4, 3)), !dbg !26 | ||
ret i8 %0, !dbg !27 | ||
} | ||
|
||
declare void @llvm.dbg.declare(metadata, metadata, metadata) | ||
declare void @llvm.dbg.value(metadata, metadata, metadata) | ||
|
||
!llvm.dbg.cu = !{!2} | ||
!llvm.module.flags = !{!11, !12} | ||
|
||
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) | ||
!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 6, type: !5, isLocal: false, isDefinition: true) | ||
!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) | ||
!3 = !DIFile(filename: "DW_OP_bit_piece.cpp", directory: "./") | ||
!4 = !{!0} | ||
!5 = !DIDerivedType(tag: DW_TAG_typedef, name: "struct_t", file: !3, line: 4, baseType: !6) | ||
!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !3, line: 1, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "_ZTS8struct_t") | ||
!7 = !{!8, !10} | ||
!8 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !6, file: !3, line: 2, baseType: !9, size: 3, flags: DIFlagBitField, extraData: i64 0) | ||
!9 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) | ||
!10 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !6, file: !3, line: 3, baseType: !9, size: 4, offset: 3, flags: DIFlagBitField, extraData: i64 0) | ||
!11 = !{i32 7, !"Dwarf Version", i32 5} | ||
!12 = !{i32 2, !"Debug Info Version", i32 3} | ||
!13 = distinct !DISubprogram(name: "test1", linkageName: "test1", scope: !3, file: !3, line: 8, type: !14, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) | ||
!14 = !DISubroutineType(types: !15) | ||
!15 = !{!9} | ||
!16 = !{} | ||
!17 = !DILocalVariable(name: "x", scope: !13, file: !3, line: 9, type: !9) | ||
!18 = !DILocation(line: 9, column: 9, scope: !13) | ||
!19 = !DILocalVariable(name: "y", scope: !13, file: !3, line: 9, type: !9) | ||
!20 = !DILocation(line: 9, column: 12, scope: !13) | ||
!21 = !DILocation(line: 10, column: 3, scope: !13) | ||
!22 = distinct !DISubprogram(name: "test2", linkageName: "test2", scope: !3, file: !3, line: 8, type: !14, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) | ||
!23 = !DILocalVariable(name: "x", scope: !22, file: !3, line: 9, type: !9) | ||
!24 = !DILocation(line: 9, column: 9, scope: !22) | ||
!25 = !DILocalVariable(name: "y", scope: !22, file: !3, line: 9, type: !9) | ||
!26 = !DILocation(line: 9, column: 12, scope: !22) | ||
!27 = !DILocation(line: 10, column: 3, scope: !22) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
; RUN: not llvm-as -disable-output < %s 2>&1 | FileCheck %s | ||
|
||
; Check that we give an error for expressions that are required to be the last | ||
; in the list. | ||
|
||
!named = !{!0, !1, !2, !3, !4, !5} | ||
; CHECK: invalid expression | ||
; CHECK-NEXT: !DIExpression({{.+}}, 0, 10, {{.+}}) | ||
!0 = !DIExpression(DW_OP_LLVM_fragment, 0, 10, DW_OP_deref) | ||
; CHECK: invalid expression | ||
; CHECK-NEXT: !DIExpression({{.+}}, 0, 20, {{.+}}) | ||
!1 = !DIExpression(DW_OP_bit_piece, 0, 20, DW_OP_deref) | ||
; CHECK: invalid expression | ||
; CHECK-NEXT: !DIExpression({{.+}}, 0, 30, {{.+}}, 3, 0) | ||
!2 = !DIExpression(DW_OP_LLVM_fragment, 0, 30, DW_OP_bit_piece, 3, 0) | ||
; CHECK: invalid expression | ||
; CHECK-NEXT: !DIExpression({{.+}}, 40, 0, {{.+}}, 0, 4) | ||
!3 = !DIExpression(DW_OP_bit_piece, 40, 0, DW_OP_LLVM_fragment, 0, 4) | ||
|
||
; We shouldn't give an error if other expressions come earlier | ||
; CHECK-NOT: invalid expression | ||
!4 = !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 50) | ||
!5 = !DIExpression(DW_OP_deref, DW_OP_bit_piece, 0, 60) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing that is missing right now is discussion whether DW_OP_bit_piece can be used inside of a DW_OP_LLVM_fragment expression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we need more testcases for the edge cases regardless of which way we decide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's already a verifier check that DW_OP_LLVM_fragment is the last expression in the list, so I've made DW_OP_bit_piece do the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that effectively implements option (1) that I outlined above. I think this is a reasonable choice and consistent with how DWARF uses them. So if we do this (and I think that's fine) we need make sure that the bit pieces are correctly reported by DIExpression::getFragmentInfo() or else they will get incorrectly treated by every pass that needs to determine overlapping fragments.
At the very least there should be tests that have LLVM IR that mix & matches DW_OP_bit_piece and DW_OP_LLVM_fragment for the same variable and confirm that this gets lowered into valid DWARF.
Alternatively, if you want to make quick progress, you can also say that we don't support this for now and actively reject mixing the two within the same variable in the Verifyer. The you'd need to make sure that passes like SROA that introduce new fragments deal with this gracefully by creating undef locations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm handling this in the next part, which is SROA support (https://github.com/john-brawn-arm/llvm-project/tree/sroa_bit_piece), though it turns out in most of the places that getFragmentInfo is used it actually is specific to DW_OP_LLVM_fragment and does the wrong thing for DW_OP_bit_piece, so I think adding a separate getBitPieceInfo is simpler.
Currently if the same variable has a DW_OP_LLVM_fragment and anything else this causes assertion failures in DebugLocEntry::finalize in llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am mostly worried about the places that want to answer questions like
DIExpression::fragmentsOverlap()
in order to determine whether a fragment (or bit_piece) should replace or augment another dbg.value or DBG_VALUE for the same variable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DW_OP_bit_piece is like every other standard dwarf expression: if we see a new location for a variable then the variable is now at that location. The current behaviour for DIExpression::fragmentsOverlap() is that it returns true when the expression isn't a fragment, and that's what we want for DW_OP_bit_piece.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not it's not. It's a composite location description delimiter and cannot appear inside a DWARF expression. (See DWARF 5 section 2.6.1.2). That's why it's important that all LLVM algorithms that need to reason about fragments (the LLVM equivalent of composite location descriptions) are aware of them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see, I hadn't noticed that (I'd just read the description of DW_OP_bit_piece and hadn't read the general description of composite location descriptions). I had been understanding something like "DW_OP_reg0 RAX, DW_OP_bit_piece 0x3 0x0" to mean "this variable can be found in the 3 bits at offset 0 in RAX", but it actually means something closer to "the bottom 3 bits of this variable can be found in the 3 bits at offset 0 in RAX".
I think this doesn't make much difference though if we restrict DW_OP_bit_piece in the llvm.dbg intrinsics to use it only for its ability to describe a value stored in part of a register, with the other bits not being described, as I think that's what the behaviour I've currently implemented is. Though maybe it would be better to instead use DW_OP_and and DW_OP_shr to extract the bits from the register. I'll think about this some more.