Skip to content

Commit 1721c14

Browse files
[DebugInfo] Add DW_OP_LLVM_extract_bits (#93990)
This operation extracts a number of bits at a given offset and sign or zero extends them, which is done by emitting it as a left shift followed by a right shift. This is being added for use in clang for C++ structured bindings of bitfields that have offset or size that aren't a byte multiple. A new operation is being added, instead of shifts being used directly, as it makes correctly handling it in optimisations (which will be done in a later patch) much easier.
1 parent 0d1b367 commit 1721c14

File tree

6 files changed

+151
-6
lines changed

6 files changed

+151
-6
lines changed

llvm/docs/LangRef.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6308,6 +6308,15 @@ The current supported opcode vocabulary is limited:
63086308
(``16`` and ``DW_ATE_signed`` here, respectively) to which the top of the
63096309
expression stack is to be converted. Maps into a ``DW_OP_convert`` operation
63106310
that references a base type constructed from the supplied values.
6311+
- ``DW_OP_LLVM_extract_bits_sext, 16, 8,`` specifies the offset and size
6312+
(``16`` and ``8`` here, respectively) of bits that are to be extracted and
6313+
sign-extended from the value at the top of the expression stack. If the top of
6314+
the expression stack is a memory location then these bits are extracted from
6315+
the value pointed to by that memory location. Maps into a ``DW_OP_shl``
6316+
followed by ``DW_OP_shra``.
6317+
- ``DW_OP_LLVM_extract_bits_zext`` behaves similarly to
6318+
``DW_OP_LLVM_extract_bits_sext``, but zero-extends instead of sign-extending.
6319+
Maps into a ``DW_OP_shl`` followed by ``DW_OP_shr``.
63116320
- ``DW_OP_LLVM_tag_offset, tag_offset`` specifies that a memory tag should be
63126321
optionally applied to the pointer. The memory tag is derived from the
63136322
given tag offset in an implementation-defined manner.

llvm/include/llvm/BinaryFormat/Dwarf.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,14 @@ enum LocationAtom {
138138
#include "llvm/BinaryFormat/Dwarf.def"
139139
DW_OP_lo_user = 0xe0,
140140
DW_OP_hi_user = 0xff,
141-
DW_OP_LLVM_fragment = 0x1000, ///< Only used in LLVM metadata.
142-
DW_OP_LLVM_convert = 0x1001, ///< Only used in LLVM metadata.
143-
DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata.
144-
DW_OP_LLVM_entry_value = 0x1003, ///< Only used in LLVM metadata.
145-
DW_OP_LLVM_implicit_pointer = 0x1004, ///< Only used in LLVM metadata.
146-
DW_OP_LLVM_arg = 0x1005, ///< Only used in LLVM metadata.
141+
DW_OP_LLVM_fragment = 0x1000, ///< Only used in LLVM metadata.
142+
DW_OP_LLVM_convert = 0x1001, ///< Only used in LLVM metadata.
143+
DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata.
144+
DW_OP_LLVM_entry_value = 0x1003, ///< Only used in LLVM metadata.
145+
DW_OP_LLVM_implicit_pointer = 0x1004, ///< Only used in LLVM metadata.
146+
DW_OP_LLVM_arg = 0x1005, ///< Only used in LLVM metadata.
147+
DW_OP_LLVM_extract_bits_sext = 0x1006, ///< Only used in LLVM metadata.
148+
DW_OP_LLVM_extract_bits_zext = 0x1007, ///< Only used in LLVM metadata.
147149
};
148150

149151
enum LlvmUserLocationAtom {

llvm/lib/BinaryFormat/Dwarf.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ StringRef llvm::dwarf::OperationEncodingString(unsigned Encoding) {
155155
return "DW_OP_LLVM_implicit_pointer";
156156
case DW_OP_LLVM_arg:
157157
return "DW_OP_LLVM_arg";
158+
case DW_OP_LLVM_extract_bits_sext:
159+
return "DW_OP_LLVM_extract_bits_sext";
160+
case DW_OP_LLVM_extract_bits_zext:
161+
return "DW_OP_LLVM_extract_bits_zext";
158162
}
159163
}
160164

@@ -169,6 +173,8 @@ unsigned llvm::dwarf::getOperationEncoding(StringRef OperationEncodingString) {
169173
.Case("DW_OP_LLVM_entry_value", DW_OP_LLVM_entry_value)
170174
.Case("DW_OP_LLVM_implicit_pointer", DW_OP_LLVM_implicit_pointer)
171175
.Case("DW_OP_LLVM_arg", DW_OP_LLVM_arg)
176+
.Case("DW_OP_LLVM_extract_bits_sext", DW_OP_LLVM_extract_bits_sext)
177+
.Case("DW_OP_LLVM_extract_bits_zext", DW_OP_LLVM_extract_bits_zext)
172178
.Default(0);
173179
}
174180

llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/CodeGen/Register.h"
1919
#include "llvm/CodeGen/TargetRegisterInfo.h"
2020
#include "llvm/IR/DataLayout.h"
21+
#include "llvm/MC/MCAsmInfo.h"
2122
#include "llvm/Support/ErrorHandling.h"
2223
#include <algorithm>
2324

@@ -546,6 +547,37 @@ bool DwarfExpression::addExpression(
546547
LocationKind = Unknown;
547548
return true;
548549
}
550+
case dwarf::DW_OP_LLVM_extract_bits_sext:
551+
case dwarf::DW_OP_LLVM_extract_bits_zext: {
552+
unsigned SizeInBits = Op->getArg(1);
553+
unsigned BitOffset = Op->getArg(0);
554+
555+
// If we have a memory location then dereference to get the value
556+
if (isMemoryLocation())
557+
emitOp(dwarf::DW_OP_deref);
558+
559+
// Extract the bits by a shift left (to shift out the bits after what we
560+
// want to extract) followed by shift right (to shift the bits to position
561+
// 0 and also sign/zero extend). These operations are done in the DWARF
562+
// "generic type" whose size is the size of a pointer.
563+
unsigned PtrSizeInBytes = CU.getAsmPrinter()->MAI->getCodePointerSize();
564+
unsigned LeftShift = PtrSizeInBytes * 8 - (SizeInBits + BitOffset);
565+
unsigned RightShift = LeftShift + BitOffset;
566+
if (LeftShift) {
567+
emitOp(dwarf::DW_OP_constu);
568+
emitUnsigned(LeftShift);
569+
emitOp(dwarf::DW_OP_shl);
570+
}
571+
emitOp(dwarf::DW_OP_constu);
572+
emitUnsigned(RightShift);
573+
emitOp(OpNum == dwarf::DW_OP_LLVM_extract_bits_sext ? dwarf::DW_OP_shra
574+
: dwarf::DW_OP_shr);
575+
576+
// The value is now at the top of the stack, so set the location to
577+
// implicit so that we get a stack_value at the end.
578+
LocationKind = Implicit;
579+
break;
580+
}
549581
case dwarf::DW_OP_plus_uconst:
550582
assert(!isRegisterLocation());
551583
emitOp(dwarf::DW_OP_plus_uconst);

llvm/lib/IR/DebugInfoMetadata.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,6 +1404,8 @@ unsigned DIExpression::ExprOperand::getSize() const {
14041404
switch (Op) {
14051405
case dwarf::DW_OP_LLVM_convert:
14061406
case dwarf::DW_OP_LLVM_fragment:
1407+
case dwarf::DW_OP_LLVM_extract_bits_sext:
1408+
case dwarf::DW_OP_LLVM_extract_bits_zext:
14071409
case dwarf::DW_OP_bregx:
14081410
return 3;
14091411
case dwarf::DW_OP_constu:
@@ -1474,6 +1476,8 @@ bool DIExpression::isValid() const {
14741476
case dwarf::DW_OP_LLVM_convert:
14751477
case dwarf::DW_OP_LLVM_arg:
14761478
case dwarf::DW_OP_LLVM_tag_offset:
1479+
case dwarf::DW_OP_LLVM_extract_bits_sext:
1480+
case dwarf::DW_OP_LLVM_extract_bits_zext:
14771481
case dwarf::DW_OP_constu:
14781482
case dwarf::DW_OP_plus_uconst:
14791483
case dwarf::DW_OP_plus:
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
; RUN: llc -mtriple=x86_64-unknown-linux-gnu %s -o %t -filetype=obj
2+
; RUN: llvm-dwarfdump --debug-info %t | FileCheck %s
3+
4+
%struct.struct_t = type { i8 }
5+
6+
@g = dso_local global %struct.struct_t zeroinitializer, align 1, !dbg !0
7+
8+
; CHECK-LABEL: DW_TAG_subprogram
9+
; CHECK: DW_AT_name ("test1")
10+
; CHECK: DW_TAG_variable
11+
; CHECK: DW_AT_location (DW_OP_fbreg -1, DW_OP_deref, DW_OP_constu 0x3d, DW_OP_shl, DW_OP_constu 0x3d, DW_OP_shr, DW_OP_stack_value)
12+
; CHECK: DW_AT_name ("x")
13+
; CHECK: DW_TAG_variable
14+
; CHECK: DW_AT_location (DW_OP_fbreg -1, DW_OP_deref, DW_OP_constu 0x39, DW_OP_shl, DW_OP_constu 0x3c, DW_OP_shra, DW_OP_stack_value)
15+
; CHECK: DW_AT_name ("y")
16+
17+
define i32 @test1() !dbg !13 {
18+
entry:
19+
%0 = alloca %struct.struct_t, align 1
20+
tail call void @llvm.dbg.declare(metadata ptr %0, metadata !16, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 3)), !dbg !17
21+
tail call void @llvm.dbg.declare(metadata ptr %0, metadata !18, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 3, 4)), !dbg !17
22+
ret i32 0, !dbg !17
23+
}
24+
25+
; CHECK-LABEL: DW_TAG_subprogram
26+
; CHECK: DW_AT_name ("test2")
27+
; CHECK: DW_TAG_variable
28+
; CHECK: DW_AT_location (DW_OP_breg0 {{R[^+]+}}+0, DW_OP_constu 0xff, DW_OP_and, DW_OP_constu 0x3d, DW_OP_shl, DW_OP_constu 0x3d, DW_OP_shr, DW_OP_stack_value)
29+
; CHECK: DW_AT_name ("x")
30+
; CHECK: DW_TAG_variable
31+
; CHECK: DW_AT_location (DW_OP_breg0 {{R[^+]+}}+0, DW_OP_constu 0xff, DW_OP_and, DW_OP_constu 0x39, DW_OP_shl, DW_OP_constu 0x3c, DW_OP_shra, DW_OP_stack_value)
32+
; CHECK: DW_AT_name ("y")
33+
34+
define i8 @test2() !dbg !20 {
35+
entry:
36+
%0 = load i8, ptr @g, align 1
37+
tail call void @llvm.dbg.value(metadata i8 %0, metadata !21, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 3)), !dbg !22
38+
tail call void @llvm.dbg.value(metadata i8 %0, metadata !23, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 3, 4)), !dbg !22
39+
ret i8 %0, !dbg !22
40+
}
41+
42+
; CHECK-LABEL: DW_TAG_subprogram
43+
; CHECK: DW_AT_name ("test3")
44+
; CHECK: DW_TAG_variable
45+
; CHECK: DW_AT_location (DW_OP_breg0 {{R[^+]+}}+0, DW_OP_constu 0x3f, DW_OP_shr, DW_OP_stack_value)
46+
; CHECK: DW_AT_name ("x")
47+
; CHECK: DW_TAG_variable
48+
; CHECK: DW_AT_location (DW_OP_breg0 {{R[^+]+}}+0, DW_OP_constu 0x3f, DW_OP_shra, DW_OP_stack_value)
49+
; CHECK: DW_AT_name ("y")
50+
51+
define i64 @test3(ptr %p) !dbg !24 {
52+
entry:
53+
%0 = load i64, ptr %p, align 8
54+
tail call void @llvm.dbg.value(metadata i64 %0, metadata !25, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 63, 1)), !dbg !26
55+
tail call void @llvm.dbg.value(metadata i64 %0, metadata !27, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 63, 1)), !dbg !26
56+
ret i64 %0, !dbg !26
57+
}
58+
59+
declare void @llvm.dbg.declare(metadata, metadata, metadata)
60+
declare void @llvm.dbg.value(metadata, metadata, metadata)
61+
62+
!llvm.dbg.cu = !{!2}
63+
!llvm.module.flags = !{!11, !12}
64+
65+
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
66+
!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, type: !5, isLocal: false, isDefinition: true)
67+
!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
68+
!3 = !DIFile(filename: "DW_OP_bit_piece.cpp", directory: "./")
69+
!4 = !{!0}
70+
!5 = !DIDerivedType(tag: DW_TAG_typedef, name: "struct_t", file: !3, baseType: !6)
71+
!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !3, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "_ZTS8struct_t")
72+
!7 = !{!8, !10}
73+
!8 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !6, file: !3, baseType: !9, size: 3, flags: DIFlagBitField, extraData: i64 0)
74+
!9 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
75+
!10 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !6, file: !3, baseType: !9, size: 4, offset: 3, flags: DIFlagBitField, extraData: i64 0)
76+
!11 = !{i32 7, !"Dwarf Version", i32 5}
77+
!12 = !{i32 2, !"Debug Info Version", i32 3}
78+
!13 = distinct !DISubprogram(name: "test1", linkageName: "test1", scope: !3, file: !3, type: !14, spFlags: DISPFlagDefinition, unit: !2)
79+
!14 = !DISubroutineType(types: !15)
80+
!15 = !{!9}
81+
!16 = !DILocalVariable(name: "x", scope: !13, file: !3, type: !9)
82+
!17 = !DILocation(line: 0, scope: !13)
83+
!18 = !DILocalVariable(name: "y", scope: !13, file: !3, type: !19)
84+
!19 = !DIBasicType(name: "signed int", size: 32, encoding: DW_ATE_signed)
85+
!20 = distinct !DISubprogram(name: "test2", linkageName: "test2", scope: !3, file: !3, type: !14, spFlags: DISPFlagDefinition, unit: !2)
86+
!21 = !DILocalVariable(name: "x", scope: !20, file: !3, type: !9)
87+
!22 = !DILocation(line: 0, scope: !20)
88+
!23 = !DILocalVariable(name: "y", scope: !20, file: !3, type: !19)
89+
!24 = distinct !DISubprogram(name: "test3", linkageName: "test3", scope: !3, file: !3, type: !14, spFlags: DISPFlagDefinition, unit: !2)
90+
!25 = !DILocalVariable(name: "x", scope: !24, file: !3, type: !9)
91+
!26 = !DILocation(line: 0, scope: !24)
92+
!27 = !DILocalVariable(name: "y", scope: !24, file: !3, type: !19)

0 commit comments

Comments
 (0)