Skip to content

[DebugInfo] Add DW_OP_LLVM_extract_bits #93990

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6312,6 +6312,15 @@ The current supported opcode vocabulary is limited:
(``16`` and ``DW_ATE_signed`` here, respectively) to which the top of the
expression stack is to be converted. Maps into a ``DW_OP_convert`` operation
that references a base type constructed from the supplied values.
- ``DW_OP_LLVM_extract_bits_sext, 16, 8,`` specifies the offset and size
(``16`` and ``8`` here, respectively) of bits that are to be extracted and
sign-extended from the value at the top of the expression stack. If the top of
the expression stack is a memory location then these bits are extracted from
the value pointed to by that memory location. Maps into a ``DW_OP_shl``
followed by ``DW_OP_shra``.
- ``DW_OP_LLVM_extract_bits_zext`` behaves similarly to
``DW_OP_LLVM_extract_bits_sext``, but zero-extends instead of sign-extending.
Maps into a ``DW_OP_shl`` followed by ``DW_OP_shr``.
- ``DW_OP_LLVM_tag_offset, tag_offset`` specifies that a memory tag should be
optionally applied to the pointer. The memory tag is derived from the
given tag offset in an implementation-defined manner.
Expand Down
14 changes: 8 additions & 6 deletions llvm/include/llvm/BinaryFormat/Dwarf.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,14 @@ enum LocationAtom {
#include "llvm/BinaryFormat/Dwarf.def"
DW_OP_lo_user = 0xe0,
DW_OP_hi_user = 0xff,
DW_OP_LLVM_fragment = 0x1000, ///< Only used in LLVM metadata.
DW_OP_LLVM_convert = 0x1001, ///< Only used in LLVM metadata.
DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata.
DW_OP_LLVM_entry_value = 0x1003, ///< Only used in LLVM metadata.
DW_OP_LLVM_implicit_pointer = 0x1004, ///< Only used in LLVM metadata.
DW_OP_LLVM_arg = 0x1005, ///< Only used in LLVM metadata.
DW_OP_LLVM_fragment = 0x1000, ///< Only used in LLVM metadata.
DW_OP_LLVM_convert = 0x1001, ///< Only used in LLVM metadata.
DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata.
DW_OP_LLVM_entry_value = 0x1003, ///< Only used in LLVM metadata.
DW_OP_LLVM_implicit_pointer = 0x1004, ///< Only used in LLVM metadata.
DW_OP_LLVM_arg = 0x1005, ///< Only used in LLVM metadata.
DW_OP_LLVM_extract_bits_sext = 0x1006, ///< Only used in LLVM metadata.
DW_OP_LLVM_extract_bits_zext = 0x1007, ///< Only used in LLVM metadata.
};

enum LlvmUserLocationAtom {
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/BinaryFormat/Dwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ StringRef llvm::dwarf::OperationEncodingString(unsigned Encoding) {
return "DW_OP_LLVM_implicit_pointer";
case DW_OP_LLVM_arg:
return "DW_OP_LLVM_arg";
case DW_OP_LLVM_extract_bits_sext:
return "DW_OP_LLVM_extract_bits_sext";
case DW_OP_LLVM_extract_bits_zext:
return "DW_OP_LLVM_extract_bits_zext";
}
}

Expand All @@ -169,6 +173,8 @@ unsigned llvm::dwarf::getOperationEncoding(StringRef OperationEncodingString) {
.Case("DW_OP_LLVM_entry_value", DW_OP_LLVM_entry_value)
.Case("DW_OP_LLVM_implicit_pointer", DW_OP_LLVM_implicit_pointer)
.Case("DW_OP_LLVM_arg", DW_OP_LLVM_arg)
.Case("DW_OP_LLVM_extract_bits_sext", DW_OP_LLVM_extract_bits_sext)
.Case("DW_OP_LLVM_extract_bits_zext", DW_OP_LLVM_extract_bits_zext)
.Default(0);
}

Expand Down
32 changes: 32 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "llvm/CodeGen/Register.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/ErrorHandling.h"
#include <algorithm>

Expand Down Expand Up @@ -546,6 +547,37 @@ bool DwarfExpression::addExpression(
LocationKind = Unknown;
return true;
}
case dwarf::DW_OP_LLVM_extract_bits_sext:
case dwarf::DW_OP_LLVM_extract_bits_zext: {
unsigned SizeInBits = Op->getArg(1);
unsigned BitOffset = Op->getArg(0);

// If we have a memory location then dereference to get the value
if (isMemoryLocation())
emitOp(dwarf::DW_OP_deref);

// Extract the bits by a shift left (to shift out the bits after what we
// want to extract) followed by shift right (to shift the bits to position
// 0 and also sign/zero extend). These operations are done in the DWARF
// "generic type" whose size is the size of a pointer.
unsigned PtrSizeInBytes = CU.getAsmPrinter()->MAI->getCodePointerSize();
unsigned LeftShift = PtrSizeInBytes * 8 - (SizeInBits + BitOffset);
unsigned RightShift = LeftShift + BitOffset;
if (LeftShift) {
emitOp(dwarf::DW_OP_constu);
emitUnsigned(LeftShift);
emitOp(dwarf::DW_OP_shl);
}
emitOp(dwarf::DW_OP_constu);
emitUnsigned(RightShift);
emitOp(OpNum == dwarf::DW_OP_LLVM_extract_bits_sext ? dwarf::DW_OP_shra
: dwarf::DW_OP_shr);

// The value is now at the top of the stack, so set the location to
// implicit so that we get a stack_value at the end.
LocationKind = Implicit;
break;
}
case dwarf::DW_OP_plus_uconst:
assert(!isRegisterLocation());
emitOp(dwarf::DW_OP_plus_uconst);
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/IR/DebugInfoMetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1404,6 +1404,8 @@ unsigned DIExpression::ExprOperand::getSize() const {
switch (Op) {
case dwarf::DW_OP_LLVM_convert:
case dwarf::DW_OP_LLVM_fragment:
case dwarf::DW_OP_LLVM_extract_bits_sext:
case dwarf::DW_OP_LLVM_extract_bits_zext:
case dwarf::DW_OP_bregx:
return 3;
case dwarf::DW_OP_constu:
Expand Down Expand Up @@ -1474,6 +1476,8 @@ bool DIExpression::isValid() const {
case dwarf::DW_OP_LLVM_convert:
case dwarf::DW_OP_LLVM_arg:
case dwarf::DW_OP_LLVM_tag_offset:
case dwarf::DW_OP_LLVM_extract_bits_sext:
case dwarf::DW_OP_LLVM_extract_bits_zext:
case dwarf::DW_OP_constu:
case dwarf::DW_OP_plus_uconst:
case dwarf::DW_OP_plus:
Expand Down
92 changes: 92 additions & 0 deletions llvm/test/DebugInfo/X86/DW_OP_LLVM_extract_bits.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
; 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_deref, DW_OP_constu 0x3d, DW_OP_shl, DW_OP_constu 0x3d, DW_OP_shr, DW_OP_stack_value)
; CHECK: DW_AT_name ("x")
; CHECK: DW_TAG_variable
; 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)
; 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 !16, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 3)), !dbg !17
tail call void @llvm.dbg.declare(metadata ptr %0, metadata !18, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 3, 4)), !dbg !17
ret i32 0, !dbg !17
}

; CHECK-LABEL: DW_TAG_subprogram
; CHECK: DW_AT_name ("test2")
; CHECK: DW_TAG_variable
; 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)
; CHECK: DW_AT_name ("x")
; CHECK: DW_TAG_variable
; 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)
; CHECK: DW_AT_name ("y")

define i8 @test2() !dbg !20 {
entry:
%0 = load i8, ptr @g, align 1
tail call void @llvm.dbg.value(metadata i8 %0, metadata !21, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 3)), !dbg !22
tail call void @llvm.dbg.value(metadata i8 %0, metadata !23, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 3, 4)), !dbg !22
ret i8 %0, !dbg !22
}

; CHECK-LABEL: DW_TAG_subprogram
; CHECK: DW_AT_name ("test3")
; CHECK: DW_TAG_variable
; CHECK: DW_AT_location (DW_OP_breg0 {{R[^+]+}}+0, DW_OP_constu 0x3f, DW_OP_shr, DW_OP_stack_value)
; CHECK: DW_AT_name ("x")
; CHECK: DW_TAG_variable
; CHECK: DW_AT_location (DW_OP_breg0 {{R[^+]+}}+0, DW_OP_constu 0x3f, DW_OP_shra, DW_OP_stack_value)
; CHECK: DW_AT_name ("y")

define i64 @test3(ptr %p) !dbg !24 {
entry:
%0 = load i64, ptr %p, align 8
tail call void @llvm.dbg.value(metadata i64 %0, metadata !25, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 63, 1)), !dbg !26
tail call void @llvm.dbg.value(metadata i64 %0, metadata !27, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 63, 1)), !dbg !26
ret i64 %0, !dbg !26
}

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, 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, baseType: !6)
!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !3, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "_ZTS8struct_t")
!7 = !{!8, !10}
!8 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !6, file: !3, 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, 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, type: !14, spFlags: DISPFlagDefinition, unit: !2)
!14 = !DISubroutineType(types: !15)
!15 = !{!9}
!16 = !DILocalVariable(name: "x", scope: !13, file: !3, type: !9)
!17 = !DILocation(line: 0, scope: !13)
!18 = !DILocalVariable(name: "y", scope: !13, file: !3, type: !19)
!19 = !DIBasicType(name: "signed int", size: 32, encoding: DW_ATE_signed)
!20 = distinct !DISubprogram(name: "test2", linkageName: "test2", scope: !3, file: !3, type: !14, spFlags: DISPFlagDefinition, unit: !2)
!21 = !DILocalVariable(name: "x", scope: !20, file: !3, type: !9)
!22 = !DILocation(line: 0, scope: !20)
!23 = !DILocalVariable(name: "y", scope: !20, file: !3, type: !19)
!24 = distinct !DISubprogram(name: "test3", linkageName: "test3", scope: !3, file: !3, type: !14, spFlags: DISPFlagDefinition, unit: !2)
!25 = !DILocalVariable(name: "x", scope: !24, file: !3, type: !9)
!26 = !DILocation(line: 0, scope: !24)
!27 = !DILocalVariable(name: "y", scope: !24, file: !3, type: !19)
Loading