diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -566,6 +566,12 @@ case dwarf::DW_OP_dup: case dwarf::DW_OP_push_object_address: case dwarf::DW_OP_over: + case dwarf::DW_OP_eq: + case dwarf::DW_OP_ne: + case dwarf::DW_OP_gt: + case dwarf::DW_OP_ge: + case dwarf::DW_OP_lt: + case dwarf::DW_OP_le: emitOp(OpNum); break; case dwarf::DW_OP_deref: diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -1461,6 +1461,12 @@ case dwarf::DW_OP_push_object_address: case dwarf::DW_OP_over: case dwarf::DW_OP_consts: + case dwarf::DW_OP_eq: + case dwarf::DW_OP_ne: + case dwarf::DW_OP_gt: + case dwarf::DW_OP_ge: + case dwarf::DW_OP_lt: + case dwarf::DW_OP_le: break; } } diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -1980,6 +1980,18 @@ } } +static void handleSSAValueOperands(uint64_t CurrentLocOps, + SmallVectorImpl &Opcodes, + SmallVectorImpl &AdditionalValues, + Instruction *I) { + if (!CurrentLocOps) { + Opcodes.append({dwarf::DW_OP_LLVM_arg, 0}); + CurrentLocOps = 1; + } + Opcodes.append({dwarf::DW_OP_LLVM_arg, CurrentLocOps}); + AdditionalValues.push_back(I->getOperand(1)); +} + Value *getSalvageOpsForBinOp(BinaryOperator *BI, uint64_t CurrentLocOps, SmallVectorImpl &Opcodes, SmallVectorImpl &AdditionalValues) { @@ -2002,12 +2014,7 @@ } Opcodes.append({dwarf::DW_OP_constu, Val}); } else { - if (!CurrentLocOps) { - Opcodes.append({dwarf::DW_OP_LLVM_arg, 0}); - CurrentLocOps = 1; - } - Opcodes.append({dwarf::DW_OP_LLVM_arg, CurrentLocOps}); - AdditionalValues.push_back(BI->getOperand(1)); + handleSSAValueOperands(CurrentLocOps, Opcodes, AdditionalValues, BI); } // Add salvaged binary operator to expression stack, if it has a valid @@ -2019,6 +2026,60 @@ return BI->getOperand(0); } +uint64_t getDwarfOpForIcmpPred(CmpInst::Predicate Pred) { + // The signedness of the operation is implicit in the typed stack, signed and + // unsigned instructions map to the same DWARF opcode. + switch (Pred) { + case CmpInst::ICMP_EQ: + return dwarf::DW_OP_eq; + case CmpInst::ICMP_NE: + return dwarf::DW_OP_ne; + case CmpInst::ICMP_UGT: + case CmpInst::ICMP_SGT: + return dwarf::DW_OP_gt; + case CmpInst::ICMP_UGE: + case CmpInst::ICMP_SGE: + return dwarf::DW_OP_ge; + case CmpInst::ICMP_ULT: + case CmpInst::ICMP_SLT: + return dwarf::DW_OP_lt; + case CmpInst::ICMP_ULE: + case CmpInst::ICMP_SLE: + return dwarf::DW_OP_le; + default: + return 0; + } +} + +Value *getSalvageOpsForIcmpOp(ICmpInst *Icmp, uint64_t CurrentLocOps, + SmallVectorImpl &Opcodes, + SmallVectorImpl &AdditionalValues) { + // Handle icmp operations with constant integer operands as a special case. + auto *ConstInt = dyn_cast(Icmp->getOperand(1)); + // Values wider than 64 bits cannot be represented within a DIExpression. + if (ConstInt && ConstInt->getBitWidth() > 64) + return nullptr; + // Push any Constant Int operand onto the expression stack. + if (ConstInt) { + if (Icmp->isSigned()) + Opcodes.push_back(dwarf::DW_OP_consts); + else + Opcodes.push_back(dwarf::DW_OP_constu); + uint64_t Val = ConstInt->getSExtValue(); + Opcodes.push_back(Val); + } else { + handleSSAValueOperands(CurrentLocOps, Opcodes, AdditionalValues, Icmp); + } + + // Add salvaged binary operator to expression stack, if it has a valid + // representation in a DIExpression. + uint64_t DwarfIcmpOp = getDwarfOpForIcmpPred(Icmp->getPredicate()); + if (!DwarfIcmpOp) + return nullptr; + Opcodes.push_back(DwarfIcmpOp); + return Icmp->getOperand(0); +} + Value *llvm::salvageDebugInfoImpl(Instruction &I, uint64_t CurrentLocOps, SmallVectorImpl &Ops, SmallVectorImpl &AdditionalValues) { @@ -2058,6 +2119,8 @@ return getSalvageOpsForGEP(GEP, DL, CurrentLocOps, Ops, AdditionalValues); if (auto *BI = dyn_cast(&I)) return getSalvageOpsForBinOp(BI, CurrentLocOps, Ops, AdditionalValues); + if (auto *IC = dyn_cast(&I)) + return getSalvageOpsForIcmpOp(IC, CurrentLocOps, Ops, AdditionalValues); // *Not* to do: we should not attempt to salvage load instructions, // because the validity and lifetime of a dbg.value containing diff --git a/llvm/test/DebugInfo/X86/dbg_value_list_emission.mir b/llvm/test/DebugInfo/X86/dbg_value_list_emission.mir --- a/llvm/test/DebugInfo/X86/dbg_value_list_emission.mir +++ b/llvm/test/DebugInfo/X86/dbg_value_list_emission.mir @@ -40,6 +40,7 @@ !28 = !DILocalVariable(name: "localf", scope: !7, file: !1, line: 6, type: !10) !29 = !DILocalVariable(name: "localg", scope: !7, file: !1, line: 6, type: !10) !30 = !DILocalVariable(name: "localh", scope: !7, file: !1, line: 6, type: !10) + !31 = !DILocalVariable(name: "locali", scope: !7, file: !1, line: 6, type: !10) ... --- @@ -95,5 +96,12 @@ DBG_VALUE_LIST !30, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $eax, $noreg, debug-location !15 ; CHECK-NOT: DW_AT_name ("localh") + ; (9) Check that relational operators work + DBG_VALUE_LIST !31, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_eq, DW_OP_LLVM_arg, 0, DW_OP_ne, DW_OP_LLVM_arg, 1, DW_OP_gt, DW_OP_LLVM_arg, 0, DW_OP_lt, DW_OP_LLVM_arg, 1, DW_OP_le, DW_OP_stack_value), $eax, $edi, debug-location !15 + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: (DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_breg5 RDI+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_eq, DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_ne, DW_OP_breg5 RDI+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_gt, DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_lt, DW_OP_breg5 RDI+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_le, DW_OP_stack_value) + ; CHECK-NEXT: DW_AT_name ("locali") + + RET64 debug-location !15 ... diff --git a/llvm/test/DebugInfo/X86/debug_value_list_selectiondag.ll b/llvm/test/DebugInfo/X86/debug_value_list_selectiondag.ll --- a/llvm/test/DebugInfo/X86/debug_value_list_selectiondag.ll +++ b/llvm/test/DebugInfo/X86/debug_value_list_selectiondag.ll @@ -13,16 +13,20 @@ ; CHECK-DAG: [[A_VAR:![0-9]+]] = !DILocalVariable(name: "a" ; CHECK-DAG: [[B_VAR:![0-9]+]] = !DILocalVariable(name: "b" ; CHECK-DAG: [[C_VAR:![0-9]+]] = !DILocalVariable(name: "c" +; CHECK-DAG: [[D_VAR:![0-9]+]] = !DILocalVariable(name: "d" ; CHECK-LABEL: bb.{{(0|1)}}.entry ; DAG-DAG: DBG_VALUE_LIST [[A_VAR]], !DIExpression(DW_OP_LLVM_arg, 0), %0, debug-location ; DAG-DAG: DBG_VALUE_LIST [[B_VAR]], !DIExpression(DW_OP_LLVM_arg, 0), %1, debug-location ; DAG: DBG_VALUE_LIST [[C_VAR]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), %0, %1, debug-location +; DAG: DBG_VALUE_LIST [[D_VAR]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_eq, DW_OP_LLVM_arg, 0, DW_OP_ne, DW_OP_LLVM_arg, 1, DW_OP_gt, DW_OP_LLVM_arg, 0, DW_OP_lt, DW_OP_LLVM_arg, 1, DW_OP_le), %0, %1, debug-location ; FAST-DAG: DBG_VALUE $noreg, $noreg, [[A_VAR]], !DIExpression(DW_OP_LLVM_arg, 0), debug-location ; FAST-DAG: DBG_VALUE $noreg, $noreg, [[B_VAR]], !DIExpression(DW_OP_LLVM_arg, 0), debug-location ; FAST: DBG_VALUE $noreg, $noreg, [[C_VAR]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), debug-location +; FAST: DBG_VALUE $noreg, $noreg, [[D_VAR]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_eq, DW_OP_LLVM_arg, 0, DW_OP_ne, DW_OP_LLVM_arg, 1, DW_OP_gt, DW_OP_LLVM_arg, 0, DW_OP_lt, DW_OP_LLVM_arg, 1, DW_OP_le), debug-location ; GLOBAL-DAG: DBG_VALUE $noreg, 0, [[A_VAR]], !DIExpression(DW_OP_LLVM_arg, 0), debug-location ; GLOBAL-DAG: DBG_VALUE $noreg, 0, [[B_VAR]], !DIExpression(DW_OP_LLVM_arg, 0), debug-location ; GLOBAL: DBG_VALUE $noreg, 0, [[C_VAR]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), debug-location +; GLOBAL: DBG_VALUE $noreg, 0, [[D_VAR]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_eq, DW_OP_LLVM_arg, 0, DW_OP_ne, DW_OP_LLVM_arg, 1, DW_OP_gt, DW_OP_LLVM_arg, 0, DW_OP_lt, DW_OP_LLVM_arg, 1, DW_OP_le), debug-location target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc19.16.27034" @@ -32,6 +36,7 @@ call void @llvm.dbg.value(metadata !DIArgList(i32 %b), metadata !14, metadata !DIExpression(DW_OP_LLVM_arg, 0)), !dbg !17 call void @llvm.dbg.value(metadata !DIArgList(i32 %a), metadata !15, metadata !DIExpression(DW_OP_LLVM_arg, 0)), !dbg !17 call void @llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b), metadata !16, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus)), !dbg !17 + call void @llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b), metadata !20, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_eq, DW_OP_LLVM_arg, 0, DW_OP_ne, DW_OP_LLVM_arg, 1, DW_OP_gt, DW_OP_LLVM_arg, 0, DW_OP_lt, DW_OP_LLVM_arg, 1, DW_OP_le)), !dbg !17 %mul = mul nsw i32 %b, %a, !dbg !18 ret i32 %mul, !dbg !18 } @@ -64,3 +69,4 @@ !17 = !DILocation(line: 0, scope: !8) !18 = !DILocation(line: 3, scope: !8) !19 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!20 = !DILocalVariable(name: "d", scope: !8, file: !9, line: 2, type: !12) diff --git a/llvm/test/DebugInfo/salvage-icmp.ll b/llvm/test/DebugInfo/salvage-icmp.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/salvage-icmp.ll @@ -0,0 +1,85 @@ +; RUN: opt %s -passes=dce -S | FileCheck %s + +; Tests the results of salvaging variadic dbg.values that use the same SSA value +; multiple times. + +; CHECK: call void @llvm.dbg.value(metadata i32 %a, +; CHECK-SAME: ![[VAR_C:[0-9]+]], +; CHECK-SAME: !DIExpression(DW_OP_constu, 0, DW_OP_ne, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_constu, 0, DW_OP_eq, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_constu, 1, DW_OP_gt, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_consts, 18446744073709551615, DW_OP_gt, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_constu, 2, DW_OP_ge, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_consts, 18446744073709551614, DW_OP_ge, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_constu, 3, DW_OP_lt, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_consts, 18446744073709551613, DW_OP_lt, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_constu, 4, DW_OP_le, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_consts, 18446744073709551612, DW_OP_le, DW_OP_stack_value)) + +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %a, i32 %a, i32 %b, i32 %a, i32 %b, i32 %b, i32 %a, i32 %a, i32 %b, i32 %b), +; CHECK-SAME: ![[VAR_C:[0-9]+]], +; CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 10, DW_OP_ne, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_arg, 9, DW_OP_eq, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_arg, 8, DW_OP_gt, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_arg, 7, DW_OP_gt, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_arg, 6, DW_OP_ge, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_arg, 5, DW_OP_ge, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_arg, 4, DW_OP_lt, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_arg, 3, DW_OP_lt, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_arg, 2, DW_OP_le, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_arg, 1, DW_OP_le, DW_OP_stack_value)) + +; CHECK: ![[VAR_C]] = !DILocalVariable(name: "c" + +define i32 @"?multiply@@YAHHH@Z"(i32 %a, i32 %b) !dbg !8 { +entry: + %icmp1 = icmp ne i32 %a, 0, !dbg !15 + %icmp1.1 = zext i1 %icmp1 to i32 + %icmp2 = icmp eq i32 %icmp1.1, 0, !dbg !15 + %icmp2.1 = zext i1 %icmp2 to i32 + %icmp3 = icmp ugt i32 %icmp2.1, 1, !dbg !15 + %icmp3.1 = zext i1 %icmp3 to i32 + %icmp4 = icmp sgt i32 %icmp3.1, -1, !dbg !15 + %icmp4.1 = zext i1 %icmp4 to i32 + %icmp5 = icmp uge i32 %icmp4.1, 2, !dbg !15 + %icmp5.1 = zext i1 %icmp5 to i32 + %icmp6 = icmp sge i32 %icmp5.1, -2, !dbg !15 + %icmp6.1 = zext i1 %icmp6 to i32 + %icmp7 = icmp ult i32 %icmp6.1, 3, !dbg !15 + %icmp7.1 = zext i1 %icmp7 to i32 + %icmp8 = icmp slt i32 %icmp7.1, -3, !dbg !15 + %icmp8.1 = zext i1 %icmp8 to i32 + %icmp9 = icmp ule i32 %icmp8.1, 4, !dbg !15 + %icmp9.1 = zext i1 %icmp9 to i32 + %icmp10 = icmp sle i32 %icmp9.1, -4, !dbg !15 + call void @llvm.dbg.value(metadata i1 %icmp10, metadata !16, metadata !DIExpression()), !dbg !13 + %icmp11 = icmp ne i32 %a, %b, !dbg !15 + %icmp11.1 = zext i1 %icmp11 to i32 + %icmp12 = icmp eq i32 %icmp11.1, %b, !dbg !15 + %icmp12.1 = zext i1 %icmp12 to i32 + %icmp13 = icmp ugt i32 %icmp12.1, %a, !dbg !15 + %icmp13.1 = zext i1 %icmp13 to i32 + %icmp14 = icmp sgt i32 %icmp13.1, %a, !dbg !15 + %icmp14.1 = zext i1 %icmp14 to i32 + %icmp15 = icmp uge i32 %icmp14.1, %b, !dbg !15 + %icmp15.1 = zext i1 %icmp15 to i32 + %icmp16 = icmp sge i32 %icmp15.1, %b, !dbg !15 + %icmp16.1 = zext i1 %icmp16 to i32 + %icmp17 = icmp ult i32 %icmp16.1, %a, !dbg !15 + %icmp17.1 = zext i1 %icmp17 to i32 + %icmp18 = icmp slt i32 %icmp17.1, %b, !dbg !15 + %icmp18.1 = zext i1 %icmp18 to i32 + %icmp19 = icmp ule i32 %icmp18.1, %a, !dbg !15 + %icmp19.1 = zext i1 %icmp19 to i32 + %icmp20 = icmp sle i32 %icmp19.1, %a, !dbg !15 + call void @llvm.dbg.value(metadata i1 %icmp20, metadata !16, metadata !DIExpression()), !dbg !13 + %mul = mul nsw i32 %a, %b, !dbg !17 + ret i32 %mul, !dbg !17 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "multiply", linkageName: "?multiply@@YAHHH@Z", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!9 = !DISubroutineType(types: !10) +!10 = !{!11, !11, !11} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !1, line: 1, type: !11) +!13 = !DILocation(line: 0, scope: !8) +!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 1, type: !11) +!15 = !DILocation(line: 2, scope: !8) +!16 = !DILocalVariable(name: "c", scope: !8, file: !1, line: 2, type: !11) +!17 = !DILocation(line: 3, scope: !8)