Index: llvm/include/llvm/Analysis/ScalarEvolution.h =================================================================== --- llvm/include/llvm/Analysis/ScalarEvolution.h +++ llvm/include/llvm/Analysis/ScalarEvolution.h @@ -540,6 +540,9 @@ /// Notify this ScalarEvolution that \p User directly uses SCEVs in \p Ops. void registerUser(const SCEV *User, ArrayRef Ops); + /// Return true if the SCEV expression contains an undef value. + bool containsUndefs(const SCEV *S) const; + /// Return a SCEV expression for the full generality of the specified /// expression. const SCEV *getSCEV(Value *V); Index: llvm/lib/Analysis/ScalarEvolution.cpp =================================================================== --- llvm/lib/Analysis/ScalarEvolution.cpp +++ llvm/lib/Analysis/ScalarEvolution.cpp @@ -12219,7 +12219,7 @@ } // Return true when S contains at least an undef value. -static inline bool containsUndefs(const SCEV *S) { +bool ScalarEvolution::containsUndefs(const SCEV *S) const { return SCEVExprContains(S, [](const SCEV *S) { if (const auto *SU = dyn_cast(S)) return isa(SU->getValue()); Index: llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp =================================================================== --- llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp +++ llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp @@ -6234,7 +6234,8 @@ } /// Identify and cache salvageable DVI locations and expressions along with the -/// corresponding SCEV(s). Also ensure that the DVI is not deleted before +/// corresponding SCEV(s). Also ensure that the DVI is not deleted between +/// cacheing and salvaging. static void DbgGatherSalvagableDVI(Loop *L, ScalarEvolution &SE, SmallVector &SalvageableDVISCEVs, @@ -6255,6 +6256,16 @@ !SE.isSCEVable(DVI->getVariableLocationOp(0)->getType())) continue; + // SCEVUnknown wraps an llvm::Value, it does not have a start and stride. + // Therefore no translation to DIExpression is performed. + const SCEV *S = SE.getSCEV(DVI->getVariableLocationOp(0)); + if (isa(S)) + continue; + + // Avoid wasting resources generating an expression containing undef. + if (SE.containsUndefs(S)) + continue; + SalvageableDVISCEVs.push_back( {DVI, DVI->getExpression(), DVI->getRawLocation(), SE.getSCEV(DVI->getVariableLocationOp(0))}); @@ -6268,33 +6279,34 @@ /// surviving subsequent transforms. static llvm::PHINode *GetInductionVariable(const Loop &L, ScalarEvolution &SE, const LSRInstance &LSR) { - // For now, just pick the first IV generated and inserted. Ideally pick an IV - // that is unlikely to be optimised away by subsequent transforms. + + auto IsSuitableIV = [&](PHINode *P) { + if (!SE.isSCEVable(P->getType())) + return false; + if (const SCEVAddRecExpr *Rec = dyn_cast(SE.getSCEV(P))) + return Rec->isAffine() && !SE.containsUndefs(SE.getSCEV(P)); + return false; + }; + + // For now, just pick the first IV that was generated and inserted by + // ScalarEvolution. Ideally pick an IV that is unlikely to be optimised away + // by subsequent transforms. for (const WeakVH &IV : LSR.getScalarEvolutionIVs()) { if (!IV) continue; assert(isa(&*IV) && "Expected PhI node."); - if (SE.isSCEVable((*IV).getType())) { - PHINode *Phi = dyn_cast(&*IV); - LLVM_DEBUG(dbgs() << "scev-salvage: IV : " << *IV - << "with SCEV: " << *SE.getSCEV(Phi) << "\n"); - return Phi; - } - } - - for (PHINode &Phi : L.getHeader()->phis()) { - if (!SE.isSCEVable(Phi.getType())) + PHINode *P = dyn_cast(&*IV); + if (!P) continue; - const llvm::SCEV *PhiSCEV = SE.getSCEV(&Phi); - if (const llvm::SCEVAddRecExpr *Rec = dyn_cast(PhiSCEV)) - if (!Rec->isAffine()) - continue; + if (IsSuitableIV(P)) + return P; + } - LLVM_DEBUG(dbgs() << "scev-salvage: Selected IV from loop header: " << Phi - << " with SCEV: " << *PhiSCEV << "\n"); - return Φ + for (PHINode &P : L.getHeader()->phis()) { + if (IsSuitableIV(&P)) + return &P; } return nullptr; } Index: llvm/test/Transforms/LoopStrengthReduce/pr52161.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/LoopStrengthReduce/pr52161.ll @@ -0,0 +1,54 @@ +; RUN: opt -S -loop-reduce %s 2>&1 | FileCheck %s + +;; Ensure that scev-based salvaging in LSR does not select an IV containing +;; an 'undef' element. + +target triple = "x86_64-unknown-linux-gnu" + +define i16 @n() !dbg !8 { +entry: + br i1 undef, label %m, label %for.body + +for.body: ; preds = %for.body, %entry + %iv = phi i16 [ %ivdec, %for.body ], [ 14, %entry ] + %ivdec = sub i16 %iv, 1 + call void @llvm.dbg.value(metadata i16 %iv, metadata !21, metadata !DIExpression()), !dbg !19 + br label %for.body + +m: ; preds = %m, %entry + %0 = phi i16 [ 3, %m ], [ 6, %entry ] + %gg = add i16 %0, 23 + ; CHECK: call void @llvm.dbg.value(metadata i16 undef, metadata !{{[0-9]+}}, metadata !DIExpression()), + call void @llvm.dbg.value(metadata i16 %0, metadata !14, metadata !DIExpression()), !dbg !19 + br label %m +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +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_C99, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "reduced.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 1} +!6 = !{i32 7, !"frame-pointer", i32 2} +!7 = !{!"clang version 14.0.0"} +!8 = distinct !DISubprogram(name: "n", scope: !1, file: !1, line: 18, type: !9, scopeLine: 18, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12) +!9 = !DISubroutineType(types: !10) +!10 = !{!11} +!11 = !DIBasicType(name: "int", size: 16, encoding: DW_ATE_signed) +!12 = !{!13} +!13 = !DILabel(scope: !8, name: "m", file: !1, line: 22) +!14 = !DILocalVariable(name: "k", arg: 2, scope: !15, file: !1, line: 9, type: !11) +!15 = distinct !DISubprogram(name: "i", scope: !1, file: !1, line: 9, type: !16, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !18) +!16 = !DISubroutineType(types: !17) +!17 = !{!11, !11, !11} +!18 = !{!14} +!19 = !DILocation(line: 0, scope: !15, inlinedAt: !20) +!20 = distinct !DILocation(line: 23, scope: !8) +!21 = !DILocalVariable(name: "x", arg: 2, scope: !15, file: !1, line: 1, type: !11)