Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -4967,6 +4967,10 @@ (``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_convert_generic`` converts the top of the expression stack to + what's referred to in DWARF as the generic type; that is, with the size of an + address on the target machine and with unspecified signedness. Maps into a + ``DW_OP_convert`` operation with operand 0. - ``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. Index: llvm/include/llvm/BinaryFormat/Dwarf.h =================================================================== --- llvm/include/llvm/BinaryFormat/Dwarf.h +++ llvm/include/llvm/BinaryFormat/Dwarf.h @@ -118,10 +118,11 @@ #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_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_convert_generic = 0x1004, ///< Only used in LLVM metadata. }; enum TypeKind : uint8_t { Index: llvm/lib/BinaryFormat/Dwarf.cpp =================================================================== --- llvm/lib/BinaryFormat/Dwarf.cpp +++ llvm/lib/BinaryFormat/Dwarf.cpp @@ -151,6 +151,8 @@ return "DW_OP_LLVM_tag_offset"; case DW_OP_LLVM_entry_value: return "DW_OP_LLVM_entry_value"; + case DW_OP_LLVM_convert_generic: + return "DW_OP_LLVM_convert_generic"; } } @@ -163,6 +165,7 @@ .Case("DW_OP_LLVM_fragment", DW_OP_LLVM_fragment) .Case("DW_OP_LLVM_tag_offset", DW_OP_LLVM_tag_offset) .Case("DW_OP_LLVM_entry_value", DW_OP_LLVM_entry_value) + .Case("DW_OP_LLVM_convert_generic", DW_OP_LLVM_convert_generic) .Default(0); } Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -2280,7 +2280,9 @@ // DwarfExpression::addExpression) so for those ops (e.g. DW_OP_convert) that // need to reference a base_type DIE the offset of that DIE is not yet known. // To deal with this we instead insert a placeholder early and then extract - // it here and replace it with the real reference. + // it here and replace it with the real reference. The value 0 is reserved + // for DWARF operations that refer to the generic type with a zero-valued + // operand, so just emit that as is. unsigned PtrSize = Asm->MAI->getCodePointerSize(); DWARFDataExtractor Data(StringRef(DebugLocs.getBytes(Entry).data(), DebugLocs.getBytes(Entry).size()), @@ -2297,9 +2299,8 @@ for (unsigned I = 0; I < 2; ++I) { if (Op.getDescription().Op[I] == Encoding::SizeNA) continue; - if (Op.getDescription().Op[I] == Encoding::BaseTypeRef) { - assert(Op.getRawOperand(I) && - "BaseTypeRefs referring to the generic type are not supported"); + if (Op.getDescription().Op[I] == Encoding::BaseTypeRef && + Op.getRawOperand(I) > 0) { uint64_t Offset = CU->getBaseTypeDIE(Op.getRawOperand(I))->getOffset(); assert(Offset < (1ULL << (ULEB128PadSize * 7)) && "Offset wont fit"); Streamer.emitULEB128(Offset, "", ULEB128PadSize); Index: llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -476,6 +476,17 @@ } break; } + case dwarf::DW_OP_LLVM_convert_generic: + // In DWARF version 4 and earlier the stack elements are only of an + // unspecified type (what is referred to as the generic type in DWARF + // version 5), and earlier conversion operations on the stack have just + // been done using bit manipulation on that type, so nothing needs to be + // done here. + if (DwarfVersion >= 5) { + emitOp(dwarf::DW_OP_convert); + emitUnsigned(0); + } + break; case dwarf::DW_OP_stack_value: LocationKind = Implicit; break; Index: llvm/lib/IR/DebugInfoMetadata.cpp =================================================================== --- llvm/lib/IR/DebugInfoMetadata.cpp +++ llvm/lib/IR/DebugInfoMetadata.cpp @@ -908,6 +908,7 @@ getNumElements() == 2; } case dwarf::DW_OP_LLVM_convert: + case dwarf::DW_OP_LLVM_convert_generic: case dwarf::DW_OP_LLVM_tag_offset: case dwarf::DW_OP_constu: case dwarf::DW_OP_plus_uconst: Index: llvm/test/Assembler/diexpression.ll =================================================================== --- llvm/test/Assembler/diexpression.ll +++ llvm/test/Assembler/diexpression.ll @@ -10,9 +10,10 @@ ; CHECK-SAME: !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef), ; CHECK-SAME: !DIExpression(DW_OP_plus_uconst, 3) ; CHECK-SAME: !DIExpression(DW_OP_LLVM_convert, 16, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_signed) -; CHECK-SAME: !DIExpression(DW_OP_LLVM_tag_offset, 1)} +; CHECK-SAME: !DIExpression(DW_OP_LLVM_tag_offset, 1) +; CHECK-SAME: !DIExpression(DW_OP_LLVM_convert_generic)} -!named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8} +!named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9} !0 = !DIExpression() !1 = !DIExpression(DW_OP_deref) @@ -23,3 +24,4 @@ !6 = !DIExpression(DW_OP_plus_uconst, 3) !7 = !DIExpression(DW_OP_LLVM_convert, 16, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_signed) !8 = !DIExpression(DW_OP_LLVM_tag_offset, 1) +!9 = !DIExpression(DW_OP_LLVM_convert_generic) Index: llvm/test/DebugInfo/X86/convert-generic.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/convert-generic.ll @@ -0,0 +1,82 @@ +; RUN: %llc_dwarf -dwarf-version=5 -filetype=obj -O0 < %s | llvm-dwarfdump - \ +; RUN: | FileCheck %s --check-prefix=DW5 +; RUN: %llc_dwarf -dwarf-version=4 -filetype=obj -O0 < %s | llvm-dwarfdump - \ +; RUN: | FileCheck %s --check-prefix=DW4 + +; Verify that for DWARFv5, the DW_OP_LLVM_convert_generic operation is +; converted to a DW_OP_convert with the generic type specified. For earlier +; DWARF versions nothing should be emitted for that operation. + +; DW5: DW_AT_location (DW_OP_breg5 RDI+0, DW_OP_constu 0xffff, DW_OP_and, DW_OP_convert ({{.*}}) "DW_ATE_signed_16", DW_OP_convert ({{.*}}) "DW_ATE_signed_32", DW_OP_convert 0x0, DW_OP_constu 0x7b, DW_OP_minus, DW_OP_stack_value) +; DW5-NEXT: DW_AT_name ("local1") + +; DW5: DW_AT_location +; DW5-NEXT: DW_OP_breg5 RDI+0, DW_OP_constu 0xffff, DW_OP_and, DW_OP_convert (0x00000027) "DW_ATE_signed_16", DW_OP_convert (0x0000002b) "DW_ATE_signed_32", DW_OP_convert 0x0, DW_OP_constu 0x7b, DW_OP_minus, DW_OP_stack_value +; DW5-NEXT: DW_OP_consts +123, DW_OP_stack_value) +; DW5-NEXT: DW_AT_name ("local2") + +; DW4: DW_AT_location (DW_OP_breg5 RDI+0, DW_OP_constu 0xffff, DW_OP_and, DW_OP_dup, DW_OP_constu 0xf, DW_OP_shr, DW_OP_lit0, DW_OP_not, DW_OP_mul, DW_OP_constu 0x10, DW_OP_shl, DW_OP_or, DW_OP_constu 0x7b, DW_OP_minus, DW_OP_stack_value) +; DW4-NEXT: DW_AT_name ("local1") + +; DW4: DW_AT_location +; DW4-NEXT: DW_OP_breg5 RDI+0, DW_OP_constu 0xffff, DW_OP_and, DW_OP_dup, DW_OP_constu 0xf, DW_OP_shr, DW_OP_lit0, DW_OP_not, DW_OP_mul, DW_OP_constu 0x10, DW_OP_shl, DW_OP_or, DW_OP_constu 0x7b, DW_OP_minus, DW_OP_stack_value +; DW4-NEXT: DW_OP_consts +123, DW_OP_stack_value) +; DW4-NEXT: DW_AT_name ("local2") + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@global = external dso_local local_unnamed_addr global i16, align 2 + +; Function Attrs: norecurse nounwind readnone uwtable +define dso_local signext i16 @fn1(i16 returned signext %param1) local_unnamed_addr #0 !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i16 %param1, metadata !13, metadata !DIExpression(DW_OP_LLVM_convert, 16, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_LLVM_convert_generic, DW_OP_constu, 123, DW_OP_minus, DW_OP_stack_value)), !dbg !15 + ret i16 %param1, !dbg !16 +} + +; Function Attrs: nofree norecurse nounwind uwtable writeonly +define dso_local void @fn2(i16 signext %param2) local_unnamed_addr !dbg !17 { +entry: + call void @llvm.dbg.value(metadata i16 %param2, metadata !22, metadata !DIExpression(DW_OP_LLVM_convert, 16, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_LLVM_convert_generic, DW_OP_constu, 123, DW_OP_minus, DW_OP_stack_value)), !dbg !23 + store i16 %param2, i16* @global, align 2, !dbg !24 + call void @llvm.dbg.value(metadata i32 123, metadata !22, metadata !DIExpression()), !dbg !23 + ret void, !dbg !25 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { norecurse nounwind readnone uwtable } +attributes #1 = { nounwind readnone speculatable willreturn } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "ext.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 11.0.0 "} +!7 = distinct !DISubprogram(name: "fn1", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10, !10} +!10 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed) +!11 = !{!12, !13} +!12 = !DILocalVariable(name: "param1", arg: 1, scope: !7, file: !1, line: 3, type: !10) +!13 = !DILocalVariable(name: "local1", scope: !7, file: !1, line: 4, type: !14) +!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!15 = !DILocation(line: 0, scope: !7) +!16 = !DILocation(line: 5, scope: !7) +!17 = distinct !DISubprogram(name: "fn2", scope: !1, file: !1, line: 8, type: !18, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !20) +!18 = !DISubroutineType(types: !19) +!19 = !{null, !10} +!20 = !{!21, !22} +!21 = !DILocalVariable(name: "param2", arg: 1, scope: !17, file: !1, line: 8, type: !10) +!22 = !DILocalVariable(name: "local2", scope: !17, file: !1, line: 9, type: !14) +!23 = !DILocation(line: 0, scope: !17) +!24 = !DILocation(line: 10, scope: !17) +!25 = !DILocation(line: 12, scope: !17)