diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h --- a/llvm/include/llvm/Analysis/ScalarEvolution.h +++ b/llvm/include/llvm/Analysis/ScalarEvolution.h @@ -1776,8 +1776,10 @@ /// frugal here since we just bail out of actually constructing and /// canonicalizing an expression in the cases where the result isn't going /// to be a constant. +public: Optional computeConstantDifference(const SCEV *LHS, const SCEV *RHS); +private: /// Drop memoized information computed for S. void forgetMemoizedResults(const SCEV *S); diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp --- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp +++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp @@ -59,6 +59,7 @@ #include "llvm/ADT/Hashing.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetOperations.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallPtrSet.h" @@ -80,6 +81,7 @@ #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/GlobalValue.h" @@ -5775,6 +5777,24 @@ if (MSSA) MSSAU = std::make_unique(MSSA); + // Debug preservation - record all llvm.dbg.value from the loop as well as + // the SCEV of their variable location. Since salvageDebugInfo may change the + // DIExpression we need to store the original here as well (i.e. it needs to + // be in sync with the SCEV). + SmallVector, 32> + DbgValues; + for (auto &B : L->getBlocks()) { + for (auto &I : *B) { + if (DbgValueInst *D = dyn_cast(&I)) { + auto V = D->getVariableLocation(); + if (!SE.isSCEVable(V->getType())) + continue; + auto DS = SE.getSCEV(V); + DbgValues.push_back(std::make_tuple(D, DS, D->getExpression())); + } + } + } + // Run the main LSR transformation. Changed |= LSRInstance(L, IU, SE, DT, LI, TTI, AC, TLI, MSSAU.get()).getChanged(); @@ -5796,6 +5816,37 @@ DeleteDeadPHIs(L->getHeader(), &TLI, MSSAU.get()); } } + // Debug preservation - go through all recorded llvm.dbg.value and for those + // that now have an undef variable location use the recorded SCEV to try and + // update it. Compare with SCEV of Phi-nodes of loop header to find a + // suitable update candidate. SCEV match with constant offset is allowed and + // will be compensated for in the DIExpression. + if (Changed) { + for (auto &D : DbgValues) { + auto DbgValue = std::get(D); + auto DbgValueSCEV = std::get(D); + auto DbgDIExpr = std::get(D); + if (!isa(DbgValue->getVariableLocation())) + continue; + for (PHINode &Phi : L->getHeader()->phis()) { + if (!SE.isSCEVable(Phi.getType())) + continue; + auto PhiSCEV = SE.getSCEV(&Phi); + if (Optional Offset = + SE.computeConstantDifference(DbgValueSCEV, PhiSCEV)) { + auto &Ctx = DbgValue->getContext(); + DbgValue->setOperand( + 0, MetadataAsValue::get(Ctx, ValueAsMetadata::get(&Phi))); + if (Offset.getValue().getSExtValue()) { + SmallVector Ops; + DIExpression::appendOffset(Ops, Offset.getValue().getSExtValue()); + DbgDIExpr = DIExpression::prependOpcodes(DbgDIExpr, Ops, true); + } + DbgValue->setOperand(2, MetadataAsValue::get(Ctx, DbgDIExpr)); + } + } + } + } return Changed; } diff --git a/llvm/test/DebugInfo/COFF/fpo-shrink-wrap.ll b/llvm/test/DebugInfo/COFF/fpo-shrink-wrap.ll --- a/llvm/test/DebugInfo/COFF/fpo-shrink-wrap.ll +++ b/llvm/test/DebugInfo/COFF/fpo-shrink-wrap.ll @@ -33,7 +33,7 @@ ; ASM: popl %ebx ; ASM: [[EPILOGUE]]: # %return ; ASM: retl $8 -; ASM: Ltmp10: +; ASM: Ltmp11: ; ASM: .cv_fpo_endproc ; Note how RvaStart advances 7 bytes to skip the shrink-wrapped portion. diff --git a/llvm/test/Transforms/LoopStrengthReduce/dbg-preserve-0.ll b/llvm/test/Transforms/LoopStrengthReduce/dbg-preserve-0.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/LoopStrengthReduce/dbg-preserve-0.ll @@ -0,0 +1,75 @@ +; RUN: opt < %s -loop-reduce -S | FileCheck %s + +; Test that LSR preserves debug-info for induction variables. + +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" + +define dso_local void @foo(i8* nocapture %p) local_unnamed_addr !dbg !7 { +; CHECK-LABEL: @foo( +entry: + call void @llvm.dbg.value(metadata i8* %p, metadata !13, metadata !DIExpression()), !dbg !16 + call void @llvm.dbg.value(metadata i8 0, metadata !14, metadata !DIExpression()), !dbg !17 + br label %for.body, !dbg !18 + +for.cond.cleanup: ; preds = %for.body + ret void, !dbg !19 + +for.body: ; preds = %entry, %for.body +; CHECK-LABEL: for.body: + %i.06 = phi i8 [ 0, %entry ], [ %inc, %for.body ] + %p.addr.05 = phi i8* [ %p, %entry ], [ %add.ptr, %for.body ] + call void @llvm.dbg.value(metadata i8 %i.06, metadata !14, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i8* %p.addr.05, metadata !13, metadata !DIExpression()), !dbg !16 +; CHECK-NOT: call void @llvm.dbg.value(metadata i8* undef +; CHECK: call void @llvm.dbg.value(metadata i8* %lsr.iv, metadata ![[MID_p:[0-9]+]], metadata !DIExpression(DW_OP_constu, 3, DW_OP_minus, DW_OP_stack_value)), !dbg !16 + %add.ptr = getelementptr inbounds i8, i8* %p.addr.05, i64 3, !dbg !20 + call void @llvm.dbg.value(metadata i8* %add.ptr, metadata !13, metadata !DIExpression()), !dbg !16 +; CHECK-NOT: call void @llvm.dbg.value(metadata i8* undef +; CHECK: call void @llvm.dbg.value(metadata i8* %lsr.iv, metadata ![[MID_p]], metadata !DIExpression()), !dbg !16 + store i8 %i.06, i8* %add.ptr, align 1, !dbg !23, !tbaa !24 + %inc = add nuw nsw i8 %i.06, 1, !dbg !27 + call void @llvm.dbg.value(metadata i8 %inc, metadata !14, metadata !DIExpression()), !dbg !17 + %exitcond.not = icmp eq i8 %inc, 32, !dbg !28 + br i1 %exitcond.not, label %for.cond.cleanup, label %for.body, !dbg !18, !llvm.loop !29 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!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 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "lsrdbg.c", directory: "/repo/elavkje/llvm-project/llvm") +!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 12.0.0"} +!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12) +!8 = !DISubroutineType(types: !9) +!9 = !{null, !10} +!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64) +!11 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char) +!12 = !{!13, !14} +!13 = !DILocalVariable(name: "p", arg: 1, scope: !7, file: !1, line: 2, type: !10) +; CHECK: ![[MID_p]] = !DILocalVariable(name: "p", arg: 1, scope: !7, file: !1, line: 2, type: !10) +!14 = !DILocalVariable(name: "i", scope: !15, file: !1, line: 4, type: !11) +!15 = distinct !DILexicalBlock(scope: !7, file: !1, line: 4, column: 3) +!16 = !DILocation(line: 0, scope: !7) +!17 = !DILocation(line: 0, scope: !15) +!18 = !DILocation(line: 4, column: 3, scope: !15) +!19 = !DILocation(line: 8, column: 1, scope: !7) +!20 = !DILocation(line: 5, column: 7, scope: !21) +!21 = distinct !DILexicalBlock(scope: !22, file: !1, line: 4, column: 42) +!22 = distinct !DILexicalBlock(scope: !15, file: !1, line: 4, column: 3) +!23 = !DILocation(line: 6, column: 8, scope: !21) +!24 = !{!25, !25, i64 0} +!25 = !{!"omnipotent char", !26, i64 0} +!26 = !{!"Simple C/C++ TBAA"} +!27 = !DILocation(line: 4, column: 38, scope: !22) +!28 = !DILocation(line: 4, column: 31, scope: !22) +!29 = distinct !{!29, !18, !30, !31} +!30 = !DILocation(line: 7, column: 3, scope: !15) +!31 = !{!"llvm.loop.unroll.disable"}