diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp index 126867a6eac96..4482d0b841c49 100644 --- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -32,6 +32,7 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/VectorUtils.h" +#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/CodeGen/ByteProvider.h" #include "llvm/CodeGen/DAGCombine.h" #include "llvm/CodeGen/ISDOpcodes.h" @@ -51,6 +52,7 @@ #include "llvm/IR/Attributes.h" #include "llvm/IR/Constant.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/Metadata.h" @@ -78,6 +80,7 @@ #include #include "MatchContext.h" +#include "SDNodeDbgValue.h" using namespace llvm; using namespace llvm::SDPatternMatch; @@ -14455,15 +14458,31 @@ static SDValue tryToFoldExtOfLoad(SelectionDAG &DAG, DAGCombiner &Combiner, LN0->getBasePtr(), N0.getValueType(), LN0->getMemOperand()); Combiner.ExtendSetCCUses(SetCCs, N0, ExtLoad, ExtOpc); + // If the load value is used only by N, replace it via CombineTo N. - bool NoReplaceTrunc = SDValue(LN0, 0).hasOneUse(); + SDValue OldLoadVal(LN0, 0); + SDValue OldExtValue(N, 0); + bool NoReplaceTrunc = OldLoadVal.hasOneUse(); Combiner.CombineTo(N, ExtLoad); + if (NoReplaceTrunc) { + if (LN0->getHasDebugValue()) { + DAG.transferDbgValues(OldLoadVal, ExtLoad); + DAG.salvageDebugInfo(*ExtLoad.getNode()); + } + if (N->getHasDebugValue()) + DAG.transferDbgValues(OldExtValue, ExtLoad); DAG.ReplaceAllUsesOfValueWith(SDValue(LN0, 1), ExtLoad.getValue(1)); Combiner.recursivelyDeleteUnusedNodes(LN0); } else { SDValue Trunc = DAG.getNode(ISD::TRUNCATE, SDLoc(N0), N0.getValueType(), ExtLoad); + if (LN0->getHasDebugValue()) { + DAG.transferDbgValues(OldLoadVal, Trunc); + DAG.salvageDebugInfo(*Trunc.getNode()); + } + if (N->getHasDebugValue()) + DAG.transferDbgValues(OldExtValue, Trunc); Combiner.CombineTo(LN0, Trunc, ExtLoad.getValue(1)); } return SDValue(N, 0); // Return N so it doesn't get rechecked! diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index b009e6a3d5f5f..8208f8ec172c4 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -12123,6 +12123,46 @@ void SelectionDAG::salvageDebugInfo(SDNode &N) { dbgs() << " into " << *DbgExpression << '\n'); break; } + case ISD::LOAD: { + SDValue N0 = N.getOperand(0); + if (const LoadSDNode *LD = dyn_cast(&N)) { + + // Currently, this is written to only work for S|Z EXT. + if (LD->getExtensionType() == ISD::NON_EXTLOAD || + LD->getExtensionType() == ISD::EXTLOAD) + break; + + // Because we are replacing a load and a s|zext with a load-s|zext + // instruction, the dbg_value attached to the load will be of a smaller + // bit width, and we have to add a DW_OP_LLVM_fragment to the + // DIExpression. + unsigned VarBitsTo = LD->getValueSizeInBits(0); + unsigned VarBitsFrom = LD->getMemoryVT().getSizeInBits(); + bool IsSigned = LD->getExtensionType() == ISD::SEXTLOAD; + + // Build a convert expression for the s|z extend. + const DIExpression *OldE = DV->getExpression(); + SmallVector Ops; + dwarf::TypeKind TK = + IsSigned ? dwarf::DW_ATE_signed : dwarf::DW_ATE_unsigned; + Ops.append({dwarf::DW_OP_LLVM_convert, VarBitsFrom, TK, + dwarf::DW_OP_LLVM_convert, VarBitsTo, TK}); + auto *NewE = DIExpression::get(OldE->getContext(), Ops); + + // Create a new SDDbgValue that points at the widened node with the + // fragment. + SDDbgValue *NewDV = + getDbgValue(DV->getVariable(), NewE, &N, N0.getResNo(), + DV->isIndirect(), DV->getDebugLoc(), DV->getOrder()); + ClonedDVs.push_back(NewDV); + DV->setIsInvalidated(); + DV->setIsEmitted(); + LLVM_DEBUG(dbgs() << "SALVAGE: Rewriting"; + N0.getNode()->dumprFull(this); + dbgs() << " into " << *NewE << '\n'); + break; + } + } } } diff --git a/llvm/test/DebugInfo/X86/selectionDAG-load-sext.ll b/llvm/test/DebugInfo/X86/selectionDAG-load-sext.ll new file mode 100644 index 0000000000000..d01d8ea8caf7a --- /dev/null +++ b/llvm/test/DebugInfo/X86/selectionDAG-load-sext.ll @@ -0,0 +1,53 @@ +; This test checks that after SelectionDAG runs, it preserves the debug info that is lost due to the DAGCombiner combining a load and a sext instruction, where the #dbg_value is pointing to the result of the load. + +; RUN: llc %s -mtriple=x86_64-unkown-linux -start-before=x86-isel -stop-after=x86-isel -o - | FileCheck %s --check-prefix=MIR +; RUN: llc -O2 %s -start-before=x86-isel -mtriple=x86_64-unkown-linux --filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o --name Idx | FileCheck %s --check-prefix=DUMP + +; MIR: ![[IDX:[0-9]+]] = !DILocalVariable(name: "Idx" +; MIR-LABEL: bb.0 +; MIR: %{{[0-9a-f]+}}{{.*}} = MOVSX64rm32 ${{.*}}, 1, $noreg, @GlobArr, $noreg, debug-instr-number [[INSTR_NUM:[0-9]+]] +; MIR-NEXT: DBG_INSTR_REF ![[IDX]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_LLVM_convert, 64, DW_ATE_signed), dbg-instr-ref([[INSTR_NUM]], 0) + +; DUMP: DW_AT_location (indexed ({{[0-9a-f]+}}x{{[0-9a-f]+}}) loclist = 0x{{[0-9]+}}: +; DUMP-NEXT: [0x{{[0-9a-f]+}}, 0x{{[0-9a-f]+}}): DW_OP_breg0 RAX+0, DW_OP_convert (0x{{[0-9a-f]+}}) "DW_ATE_signed_32", DW_OP_convert (0x{{[0-9a-f]+}}) "DW_ATE_signed_64") + + @GlobArr = dso_local local_unnamed_addr global [5 x i32] [i32 1, i32 1, i32 2, i32 3, i32 5], align 16, !dbg !0 + @__const.main.Data = private unnamed_addr constant [7 x i32] [i32 10, i32 20, i32 30, i32 40, i32 50, i32 60, i32 70], align 16 + define dso_local void @_Z8useValuei(i32 noundef %0) local_unnamed_addr #0 !dbg !22 { + ret void, !dbg !28 + } + define dso_local noundef i32 @main() local_unnamed_addr #1 !dbg !29 { + %1 = load i32, ptr @GlobArr + #dbg_value(i32 %1, !43, !DIExpression(), !52) + %2 = sext i32 %1 to i64 + %3 = getelementptr inbounds i32, ptr @__const.main.Data, i64 %2 + %4 = load i32, ptr %3 + tail call void @_Z8useValuei(i32 noundef %4), !dbg !56 + ret i32 0 + } + !llvm.dbg.cu = !{!2} + !llvm.module.flags = !{!10, !11, !16} + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(type: !6, isDefinition: true) + !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, emissionKind: FullDebug, nameTableKind: None) + !3 = !DIFile(filename: "/tmp/test.cpp", directory: "/Users/srastogi/Development/llvm-project/build_ninja", checksumkind: CSK_MD5, checksum: "0fe735937e606b4db3e3b2e9253eff90") + !6 = !DICompositeType(tag: DW_TAG_array_type, elements: !8) + !7 = !DIBasicType() + !8 = !{} + !10 = !{i32 7, !"Dwarf Version", i32 5} + !11 = !{i32 2, !"Debug Info Version", i32 3} + !16 = !{i32 7, !"debug-info-assignment-tracking", i1 true} + !22 = distinct !DISubprogram(type: !23, unit: !2, keyInstructions: true) + !23 = !DISubroutineType(types: !24) + !24 = !{} + !28 = !DILocation(scope: !22, atomRank: 1) + !29 = distinct !DISubprogram(type: !30, unit: !2, keyInstructions: true) + !30 = !DISubroutineType(types: !31) + !31 = !{} + !38 = distinct !DILexicalBlock(scope: !29, line: 5, column: 3) + !43 = !DILocalVariable(name: "Idx", scope: !44, type: !7) + !44 = distinct !DILexicalBlock(scope: !38, line: 5, column: 3) + !46 = distinct !DILexicalBlock(scope: !44, line: 5, column: 27) + !52 = !DILocation(scope: !44) + !56 = !DILocation(scope: !46)