diff --git a/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h b/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h index 8662dbf385d..59bf3a342ca 100644 --- a/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h +++ b/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h @@ -83,6 +83,9 @@ class SCEVExpander : public SCEVVisitor { /// InsertedValues/InsertedPostIncValues. SmallPtrSet ReusedValues; + // The induction variables generated. + SmallVector InsertedIVs; + /// A memoization of the "relevant" loop for a given SCEV. DenseMap RelevantLoops; @@ -199,9 +202,11 @@ public: InsertedPostIncValues.clear(); ReusedValues.clear(); ChainedPhis.clear(); + InsertedIVs.clear(); } ScalarEvolution *getSE() { return &SE; } + const SmallVectorImpl &getInsertedIVs() const { return InsertedIVs; } /// Return a vector containing all instructions inserted during expansion. SmallVector getAllInsertedInstructions() const { diff --git a/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/lib/Transforms/Scalar/LoopStrengthReduce.cpp index 54887460629..51ce2805ed3 100644 --- a/lib/Transforms/Scalar/LoopStrengthReduce.cpp +++ b/lib/Transforms/Scalar/LoopStrengthReduce.cpp @@ -1981,6 +1981,9 @@ class LSRInstance { /// IV users that belong to profitable IVChains. SmallPtrSet IVIncSet; + /// Induction variables that were generated and inserted by the SCEV Expander. + SmallVector ScalarEvolutionIVs; + void OptimizeShadowIV(); bool FindIVUserForCond(ICmpInst *Cond, IVStrideUse *&CondUse); ICmpInst *OptimizeMax(ICmpInst *Cond, IVStrideUse* &CondUse); @@ -2085,6 +2088,9 @@ public: TargetLibraryInfo &TLI, MemorySSAUpdater *MSSAU); bool getChanged() const { return Changed; } + const SmallVectorImpl &getScalarEvolutionIVs() const { + return ScalarEvolutionIVs; + } void print_factors_and_types(raw_ostream &OS) const; void print_fixups(raw_ostream &OS) const; @@ -5589,6 +5595,11 @@ void LSRInstance::ImplementSolution( GenerateIVChain(Chain, Rewriter, DeadInsts); Changed = true; } + + for (const WeakVH &IV : Rewriter.getInsertedIVs()) + if (IV && dyn_cast(&*IV)->getParent()) + ScalarEvolutionIVs.push_back(IV); + // Clean up after ourselves. This must be done before deleting any // instructions. Rewriter.clear(); @@ -5859,87 +5870,389 @@ void LoopStrengthReduce::getAnalysisUsage(AnalysisUsage &AU) const { AU.addPreserved(); } -using EqualValues = SmallVector, 4>; -using EqualValuesMap = - DenseMap>>; -using LocationMap = - DenseMap>; +struct SCEVDbgValueBuilder { + SCEVDbgValueBuilder() = default; + SCEVDbgValueBuilder(const SCEVDbgValueBuilder &Base) { + Values = Base.Values; + Expr = Base.Expr; + } -static void DbgGatherEqualValues(Loop *L, ScalarEvolution &SE, - EqualValuesMap &DbgValueToEqualSet, - LocationMap &DbgValueToLocation) { + /// The DIExpression as we translate the SCEV. + SmallVector Expr; + /// The location ops of the DIExpression. + SmallVector Values; + + void pushOperator(uint64_t Op) { Expr.push_back(Op); } + void pushUInt(uint64_t Operand) { Expr.push_back(Operand); } + + /// Add a DW_OP_LLVM_arg to the expression, followed by the index of the value + /// in the set of values referenced by the expression. + void pushValue(llvm::Value *V) { + Expr.push_back(llvm::dwarf::DW_OP_LLVM_arg); + auto *It = + std::find(Values.begin(), Values.end(), llvm::ValueAsMetadata::get(V)); + unsigned ArgIndex = 0; + if (It != Values.end()) { + ArgIndex = std::distance(Values.begin(), It); + } else { + ArgIndex = Values.size(); + Values.push_back(llvm::ValueAsMetadata::get(V)); + } + Expr.push_back(ArgIndex); + } + + void pushValue(const SCEVUnknown *U) { + llvm::Value *V = cast(U)->getValue(); + pushValue(V); + } + + void pushConst(const SCEVConstant *C) { + Expr.push_back(llvm::dwarf::DW_OP_consts); + Expr.push_back(C->getAPInt().getSExtValue()); + } + + /// Several SCEV types are sequences of the same arithmetic operator applied + /// to constants and values that may be extended or truncated. + bool pushArithmeticExpr(const llvm::SCEVCommutativeExpr *CommExpr, + uint64_t DwarfOp) { + assert((isa(CommExpr) || isa(CommExpr)) && + "Expected arithmetic SCEV type"); + bool Success = true; + unsigned EmitOperator = 0; + for (auto &Op : CommExpr->operands()) { + Success &= pushSCEV(Op); + + if (EmitOperator >= 1) + pushOperator(DwarfOp); + ++EmitOperator; + } + return Success; + } + + // TODO: Identify and omit noop casts. + bool pushCast(const llvm::SCEVCastExpr *C, bool IsSigned) { + const llvm::SCEV *Inner = C->getOperand(0); + const llvm::Type *Type = C->getType(); + uint64_t ToWidth = Type->getIntegerBitWidth(); + bool Success = pushSCEV(Inner); + uint64_t CastOps[] = {dwarf::DW_OP_LLVM_convert, ToWidth, + IsSigned ? llvm::dwarf::DW_ATE_signed + : llvm::dwarf::DW_ATE_unsigned}; + for (const auto &Op : CastOps) + pushOperator(Op); + return Success; + } + + // TODO: MinMax - although these haven't been encountered in the test suite. + bool pushSCEV(const llvm::SCEV *S) { + bool Success = true; + if (const SCEVConstant *StartInt = dyn_cast(S)) { + pushConst(StartInt); + + } else if (const SCEVUnknown *U = dyn_cast(S)) { + if(!U->getValue()) + return false; + pushValue(U->getValue()); + + } else if (const SCEVMulExpr *MulRec = dyn_cast(S)) { + Success &= pushArithmeticExpr(MulRec, llvm::dwarf::DW_OP_mul); + + } else if (const SCEVUDivExpr *UDiv = dyn_cast(S)) { + Success &= pushSCEV(UDiv->getLHS()); + Success &= pushSCEV(UDiv->getRHS()); + pushOperator(llvm::dwarf::DW_OP_div); + + } else if (const SCEVCastExpr *Cast = dyn_cast(S)) { + // Assert if a new and unknown SCEVCastEXpr type is encountered. + assert((isa(Cast) || isa(Cast) || + isa(Cast) || isa(Cast)) && + "Unexpected cast type in SCEV."); + Success &= pushCast(Cast, (isa(Cast))); + + } else if (const SCEVAddExpr *AddExpr = dyn_cast(S)) { + Success &= pushArithmeticExpr(AddExpr, llvm::dwarf::DW_OP_plus); + + } else if (isa(S)) { + // Nested SCEVAddRecExpr are generated by nested loops and are currently + // unsupported. + return false; + + } else { + return false; + } + return Success; + } + + void setFinalExpression(llvm::DbgValueInst &DI, const DIExpression *OldExpr) { + // Re-state assumption that this dbg.value is not variadic. Any remaining + // opcodes in its expression operate on a single value already on the + // expression stack. Prepend our operations, which will re-compute and + // place that value on the expression stack. + assert(!DI.hasArgList()); + auto *NewExpr = + DIExpression::prependOpcodes(OldExpr, Expr, /*StackValue*/ true); + DI.setExpression(NewExpr); + + auto ValArrayRef = llvm::ArrayRef(Values); + DI.setRawLocation(llvm::DIArgList::get(DI.getContext(), ValArrayRef)); + } + + /// If a DVI can be emitted without a DIArgList, omit DW_OP_llvm_arg and the + /// location op index 0. + void setShortFinalExpression(llvm::DbgValueInst &DI, + const DIExpression *OldExpr) { + assert((Expr[0] == llvm::dwarf::DW_OP_LLVM_arg && Expr[1] == 0) && + "Expected DW_OP_llvm_arg and 0."); + DI.replaceVariableLocationOp( + 0u, llvm::MetadataAsValue::get(DI.getContext(), Values[0])); + + // See setFinalExpression: prepend our opcodes on the start of any old + // expression opcodes. + assert(!DI.hasArgList()); + llvm::SmallVector FinalExpr(Expr.begin() + 2, Expr.end()); + auto *NewExpr = + DIExpression::prependOpcodes(OldExpr, FinalExpr, /*StackValue*/ true); + DI.setExpression(NewExpr); + } + + /// Once the IV and variable SCEV translation is complete, write it to the + /// source DVI. + void applyExprToDbgValue(llvm::DbgValueInst &DI, + const DIExpression *OldExpr) { + assert(!Expr.empty() && "Unexpected empty expression."); + // Emit a simpler form if only a single location is referenced. + if (Values.size() == 1 && Expr[0] == llvm::dwarf::DW_OP_LLVM_arg && + Expr[1] == 0) { + setShortFinalExpression(DI, OldExpr); + } else { + setFinalExpression(DI, OldExpr); + } + } + + /// Return true if the combination of arithmetic operator and underlying + /// SCEV constant value is an identity function. + bool isIdentityFunction(uint64_t Op, const SCEV *S) { + if (const SCEVConstant *C = dyn_cast(S)) { + int64_t I = C->getAPInt().getSExtValue(); + switch (Op) { + case llvm::dwarf::DW_OP_plus: + case llvm::dwarf::DW_OP_minus: + return I == 0; + case llvm::dwarf::DW_OP_mul: + case llvm::dwarf::DW_OP_div: + return I == 1; + } + } + return false; + } + + /// Convert a SCEV of a value to a DIExpression that is pushed onto the + /// builder's expression stack. The stack should already contain an + /// expression for the iteration count, so that it can be multiplied by + /// the stride and added to the start. + /// Components of the expression are omitted if they are an identity function. + /// Chain (non-affine) SCEVs are not supported. + bool SCEVToValueExpr(const llvm::SCEVAddRecExpr &SAR, ScalarEvolution &SE) { + assert(SAR.isAffine() && "Expected affine SCEV"); + // TODO: Is this check needed? + if (isa(SAR.getStart())) + return false; + + const SCEV *Start = SAR.getStart(); + const SCEV *Stride = SAR.getStepRecurrence(SE); + + // Skip pushing arithmetic noops. + if (!isIdentityFunction(llvm::dwarf::DW_OP_mul, Stride)) { + if (!pushSCEV(Stride)) + return false; + pushOperator(llvm::dwarf::DW_OP_mul); + } + if (!isIdentityFunction(llvm::dwarf::DW_OP_plus, Start)) { + if (!pushSCEV(Start)) + return false; + pushOperator(llvm::dwarf::DW_OP_plus); + } + return true; + } + + /// Convert a SCEV of a value to a DIExpression that is pushed onto the + /// builder's expression stack. The stack should already contain an + /// expression for the iteration count, so that it can be multiplied by + /// the stride and added to the start. + /// Components of the expression are omitted if they are an identity function. + bool SCEVToIterCountExpr(const llvm::SCEVAddRecExpr &SAR, + ScalarEvolution &SE) { + assert(SAR.isAffine() && "Expected affine SCEV"); + if (isa(SAR.getStart())) { + LLVM_DEBUG(dbgs() << "scev-salvage: IV SCEV. Unsupported nested AddRec: " + << SAR << '\n'); + return false; + } + const SCEV *Start = SAR.getStart(); + const SCEV *Stride = SAR.getStepRecurrence(SE); + + // Skip pushing arithmetic noops. + if (!isIdentityFunction(llvm::dwarf::DW_OP_minus, Start)) { + if (!pushSCEV(Start)) + return false; + pushOperator(llvm::dwarf::DW_OP_minus); + } + if (!isIdentityFunction(llvm::dwarf::DW_OP_div, Stride)) { + if (!pushSCEV(Stride)) + return false; + pushOperator(llvm::dwarf::DW_OP_div); + } + return true; + } +}; + +struct DVIRecoveryRec { + DbgValueInst *DVI; + DIExpression *Expr; + Metadata *LocationOp; + const llvm::SCEV *SCEV; +}; + +static bool RewriteDVIUsingIterCount(DVIRecoveryRec CachedDVI, + const SCEVDbgValueBuilder &IterationCount, + ScalarEvolution &SE) { + // LSR may add locations to previously single location-op DVIs which + // are currently not supported. + if (CachedDVI.DVI->getNumVariableLocationOps() != 1) + return false; + + // SCEVs for SSA values are most frquently of the form + // {start,+,stride}, but sometimes they are ({start,+,stride} + %a + ..). + // This is because %a is a PHI node that is not the IV. However, these + // SCEVs have not been observed to result in debuginfo-lossy optimisations, + // so its not expected this point will be reached. + if (!isa(CachedDVI.SCEV)) + return false; + + LLVM_DEBUG(dbgs() << "scev-salvage: Value to salvage SCEV: " + << *CachedDVI.SCEV << '\n'); + + const auto *Rec = cast(CachedDVI.SCEV); + if (!Rec->isAffine()) + return false; + + // Initialise a new builder with the iteration count expression. In + // combination with the value's SCEV this enables recovery. + SCEVDbgValueBuilder RecoverValue(IterationCount); + if (!RecoverValue.SCEVToValueExpr(*Rec, SE)) + return false; + + LLVM_DEBUG(dbgs() << "scev-salvage: Updating: " << *CachedDVI.DVI << '\n'); + RecoverValue.applyExprToDbgValue(*CachedDVI.DVI, CachedDVI.Expr); + LLVM_DEBUG(dbgs() << "scev-salvage: to: " << *CachedDVI.DVI << '\n'); + return true; +} + +static bool +DbgRewriteSalvageableDVIs(llvm::Loop *L, ScalarEvolution &SE, + llvm::PHINode *LSRInductionVar, + SmallVector &DVIToUpdate) { + if (DVIToUpdate.empty()) + return false; + + const llvm::SCEV *SCEVInductionVar = SE.getSCEV(LSRInductionVar); + assert(SCEVInductionVar && + "Anticipated a SCEV for the post-LSR induction variable"); + + bool Changed = false; + if (const SCEVAddRecExpr *IVAddRec = + dyn_cast(SCEVInductionVar)) { + SCEVDbgValueBuilder IterCountExpr; + IterCountExpr.pushValue(LSRInductionVar); + if (!IterCountExpr.SCEVToIterCountExpr(*IVAddRec, SE)) + return false; + + LLVM_DEBUG(dbgs() << "scev-salvage: IV SCEV: " << *SCEVInductionVar + << '\n'); + + // Needn't salvage if the location op hasn't been undef'd by LSR. + for (auto &DVIRec : DVIToUpdate) { + if (!DVIRec.DVI->isUndef()) + continue; + + // Some DVIs that were single location-op when cached are now multi-op, + // due to LSR optimisations. However, multi-op salvaging is not yet + // supported by SCEV salvaging. But, we can attempt a salvage by restoring + // the pre-LSR single-op expression. + if (DVIRec.DVI->hasArgList()) { + llvm::Type *Ty = DVIRec.DVI->getVariableLocationOp(0)->getType(); + DVIRec.DVI->setRawLocation( + llvm::ValueAsMetadata::get(UndefValue::get(Ty))); + DVIRec.DVI->setExpression(DVIRec.Expr); + } + + Changed |= RewriteDVIUsingIterCount(DVIRec, IterCountExpr, SE); + } + } + return Changed; +} + +/// Identify and cache salvageable DVI locations and expressions along with the +/// corresponding SCEV(s). Also ensure that the DVI is not deleted before +static void +DbgGatherSalvagableDVI(Loop *L, ScalarEvolution &SE, + SmallVector &SalvageableDVISCEVs, + SmallSet, 2> &DVIHandles) { for (auto &B : L->getBlocks()) { for (auto &I : *B) { auto DVI = dyn_cast(&I); if (!DVI) continue; - for (unsigned Idx = 0; Idx < DVI->getNumVariableLocationOps(); ++Idx) { - // TODO: We can duplicate results if the same arg appears more than - // once. - Value *V = DVI->getVariableLocationOp(Idx); - if (!V || !SE.isSCEVable(V->getType())) - continue; - auto DbgValueSCEV = SE.getSCEV(V); - EqualValues EqSet; - for (PHINode &Phi : L->getHeader()->phis()) { - if (V->getType() != Phi.getType()) - continue; - if (!SE.isSCEVable(Phi.getType())) - continue; - auto PhiSCEV = SE.getSCEV(&Phi); - Optional Offset = - SE.computeConstantDifference(DbgValueSCEV, PhiSCEV); - if (Offset && Offset->getMinSignedBits() <= 64) - EqSet.emplace_back( - std::make_tuple(&Phi, Offset.getValue().getSExtValue())); - } - DbgValueToEqualSet[DVI].push_back({Idx, std::move(EqSet)}); - // If we fall back to using this raw location, at least one location op - // must be dead. A DIArgList will automatically undef arguments when - // they become unavailable, but a ValueAsMetadata will not; since we - // know the value should be undef, we use the undef value directly here. - Metadata *RawLocation = - DVI->hasArgList() ? DVI->getRawLocation() - : ValueAsMetadata::get(UndefValue::get( - DVI->getVariableLocationOp(0)->getType())); - DbgValueToLocation[DVI] = {DVI->getExpression(), RawLocation}; - } + + if (DVI->hasArgList()) + continue; + + if (!SE.isSCEVable(DVI->getVariableLocationOp(0)->getType())) + continue; + + SalvageableDVISCEVs.push_back( + {DVI, DVI->getExpression(), DVI->getRawLocation(), + SE.getSCEV(DVI->getVariableLocationOp(0))}); + DVIHandles.insert(DVI); } } } -static void DbgApplyEqualValues(EqualValuesMap &DbgValueToEqualSet, - LocationMap &DbgValueToLocation) { - for (auto A : DbgValueToEqualSet) { - auto *DVI = A.first; - // Only update those that are now undef. - if (!DVI->isUndef()) +/// Ideally pick the PHI IV inserted by ScalarEvolutionExpander. As a fallback +/// any PHi from the loop header is usable, but may have less chance of +/// 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. + for (const WeakVH &IV : LSR.getScalarEvolutionIVs()) { + if (!IV) continue; - // The dbg.value may have had its value or expression changed during LSR by - // a failed salvage attempt; refresh them from the map. - auto *DbgDIExpr = DbgValueToLocation[DVI].first; - DVI->setRawLocation(DbgValueToLocation[DVI].second); - DVI->setExpression(DbgDIExpr); - assert(DVI->isUndef() && "dbg.value with non-undef location should not " - "have been modified by LSR."); - for (auto IdxEV : A.second) { - unsigned Idx = IdxEV.first; - for (auto EV : IdxEV.second) { - auto EVHandle = std::get(EV); - if (!EVHandle) - continue; - int64_t Offset = std::get(EV); - DVI->replaceVariableLocationOp(Idx, EVHandle); - if (Offset) { - SmallVector Ops; - DIExpression::appendOffset(Ops, Offset); - DbgDIExpr = DIExpression::appendOpsToArg(DbgDIExpr, Ops, Idx, true); - } - DVI->setExpression(DbgDIExpr); - break; - } + + assert(isa(&*IV) && "Expected PhI node."); + if (SE.isSCEVable((*IV).getType())) { + PHINode *Phi = dyn_cast(&*IV); + const llvm::SCEV *S = SE.getSCEV(Phi); + LLVM_DEBUG(dbgs() << "scev-salvage: IV : " << *IV << "with SCEV: " << *S + << "\n"); + return Phi; } } + + for (PHINode &Phi : L.getHeader()->phis()) { + if (!SE.isSCEVable(Phi.getType())) + continue; + + const llvm::SCEV *PhiSCEV = SE.getSCEV(&Phi); + if (const llvm::SCEVAddRecExpr *Rec = dyn_cast(PhiSCEV)) + if (!Rec->isAffine()) + continue; + + LLVM_DEBUG(dbgs() << "scev-salvage: Selected IV from loop header: " << Phi + << " with SCEV: " << *PhiSCEV << "\n"); + return Φ + } + return nullptr; } static bool ReduceLoopStrength(Loop *L, IVUsers &IU, ScalarEvolution &SE, @@ -5948,20 +6261,21 @@ static bool ReduceLoopStrength(Loop *L, IVUsers &IU, ScalarEvolution &SE, AssumptionCache &AC, TargetLibraryInfo &TLI, MemorySSA *MSSA) { + // Debug preservation - before we start removing anything identify which DVI + // meet the salvageable criteria and store their DIExpression and SCEVs. + SmallVector SalvageableDVI; + SmallSet, 2> DVIHandles; + DbgGatherSalvagableDVI(L, SE, SalvageableDVI, DVIHandles); + bool Changed = false; std::unique_ptr MSSAU; if (MSSA) MSSAU = std::make_unique(MSSA); // Run the main LSR transformation. - Changed |= - LSRInstance(L, IU, SE, DT, LI, TTI, AC, TLI, MSSAU.get()).getChanged(); - - // Debug preservation - before we start removing anything create equivalence - // sets for the llvm.dbg.value intrinsics. - EqualValuesMap DbgValueToEqualSet; - LocationMap DbgValueToLocation; - DbgGatherEqualValues(L, SE, DbgValueToEqualSet, DbgValueToLocation); + const LSRInstance &Reducer = + LSRInstance(L, IU, SE, DT, LI, TTI, AC, TLI, MSSAU.get()); + Changed |= Reducer.getChanged(); // Remove any extra phis created by processing inner loops. Changed |= DeleteDeadPHIs(L->getHeader(), &TLI, MSSAU.get()); @@ -5981,8 +6295,22 @@ static bool ReduceLoopStrength(Loop *L, IVUsers &IU, ScalarEvolution &SE, } } - DbgApplyEqualValues(DbgValueToEqualSet, DbgValueToLocation); + if (SalvageableDVI.empty()) + return Changed; + // Obtain relevant IVs and attempt to rewrite the salvageable DVIs with + // expressions composed using the derived iteration count. + // TODO: Allow for multiple IV references for nested AddRecSCEVs + for (auto &L : LI) { + if (llvm::PHINode *IV = GetInductionVariable(*L, SE, Reducer)) + DbgRewriteSalvageableDVIs(L, SE, IV, SalvageableDVI); + else { + LLVM_DEBUG(dbgs() << "scev-salvage: SCEV salvaging not possible. An IV " + "could not be identified.\n"); + } + } + + DVIHandles.clear(); return Changed; } diff --git a/lib/Transforms/Utils/ScalarEvolutionExpander.cpp b/lib/Transforms/Utils/ScalarEvolutionExpander.cpp index 5af1c37e619..3978e1e2982 100644 --- a/lib/Transforms/Utils/ScalarEvolutionExpander.cpp +++ b/lib/Transforms/Utils/ScalarEvolutionExpander.cpp @@ -1393,9 +1393,10 @@ SCEVExpander::getAddRecExprPHILiterally(const SCEVAddRecExpr *Normalized, // can ensure that IVIncrement dominates the current uses. PostIncLoops = SavedPostIncLoops; - // Remember this PHI, even in post-inc mode. + // Remember this PHI, even in post-inc mode. LSR SCEV-based salvaging is most + // effective when we are able to use an IV inserted here, so record it. InsertedValues.insert(PN); - + InsertedIVs.push_back(PN); return PN; } diff --git a/test/Transforms/LoopStrengthReduce/dbg-preserve-0.ll b/test/Transforms/LoopStrengthReduce/dbg-preserve-0.ll index 71031aabb95..d5f2ef9383e 100644 --- a/test/Transforms/LoopStrengthReduce/dbg-preserve-0.ll +++ b/test/Transforms/LoopStrengthReduce/dbg-preserve-0.ll @@ -21,11 +21,11 @@ for.body: ; preds = %entry, %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 +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i8* %lsr.iv, i8* %p), metadata ![[MID_p:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_consts, 3, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_minus, DW_OP_consts, 3, DW_OP_div, DW_OP_consts, 3, DW_OP_mul, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)) %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 +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i8* %lsr.iv, i8* %p), metadata ![[MID_p]], metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_consts, 3, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_minus, DW_OP_consts, 3, DW_OP_div, DW_OP_consts, 3, DW_OP_mul, DW_OP_consts, 3, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_plus, DW_OP_stack_value)) 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 diff --git a/test/Transforms/LoopStrengthReduce/dbg-preserve-2.ll b/test/Transforms/LoopStrengthReduce/dbg-preserve-2.ll index 030c5bb3e5d..8bbcc4d3b3a 100644 --- a/test/Transforms/LoopStrengthReduce/dbg-preserve-2.ll +++ b/test/Transforms/LoopStrengthReduce/dbg-preserve-2.ll @@ -2,7 +2,7 @@ ; Test that LSR does not produce invalid debug info when a debug value is ; salvaged during LSR by adding additional location operands, then becomes -; undef, and finally recovered by using equal values gathered before LSR. +; undef, and finally recovered by SCEV salvaging. target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" @@ -20,7 +20,7 @@ do.body: ; preds = %do.body, %entry %Result.addr.0 = phi i32 [ %Result, %entry ], [ %or, %do.body ] %Itr.0 = phi i32 [ 0, %entry ], [ %add, %do.body ], !dbg !17 ; CHECK-NOT: call void @llvm.dbg.value(metadata !DIArgList -; CHECK: call void @llvm.dbg.value(metadata i32 %lsr.iv, metadata ![[VAR_ITR:[0-9]+]], metadata !DIExpression() +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i32 %lsr.iv, i32 %Step), metadata ![[VAR_ITR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_minus, DW_OP_LLVM_arg, 1, DW_OP_div, DW_OP_LLVM_arg, 1, DW_OP_mul, DW_OP_stack_value)) call void @llvm.dbg.value(metadata i32 %Itr.0, metadata !16, metadata !DIExpression()), !dbg !17 call void @llvm.dbg.value(metadata i32 %Result.addr.0, metadata !12, metadata !DIExpression()), !dbg !17 %add = add nsw i32 %Itr.0, %Step, !dbg !19 diff --git a/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-0.ll b/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-0.ll new file mode 100644 index 00000000000..99215db3591 --- /dev/null +++ b/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-0.ll @@ -0,0 +1,92 @@ +; RUN: opt -S -loop-reduce %s -o - | FileCheck %s +; REQUIRES: x86-registered-target + +;; Ensure that we retain debuginfo for the induction variable and dependant +;; variables when loop strength reduction is applied to the loop. +;; This IR produced from: +;; +;; clang -S -emit-llvm -Xclang -disable-llvm-passes -g lsr-basic.cpp -o +;; Then executing opt -O2 up to the the loopFullUnroll pass. +;; void mul_pow_of_2_to_shift(unsigned size, unsigned *data) { +;; unsigned i = 0; +;; #pragma clang loop vectorize(disable) +;; while (i < size) { +;; unsigned comp = i * 8; +;; data[i] = comp; +;; i++; // DexLabel('mul_pow_of_2_induction_increment') +;; } +;; } +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[i:[0-9]+]], metadata !DIExpression(DW_OP_consts, 8, DW_OP_div, DW_OP_stack_value)) +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[comp:[0-9]+]], metadata !DIExpression(DW_OP_consts, 8, DW_OP_div, DW_OP_consts, 8, DW_OP_mul, DW_OP_stack_value)) +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[i]], metadata !DIExpression(DW_OP_consts, 8, DW_OP_div, DW_OP_consts, 1, DW_OP_plus, DW_OP_stack_value)) +; CHECK: ![[i]] = !DILocalVariable(name: "i" +; CHECK: ![[comp]] = !DILocalVariable(name: "comp" + +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 @_Z21mul_pow_of_2_to_shiftjPj(i32 %size, i32* nocapture %data) local_unnamed_addr !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i32 %size, metadata !12, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata i32* %data, metadata !14, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata i32 0, metadata !15, metadata !DIExpression()), !dbg !13 + %cmp4.not = icmp eq i32 %size, 0, !dbg !13 + br i1 %cmp4.not, label %while.end, label %while.body.preheader, !dbg !13 + +while.body.preheader: ; preds = %entry + %wide.trip.count = zext i32 %size to i64, !dbg !13 + br label %while.body, !dbg !13 + +while.body: ; preds = %while.body, %while.body.preheader + %indvars.iv = phi i64 [ 0, %while.body.preheader ], [ %indvars.iv.next, %while.body ] + call void @llvm.dbg.value(metadata i64 %indvars.iv, metadata !15, metadata !DIExpression()), !dbg !13 + %0 = trunc i64 %indvars.iv to i32, !dbg !16 + %mul = shl i32 %0, 3, !dbg !16 + call void @llvm.dbg.value(metadata i32 %mul, metadata !18, metadata !DIExpression()), !dbg !16 + %arrayidx = getelementptr inbounds i32, i32* %data, i64 %indvars.iv, !dbg !16 + store i32 %mul, i32* %arrayidx, align 4, !dbg !16 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1, !dbg !16 + call void @llvm.dbg.value(metadata i64 %indvars.iv.next, metadata !15, metadata !DIExpression()), !dbg !13 + %exitcond = icmp ne i64 %indvars.iv.next, %wide.trip.count, !dbg !13 + br i1 %exitcond, label %while.body, label %while.end.loopexit, !dbg !13, !llvm.loop !19 + +while.end.loopexit: ; preds = %while.body + br label %while.end, !dbg !13 + +while.end: ; preds = %while.end.loopexit, %entry + ret void, !dbg !13 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #0 + +attributes #0 = { nofree nosync nounwind readnone speculatable willreturn } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 13.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "lsr-basic.cpp", directory: "/tmp") +!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 13.0.0"} +!7 = distinct !DISubprogram(name: "mul_pow_of_2_to_shift", linkageName: "_Z21mul_pow_of_2_to_shiftjPj", scope: !1, file: !1, line: 18, type: !8, scopeLine: 18, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{null, !10, !11} +!10 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) +!12 = !DILocalVariable(name: "size", arg: 1, scope: !7, file: !1, line: 18, type: !10) +!13 = !DILocation(line: 0, scope: !7) +!14 = !DILocalVariable(name: "data", arg: 2, scope: !7, file: !1, line: 18, type: !11) +!15 = !DILocalVariable(name: "i", scope: !7, file: !1, line: 19, type: !10) +!16 = !DILocation(line: 22, column: 27, scope: !17) +!17 = distinct !DILexicalBlock(scope: !7, file: !1, line: 21, column: 22) +!18 = !DILocalVariable(name: "comp", scope: !17, file: !1, line: 22, type: !10) +!19 = distinct !{!19, !20, !21, !22, !23} +!20 = !DILocation(line: 21, column: 5, scope: !7) +!21 = !DILocation(line: 25, column: 5, scope: !7) +!22 = !{!"llvm.loop.mustprogress"} +!23 = !{!"llvm.loop.vectorize.width", i32 1} diff --git a/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-1.ll b/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-1.ll new file mode 100644 index 00000000000..3a9117d7b9c --- /dev/null +++ b/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-1.ll @@ -0,0 +1,90 @@ +; RUN: opt -S -loop-reduce %s -o - | FileCheck %s +; REQUIRES: x86-registered-target + +;; Ensure that we retain debuginfo for the induction variable and dependant +;; variables when loop strength reduction is applied to the loop. +;; This IR produced from: +;; +;; clang -S -emit-llvm -Xclang -disable-llvm-passes -g lsr-basic.cpp -o +;; Then executing opt -O2 up to the the loopFullUnroll pass. +;; void mul_to_addition(unsigned k, unsigned size, unsigned *data) { +;; int i = 0; +;; #pragma clang loop vectorize(disable) +;; while (i < size) { +;; int comp = (4 * i) + k; +;; data[i] = comp; +;; i += 1; +;; } +;; } +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[i:[0-9]+]], metadata !DIExpression()) +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i64 %lsr.iv, i32 %k), metadata ![[comp:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_consts, 4, DW_OP_mul, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)) +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[i]], metadata !DIExpression(DW_OP_consts, 1, DW_OP_plus, DW_OP_stack_value)) +; CHECK: ![[i]] = !DILocalVariable(name: "i" +; CHECK: ![[comp]] = !DILocalVariable(name: "comp" + +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 @_Z15mul_to_additionjjPj(i32 %k, i32 %size, i32* nocapture %data) local_unnamed_addr !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i32 %k, metadata !13, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.value(metadata i32 %size, metadata !15, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.value(metadata i32* %data, metadata !16, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.value(metadata i32 0, metadata !17, metadata !DIExpression()), !dbg !14 + br label %while.cond, !dbg !14 + +while.cond: ; preds = %while.body, %entry + %i.0 = phi i32 [ 0, %entry ], [ %add1, %while.body ], !dbg !14 + call void @llvm.dbg.value(metadata i32 %i.0, metadata !17, metadata !DIExpression()), !dbg !14 + %cmp = icmp ult i32 %i.0, %size, !dbg !14 + br i1 %cmp, label %while.body, label %while.end, !dbg !14 + +while.body: ; preds = %while.cond + %mul = mul nsw i32 %i.0, 4, !dbg !19 + %add = add i32 %mul, %k, !dbg !19 + call void @llvm.dbg.value(metadata i32 %add, metadata !21, metadata !DIExpression()), !dbg !19 + %idxprom = zext i32 %i.0 to i64, !dbg !19 + %arrayidx = getelementptr inbounds i32, i32* %data, i64 %idxprom, !dbg !19 + store i32 %add, i32* %arrayidx, align 4, !dbg !19 + %add1 = add nuw nsw i32 %i.0, 1, !dbg !19 + call void @llvm.dbg.value(metadata i32 %add1, metadata !17, metadata !DIExpression()), !dbg !14 + br label %while.cond, !dbg !14, !llvm.loop !22 + +while.end: ; preds = %while.cond + ret void, !dbg !14 +} + +; 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} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "basic.cpp", directory: "/test") +!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: "mul_to_addition", linkageName: "_Z15mul_to_additionjjPj", scope: !8, file: !8, line: 64, type: !9, scopeLine: 64, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!8 = !DIFile(filename: "./basic.cpp", directory: "/test") +!9 = !DISubroutineType(types: !10) +!10 = !{null, !11, !11, !12} +!11 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64) +!13 = !DILocalVariable(name: "k", arg: 1, scope: !7, file: !8, line: 64, type: !11) +!14 = !DILocation(line: 0, scope: !7) +!15 = !DILocalVariable(name: "size", arg: 2, scope: !7, file: !8, line: 64, type: !11) +!16 = !DILocalVariable(name: "data", arg: 3, scope: !7, file: !8, line: 64, type: !12) +!17 = !DILocalVariable(name: "i", scope: !7, file: !8, line: 65, type: !18) +!18 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!19 = !DILocation(line: 68, column: 23, scope: !20) +!20 = distinct !DILexicalBlock(scope: !7, file: !8, line: 67, column: 22) +!21 = !DILocalVariable(name: "comp", scope: !20, file: !8, line: 68, type: !18) +!22 = distinct !{!22, !23, !24, !25, !26} +!23 = !DILocation(line: 67, column: 5, scope: !7) +!24 = !DILocation(line: 71, column: 5, scope: !7) +!25 = !{!"llvm.loop.mustprogress"} +!26 = !{!"llvm.loop.vectorize.width", i32 1} diff --git a/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-2.ll b/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-2.ll new file mode 100644 index 00000000000..4a297a7fd80 --- /dev/null +++ b/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-2.ll @@ -0,0 +1,87 @@ +; RUN: opt -S -loop-reduce %s -o - | FileCheck %s +; REQUIRES: x86-registered-target + +;; Ensure that we retain debuginfo for the induction variable and dependant +;; variables when loop strength reduction is applied to the loop. +;; This IR produced from: +;; +;; clang -S -emit-llvm -Xclang -disable-llvm-passes -g lsr-basic.cpp -o +;; Then executing opt -O2 up to the the loopFullUnroll pass. +;; void basic_recurrence(unsigned k, unsigned size, unsigned *data) +;; { +;; unsigned i = 0; +;; #pragma clang loop vectorize(disable) +;; while (i < size) { +;; unsigned comp = i * k; +;; data[i] = comp; +;; i++; +;; } +;; } +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[i:[0-9]+]], metadata !DIExpression()) +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i64 %lsr.iv, i32 %k), metadata ![[comp:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_mul, DW_OP_stack_value)) +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[i]], metadata !DIExpression(DW_OP_consts, 1, DW_OP_plus, DW_OP_stack_value)) +; CHECK: ![[i]] = !DILocalVariable(name: "i" +; CHECK: ![[comp]] = !DILocalVariable(name: "comp" + +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 @_Z16basic_recurrencejjPj(i32 %k, i32 %size, i32* nocapture %data) local_unnamed_addr !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i32 %k, metadata !13, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.value(metadata i32 %size, metadata !15, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.value(metadata i32* %data, metadata !16, metadata !DIExpression()), !dbg !14 + br label %while.cond, !dbg !14 + +while.cond: ; preds = %while.body, %entry + %i.0 = phi i32 [ 0, %entry ], [ %inc, %while.body ] + call void @llvm.dbg.value(metadata i32 %i.0, metadata !17, metadata !DIExpression()), !dbg !14 + %cmp = icmp ult i32 %i.0, %size, !dbg !14 + br i1 %cmp, label %while.body, label %while.end, !dbg !14 + +while.body: ; preds = %while.cond + %mul = mul i32 %i.0, %k, !dbg !18 + call void @llvm.dbg.value(metadata i32 %mul, metadata !20, metadata !DIExpression()), !dbg !18 + %idxprom = zext i32 %i.0 to i64, !dbg !18 + %arrayidx = getelementptr inbounds i32, i32* %data, i64 %idxprom, !dbg !18 + store i32 %mul, i32* %arrayidx, align 4, !dbg !18 + %inc = add nuw i32 %i.0, 1, !dbg !18 + call void @llvm.dbg.value(metadata i32 %inc, metadata !17, metadata !DIExpression()), !dbg !14 + br label %while.cond, !dbg !14, !llvm.loop !21 + +while.end: ; preds = %while.cond + ret void, !dbg !14 +} + +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_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "basic.cpp", directory: "/test") +!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: "basic_recurrence", linkageName: "_Z16basic_recurrencejjPj", scope: !8, file: !8, line: 82, type: !9, scopeLine: 83, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!8 = !DIFile(filename: "./basic.cpp", directory: "/test") +!9 = !DISubroutineType(types: !10) +!10 = !{null, !11, !11, !12} +!11 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64) +!13 = !DILocalVariable(name: "k", arg: 1, scope: !7, file: !8, line: 82, type: !11) +!14 = !DILocation(line: 0, scope: !7) +!15 = !DILocalVariable(name: "size", arg: 2, scope: !7, file: !8, line: 82, type: !11) +!16 = !DILocalVariable(name: "data", arg: 3, scope: !7, file: !8, line: 82, type: !12) +!17 = !DILocalVariable(name: "i", scope: !7, file: !8, line: 84, type: !11) +!18 = !DILocation(line: 87, column: 27, scope: !19) +!19 = distinct !DILexicalBlock(scope: !7, file: !8, line: 86, column: 22) +!20 = !DILocalVariable(name: "comp", scope: !19, file: !8, line: 87, type: !11) +!21 = distinct !{!21, !22, !23, !24, !25} +!22 = !DILocation(line: 86, column: 5, scope: !7) +!23 = !DILocation(line: 90, column: 5, scope: !7) +!24 = !{!"llvm.loop.mustprogress"} +!25 = !{!"llvm.loop.vectorize.width", i32 1} diff --git a/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-3.ll b/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-3.ll new file mode 100644 index 00000000000..0e4e687cc3f --- /dev/null +++ b/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-3.ll @@ -0,0 +1,88 @@ +; RUN: opt -S -loop-reduce %s -o - | FileCheck %s +; REQUIRES: x86-registered-target + +;; Ensure that we retain debuginfo for the induction variable and dependant +;; variables when loop strength reduction is applied to the loop. +;; This IR produced from: +;; +;; clang -S -emit-llvm -Xclang -disable-llvm-passes -g lsr-basic.cpp -o +;; Then executing opt -O2 up to the the loopFullUnroll pass. +;; void mul_pow_of_2_to_shift_var_inc(unsigned size, unsigned *data, unsigned multiplicand) { +;; unsigned i = 0; +;; #pragma clang loop vectorize(disable) +;; while (i < size) { +;; unsigned comp = i * multiplicand; +;; data[i] = comp; +;; i++; +;; } +;; } +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[i:[0-9]+]], metadata !DIExpression()) +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i64 %lsr.iv, i32 %multiplicand), metadata ![[comp:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_mul, DW_OP_stack_value)) +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[i]], metadata !DIExpression(DW_OP_consts, 1, DW_OP_plus, DW_OP_stack_value)) +; CHECK: ![[i]] = !DILocalVariable(name: "i" +; CHECK: ![[comp]] = !DILocalVariable(name: "comp" + +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" + +@__const.main.data = private unnamed_addr constant [16 x i32] [i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15], align 16 + +define dso_local void @_Z21mul_pow_of_2_to_shiftjPjj(i32 %size, i32* nocapture %data, i32 %multiplicand) local_unnamed_addr !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i32 %size, metadata !12, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata i32* %data, metadata !14, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata i32 %multiplicand, metadata !15, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata i32 0, metadata !16, metadata !DIExpression()), !dbg !13 + br label %while.cond, !dbg !13 + +while.cond: ; preds = %while.body, %entry + %i.0 = phi i32 [ 0, %entry ], [ %inc, %while.body ], !dbg !13 + call void @llvm.dbg.value(metadata i32 %i.0, metadata !16, metadata !DIExpression()), !dbg !13 + %cmp = icmp ult i32 %i.0, %size, !dbg !13 + br i1 %cmp, label %while.body, label %while.end, !dbg !13 + +while.body: ; preds = %while.cond + %mul = mul i32 %i.0, %multiplicand, !dbg !17 + call void @llvm.dbg.value(metadata i32 %mul, metadata !19, metadata !DIExpression()), !dbg !17 + %idxprom = zext i32 %i.0 to i64, !dbg !17 + %arrayidx = getelementptr inbounds i32, i32* %data, i64 %idxprom, !dbg !17 + store i32 %mul, i32* %arrayidx, align 4, !dbg !17 + %inc = add nuw i32 %i.0, 1, !dbg !17 + call void @llvm.dbg.value(metadata i32 %inc, metadata !16, metadata !DIExpression()), !dbg !13 + br label %while.cond, !dbg !13, !llvm.loop !20 + +while.end: ; preds = %while.cond + ret void, !dbg !13 +} + +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_C_plus_plus_14, file: !1, producer: "clang version 13.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "basic.cpp", directory: "/test") +!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 13.0.0"} +!7 = distinct !DISubprogram(name: "mul_pow_of_2_to_shift", linkageName: "_Z21mul_pow_of_2_to_shiftjPjj", scope: !1, file: !1, line: 17, type: !8, scopeLine: 17, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{null, !10, !11, !10} +!10 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) +!12 = !DILocalVariable(name: "size", arg: 1, scope: !7, file: !1, line: 17, type: !10) +!13 = !DILocation(line: 0, scope: !7) +!14 = !DILocalVariable(name: "data", arg: 2, scope: !7, file: !1, line: 17, type: !11) +!15 = !DILocalVariable(name: "multiplicand", arg: 3, scope: !7, file: !1, line: 17, type: !10) +!16 = !DILocalVariable(name: "i", scope: !7, file: !1, line: 18, type: !10) +!17 = !DILocation(line: 21, column: 27, scope: !18) +!18 = distinct !DILexicalBlock(scope: !7, file: !1, line: 20, column: 22) +!19 = !DILocalVariable(name: "comp", scope: !18, file: !1, line: 21, type: !10) +!20 = distinct !{!20, !21, !22, !23, !24} +!21 = !DILocation(line: 20, column: 5, scope: !7) +!22 = !DILocation(line: 24, column: 5, scope: !7) +!23 = !{!"llvm.loop.mustprogress"} +!24 = !{!"llvm.loop.vectorize.width", i32 1} diff --git a/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-4.ll b/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-4.ll new file mode 100644 index 00000000000..a97ef469fc9 --- /dev/null +++ b/test/Transforms/LoopStrengthReduce/debuginfo-scev-salvage-4.ll @@ -0,0 +1,173 @@ +; RUN: opt -S -loop-reduce %s -o - | FileCheck %s +; REQUIRES: x86-registered-target + +;; Ensure that we retain debuginfo for the induction variable and dependant +;; variables when loop strength reduction is applied to the loop. This test +;; covers the translation of a SCEVCastExpr to DIExpression containg +;; DW_OP_LLVM_CONVERT. This IR produced from: +;; +;; clang -S -emit-llvm -Xclang -disable-llvm-passes -g lsr-basic.cpp -o +;; Then executing opt -O2 up to the the loopFullUnroll pass. +;; void mul_pow_of_2_to_shift(unsigned size, unsigned *data) { +;; +;; void zext_scev(int64_t *arr, uint32_t factor0, int16_t factor1) { +;; uint32_t i = 0; +;; while(i < 63) { +;; uint32_t comp = factor0 - (4*i*factor1); +;; arr[i] = comp; +;; ++i; +;; } +;; } +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[i:[0-9]+]], metadata !DIExpression()) +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i64 %lsr.iv, i16 %factor1, i32 %factor0), metadata ![[comp:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_consts, 18446744073709551612, DW_OP_LLVM_arg, 1, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_mul, DW_OP_mul, DW_OP_LLVM_arg, 2, DW_OP_plus, DW_OP_stack_value)) +; CHECK: call void @llvm.dbg.value(metadata i64 %lsr.iv, metadata ![[i]], metadata !DIExpression(DW_OP_consts, 1, DW_OP_plus, DW_OP_stack_value)) +; CHECK: ![[i]] = !DILocalVariable(name: "i" +; CHECK: ![[comp]] = !DILocalVariable(name: "comp" + +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 @_Z9zext_scevPljs(i64* nocapture %arr, i32 %factor0, i16 signext %factor1) local_unnamed_addr !dbg !90 { +entry: + call void @llvm.dbg.value(metadata i64* %arr, metadata !94, metadata !DIExpression()), !dbg !95 + call void @llvm.dbg.value(metadata i32 %factor0, metadata !96, metadata !DIExpression()), !dbg !95 + call void @llvm.dbg.value(metadata i16 %factor1, metadata !97, metadata !DIExpression()), !dbg !95 + call void @llvm.dbg.value(metadata i32 0, metadata !98, metadata !DIExpression()), !dbg !95 + %conv = sext i16 %factor1 to i32 + %mul.neg = mul i32 %conv, -4 + call void @llvm.dbg.value(metadata i32 0, metadata !98, metadata !DIExpression()), !dbg !95 + br label %while.body, !dbg !95 + +while.body: ; preds = %while.body, %entry + %i.04 = phi i32 [ 0, %entry ], [ %inc, %while.body ] + call void @llvm.dbg.value(metadata i32 %i.04, metadata !98, metadata !DIExpression()), !dbg !95 + %mul1.neg = mul i32 %mul.neg, %i.04, !dbg !99 + %sub = add i32 %mul1.neg, %factor0, !dbg !99 + call void @llvm.dbg.value(metadata i32 %sub, metadata !101, metadata !DIExpression()), !dbg !99 + %conv2 = zext i32 %sub to i64, !dbg !99 + %idxprom = zext i32 %i.04 to i64, !dbg !99 + %arrayidx = getelementptr inbounds i64, i64* %arr, i64 %idxprom, !dbg !99 + store i64 %conv2, i64* %arrayidx, align 8, !dbg !99 + %inc = add nuw nsw i32 %i.04, 1, !dbg !99 + call void @llvm.dbg.value(metadata i32 %inc, metadata !98, metadata !DIExpression()), !dbg !95 + %cmp = icmp ult i32 %inc, 63, !dbg !95 + br i1 %cmp, label %while.body, label %while.end, !dbg !95, !llvm.loop !102 + +while.end: ; preds = %while.body + ret void, !dbg !95 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +attributes #0 = { nofree nosync nounwind readnone speculatable willreturn } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!84, !85, !86, !87, !88} +!llvm.ident = !{!89} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 13.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, imports: !3, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "cast.cpp", directory: "/test") +!2 = !{} +!3 = !{!4, !12, !16, !20, !24, !27, !29, !31, !33, !35, !37, !39, !41, !44, !46, !51, !55, !59, !63, !65, !67, !69, !71, !73, !75, !77, !79, !82} +!4 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !6, file: !11, line: 48) +!5 = !DINamespace(name: "std", scope: null) +!6 = !DIDerivedType(tag: DW_TAG_typedef, name: "int8_t", file: !7, line: 24, baseType: !8) +!7 = !DIFile(filename: "/usr/include/x86_64-linux-gnu/bits/stdint-intn.h", directory: "") +!8 = !DIDerivedType(tag: DW_TAG_typedef, name: "__int8_t", file: !9, line: 36, baseType: !10) +!9 = !DIFile(filename: "/usr/include/x86_64-linux-gnu/bits/types.h", directory: "") +!10 = !DIBasicType(name: "signed char", size: 8, encoding: DW_ATE_signed_char) +!11 = !DIFile(filename: "/usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/cstdint", directory: "") +!12 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !13, file: !11, line: 49) +!13 = !DIDerivedType(tag: DW_TAG_typedef, name: "int16_t", file: !7, line: 25, baseType: !14) +!14 = !DIDerivedType(tag: DW_TAG_typedef, name: "__int16_t", file: !9, line: 38, baseType: !15) +!15 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed) +!16 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !17, file: !11, line: 50) +!17 = !DIDerivedType(tag: DW_TAG_typedef, name: "int32_t", file: !7, line: 26, baseType: !18) +!18 = !DIDerivedType(tag: DW_TAG_typedef, name: "__int32_t", file: !9, line: 40, baseType: !19) +!19 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!20 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !21, file: !11, line: 51) +!21 = !DIDerivedType(tag: DW_TAG_typedef, name: "int64_t", file: !7, line: 27, baseType: !22) +!22 = !DIDerivedType(tag: DW_TAG_typedef, name: "__int64_t", file: !9, line: 43, baseType: !23) +!23 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) +!24 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !25, file: !11, line: 53) +!25 = !DIDerivedType(tag: DW_TAG_typedef, name: "int_fast8_t", file: !26, line: 68, baseType: !10) +!26 = !DIFile(filename: "/usr/include/stdint.h", directory: "") +!27 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !28, file: !11, line: 54) +!28 = !DIDerivedType(tag: DW_TAG_typedef, name: "int_fast16_t", file: !26, line: 70, baseType: !23) +!29 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !30, file: !11, line: 55) +!30 = !DIDerivedType(tag: DW_TAG_typedef, name: "int_fast32_t", file: !26, line: 71, baseType: !23) +!31 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !32, file: !11, line: 56) +!32 = !DIDerivedType(tag: DW_TAG_typedef, name: "int_fast64_t", file: !26, line: 72, baseType: !23) +!33 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !34, file: !11, line: 58) +!34 = !DIDerivedType(tag: DW_TAG_typedef, name: "int_least8_t", file: !26, line: 43, baseType: !10) +!35 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !36, file: !11, line: 59) +!36 = !DIDerivedType(tag: DW_TAG_typedef, name: "int_least16_t", file: !26, line: 44, baseType: !15) +!37 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !38, file: !11, line: 60) +!38 = !DIDerivedType(tag: DW_TAG_typedef, name: "int_least32_t", file: !26, line: 45, baseType: !19) +!39 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !40, file: !11, line: 61) +!40 = !DIDerivedType(tag: DW_TAG_typedef, name: "int_least64_t", file: !26, line: 47, baseType: !23) +!41 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !42, file: !11, line: 63) +!42 = !DIDerivedType(tag: DW_TAG_typedef, name: "intmax_t", file: !26, line: 111, baseType: !43) +!43 = !DIDerivedType(tag: DW_TAG_typedef, name: "__intmax_t", file: !9, line: 61, baseType: !23) +!44 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !45, file: !11, line: 64) +!45 = !DIDerivedType(tag: DW_TAG_typedef, name: "intptr_t", file: !26, line: 97, baseType: !23) +!46 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !47, file: !11, line: 66) +!47 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint8_t", file: !48, line: 24, baseType: !49) +!48 = !DIFile(filename: "/usr/include/x86_64-linux-gnu/bits/stdint-uintn.h", directory: "") +!49 = !DIDerivedType(tag: DW_TAG_typedef, name: "__uint8_t", file: !9, line: 37, baseType: !50) +!50 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char) +!51 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !52, file: !11, line: 67) +!52 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint16_t", file: !48, line: 25, baseType: !53) +!53 = !DIDerivedType(tag: DW_TAG_typedef, name: "__uint16_t", file: !9, line: 39, baseType: !54) +!54 = !DIBasicType(name: "unsigned short", size: 16, encoding: DW_ATE_unsigned) +!55 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !56, file: !11, line: 68) +!56 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint32_t", file: !48, line: 26, baseType: !57) +!57 = !DIDerivedType(tag: DW_TAG_typedef, name: "__uint32_t", file: !9, line: 41, baseType: !58) +!58 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!59 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !60, file: !11, line: 69) +!60 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint64_t", file: !48, line: 27, baseType: !61) +!61 = !DIDerivedType(tag: DW_TAG_typedef, name: "__uint64_t", file: !9, line: 44, baseType: !62) +!62 = !DIBasicType(name: "long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!63 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !64, file: !11, line: 71) +!64 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint_fast8_t", file: !26, line: 81, baseType: !50) +!65 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !66, file: !11, line: 72) +!66 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint_fast16_t", file: !26, line: 83, baseType: !62) +!67 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !68, file: !11, line: 73) +!68 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint_fast32_t", file: !26, line: 84, baseType: !62) +!69 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !70, file: !11, line: 74) +!70 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint_fast64_t", file: !26, line: 85, baseType: !62) +!71 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !72, file: !11, line: 76) +!72 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint_least8_t", file: !26, line: 54, baseType: !50) +!73 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !74, file: !11, line: 77) +!74 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint_least16_t", file: !26, line: 55, baseType: !54) +!75 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !76, file: !11, line: 78) +!76 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint_least32_t", file: !26, line: 56, baseType: !58) +!77 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !78, file: !11, line: 79) +!78 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint_least64_t", file: !26, line: 58, baseType: !62) +!79 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !80, file: !11, line: 81) +!80 = !DIDerivedType(tag: DW_TAG_typedef, name: "uintmax_t", file: !26, line: 112, baseType: !81) +!81 = !DIDerivedType(tag: DW_TAG_typedef, name: "__uintmax_t", file: !9, line: 62, baseType: !62) +!82 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !83, file: !11, line: 82) +!83 = !DIDerivedType(tag: DW_TAG_typedef, name: "uintptr_t", file: !26, line: 100, baseType: !62) +!84 = !{i32 7, !"Dwarf Version", i32 4} +!85 = !{i32 2, !"Debug Info Version", i32 3} +!86 = !{i32 1, !"wchar_size", i32 4} +!87 = !{i32 7, !"uwtable", i32 1} +!88 = !{i32 7, !"frame-pointer", i32 2} +!89 = !{!"clang version 13.0.0"} +!90 = distinct !DISubprogram(name: "zext_scev", linkageName: "_Z9zext_scevPljs", scope: !1, file: !1, line: 4, type: !91, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!91 = !DISubroutineType(types: !92) +!92 = !{null, !93, !56, !13} +!93 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) +!94 = !DILocalVariable(name: "arr", arg: 1, scope: !90, file: !1, line: 4, type: !93) +!95 = !DILocation(line: 0, scope: !90) +!96 = !DILocalVariable(name: "factor0", arg: 2, scope: !90, file: !1, line: 4, type: !56) +!97 = !DILocalVariable(name: "factor1", arg: 3, scope: !90, file: !1, line: 4, type: !13) +!98 = !DILocalVariable(name: "i", scope: !90, file: !1, line: 5, type: !56) +!99 = !DILocation(line: 7, column: 39, scope: !100) +!100 = distinct !DILexicalBlock(scope: !90, file: !1, line: 6, column: 19) +!101 = !DILocalVariable(name: "comp", scope: !100, file: !1, line: 7, type: !56) +!102 = distinct !{!102, !103, !104, !105} +!103 = !DILocation(line: 6, column: 5, scope: !90) +!104 = !DILocation(line: 10, column: 5, scope: !90) +!105 = !{!"llvm.loop.mustprogress"}