diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index c58f7f7140e47..d1edbb48b654c 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -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. diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.h b/llvm/include/llvm/BinaryFormat/Dwarf.h index 74c4d6ff3a716..607f3eb9d4c22 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.h +++ b/llvm/include/llvm/BinaryFormat/Dwarf.h @@ -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 { diff --git a/llvm/lib/BinaryFormat/Dwarf.cpp b/llvm/lib/BinaryFormat/Dwarf.cpp index 7324266172684..0bf4f201dbe10 100644 --- a/llvm/lib/BinaryFormat/Dwarf.cpp +++ b/llvm/lib/BinaryFormat/Dwarf.cpp @@ -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"; } } @@ -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); } diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp index a74d43897d45b..cc96d3c481f70 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -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 @@ -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); diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp index 9bd1d7880c9f8..2b45932093f0f 100644 --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -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: @@ -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: diff --git a/llvm/test/DebugInfo/X86/DW_OP_LLVM_extract_bits.ll b/llvm/test/DebugInfo/X86/DW_OP_LLVM_extract_bits.ll new file mode 100644 index 0000000000000..18fdfa579b9f1 --- /dev/null +++ b/llvm/test/DebugInfo/X86/DW_OP_LLVM_extract_bits.ll @@ -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)