1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 04:02:41 +01:00

[DebugInfo][LoopStrengthReduction] SCEV-based salvaging for LSR

This patch extends salvaging of debuginfo in the Loop Strength Reduction
(LSR) pass by translating Scalar Evaluations (SCEV) into DIExpressions.
The method is as follows:
- Cache dbg.value intrinsics that are salvageable.
- Obtain a loop Induction Variable (IV) from ScalarExpressionExpander or
  the loop header.
- Translate the IV SCEV into an expression that recovers the current
  loop iteration count. Combine this with the dbg.value's location
  op SCEV to create a DIExpression that salvages the value.

Review by: jmorse

Differential Revision: https://reviews.llvm.org/D105207
This commit is contained in:
Chris Jackson 2021-07-22 09:27:46 +01:00
parent b5cbfeed77
commit 2e863e863d
10 changed files with 935 additions and 83 deletions

View File

@ -83,6 +83,9 @@ class SCEVExpander : public SCEVVisitor<SCEVExpander, Value *> {
/// InsertedValues/InsertedPostIncValues. /// InsertedValues/InsertedPostIncValues.
SmallPtrSet<Value *, 16> ReusedValues; SmallPtrSet<Value *, 16> ReusedValues;
// The induction variables generated.
SmallVector<WeakVH, 2> InsertedIVs;
/// A memoization of the "relevant" loop for a given SCEV. /// A memoization of the "relevant" loop for a given SCEV.
DenseMap<const SCEV *, const Loop *> RelevantLoops; DenseMap<const SCEV *, const Loop *> RelevantLoops;
@ -199,9 +202,11 @@ public:
InsertedPostIncValues.clear(); InsertedPostIncValues.clear();
ReusedValues.clear(); ReusedValues.clear();
ChainedPhis.clear(); ChainedPhis.clear();
InsertedIVs.clear();
} }
ScalarEvolution *getSE() { return &SE; } ScalarEvolution *getSE() { return &SE; }
const SmallVectorImpl<WeakVH> &getInsertedIVs() const { return InsertedIVs; }
/// Return a vector containing all instructions inserted during expansion. /// Return a vector containing all instructions inserted during expansion.
SmallVector<Instruction *, 32> getAllInsertedInstructions() const { SmallVector<Instruction *, 32> getAllInsertedInstructions() const {

View File

@ -1981,6 +1981,9 @@ class LSRInstance {
/// IV users that belong to profitable IVChains. /// IV users that belong to profitable IVChains.
SmallPtrSet<Use*, MaxChains> IVIncSet; SmallPtrSet<Use*, MaxChains> IVIncSet;
/// Induction variables that were generated and inserted by the SCEV Expander.
SmallVector<llvm::WeakVH, 2> ScalarEvolutionIVs;
void OptimizeShadowIV(); void OptimizeShadowIV();
bool FindIVUserForCond(ICmpInst *Cond, IVStrideUse *&CondUse); bool FindIVUserForCond(ICmpInst *Cond, IVStrideUse *&CondUse);
ICmpInst *OptimizeMax(ICmpInst *Cond, IVStrideUse* &CondUse); ICmpInst *OptimizeMax(ICmpInst *Cond, IVStrideUse* &CondUse);
@ -2085,6 +2088,9 @@ public:
TargetLibraryInfo &TLI, MemorySSAUpdater *MSSAU); TargetLibraryInfo &TLI, MemorySSAUpdater *MSSAU);
bool getChanged() const { return Changed; } bool getChanged() const { return Changed; }
const SmallVectorImpl<WeakVH> &getScalarEvolutionIVs() const {
return ScalarEvolutionIVs;
}
void print_factors_and_types(raw_ostream &OS) const; void print_factors_and_types(raw_ostream &OS) const;
void print_fixups(raw_ostream &OS) const; void print_fixups(raw_ostream &OS) const;
@ -5589,6 +5595,11 @@ void LSRInstance::ImplementSolution(
GenerateIVChain(Chain, Rewriter, DeadInsts); GenerateIVChain(Chain, Rewriter, DeadInsts);
Changed = true; Changed = true;
} }
for (const WeakVH &IV : Rewriter.getInsertedIVs())
if (IV && dyn_cast<Instruction>(&*IV)->getParent())
ScalarEvolutionIVs.push_back(IV);
// Clean up after ourselves. This must be done before deleting any // Clean up after ourselves. This must be done before deleting any
// instructions. // instructions.
Rewriter.clear(); Rewriter.clear();
@ -5859,87 +5870,387 @@ void LoopStrengthReduce::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addPreserved<MemorySSAWrapperPass>(); AU.addPreserved<MemorySSAWrapperPass>();
} }
using EqualValues = SmallVector<std::tuple<WeakVH, int64_t>, 4>; struct SCEVDbgValueBuilder {
using EqualValuesMap = SCEVDbgValueBuilder() = default;
DenseMap<DbgValueInst *, SmallVector<std::pair<unsigned, EqualValues>>>; SCEVDbgValueBuilder(const SCEVDbgValueBuilder &Base) {
using LocationMap = Values = Base.Values;
DenseMap<DbgValueInst *, std::pair<DIExpression *, Metadata *>>; Expr = Base.Expr;
}
static void DbgGatherEqualValues(Loop *L, ScalarEvolution &SE, /// The DIExpression as we translate the SCEV.
EqualValuesMap &DbgValueToEqualSet, SmallVector<uint64_t, 6> Expr;
LocationMap &DbgValueToLocation) { /// The location ops of the DIExpression.
SmallVector<llvm::ValueAsMetadata *, 2> 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<SCEVUnknown>(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<llvm::SCEVAddExpr>(CommExpr) || isa<SCEVMulExpr>(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<SCEVConstant>(S)) {
pushConst(StartInt);
} else if (const SCEVUnknown *U = dyn_cast<SCEVUnknown>(S)) {
pushValue(U->getValue());
} else if (const SCEVMulExpr *MulRec = dyn_cast<SCEVMulExpr>(S)) {
Success &= pushArithmeticExpr(MulRec, llvm::dwarf::DW_OP_mul);
} else if (const SCEVUDivExpr *UDiv = dyn_cast<SCEVUDivExpr>(S)) {
Success &= pushSCEV(UDiv->getLHS());
Success &= pushSCEV(UDiv->getRHS());
pushOperator(llvm::dwarf::DW_OP_div);
} else if (const SCEVCastExpr *Cast = dyn_cast<SCEVCastExpr>(S)) {
// Assert if a new and unknown SCEVCastEXpr type is encountered.
assert((isa<SCEVZeroExtendExpr>(Cast) || isa<SCEVTruncateExpr>(Cast) ||
isa<SCEVPtrToIntExpr>(Cast) || isa<SCEVSignExtendExpr>(Cast)) &&
"Unexpected cast type in SCEV.");
Success &= pushCast(Cast, (isa<SCEVSignExtendExpr>(Cast)));
} else if (const SCEVAddExpr *AddExpr = dyn_cast<SCEVAddExpr>(S)) {
Success &= pushArithmeticExpr(AddExpr, llvm::dwarf::DW_OP_plus);
} else if (isa<SCEVAddRecExpr>(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<llvm::ValueAsMetadata *>(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<uint64_t, 6> 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<SCEVConstant>(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<SCEVAddRecExpr>(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<SCEVAddRecExpr>(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<SCEVAddRecExpr>(CachedDVI.SCEV))
return false;
LLVM_DEBUG(dbgs() << "scev-salvage: Value to salvage SCEV: "
<< *CachedDVI.SCEV << '\n');
const auto *Rec = cast<SCEVAddRecExpr>(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<DVIRecoveryRec, 2> &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<SCEVAddRecExpr>(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<DVIRecoveryRec, 2> &SalvageableDVISCEVs,
SmallSet<AssertingVH<DbgValueInst>, 2> &DVIHandles) {
for (auto &B : L->getBlocks()) { for (auto &B : L->getBlocks()) {
for (auto &I : *B) { for (auto &I : *B) {
auto DVI = dyn_cast<DbgValueInst>(&I); auto DVI = dyn_cast<DbgValueInst>(&I);
if (!DVI) if (!DVI)
continue; continue;
for (unsigned Idx = 0; Idx < DVI->getNumVariableLocationOps(); ++Idx) {
// TODO: We can duplicate results if the same arg appears more than if (DVI->hasArgList())
// once. continue;
Value *V = DVI->getVariableLocationOp(Idx);
if (!V || !SE.isSCEVable(V->getType())) if (!SE.isSCEVable(DVI->getVariableLocationOp(0)->getType()))
continue; continue;
auto DbgValueSCEV = SE.getSCEV(V);
EqualValues EqSet; SalvageableDVISCEVs.push_back(
for (PHINode &Phi : L->getHeader()->phis()) { {DVI, DVI->getExpression(), DVI->getRawLocation(),
if (V->getType() != Phi.getType()) SE.getSCEV(DVI->getVariableLocationOp(0))});
continue; DVIHandles.insert(DVI);
if (!SE.isSCEVable(Phi.getType()))
continue;
auto PhiSCEV = SE.getSCEV(&Phi);
Optional<APInt> 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};
}
} }
} }
} }
static void DbgApplyEqualValues(EqualValuesMap &DbgValueToEqualSet, /// Ideally pick the PHI IV inserted by ScalarEvolutionExpander. As a fallback
LocationMap &DbgValueToLocation) { /// any PHi from the loop header is usable, but may have less chance of
for (auto A : DbgValueToEqualSet) { /// surviving subsequent transforms.
auto *DVI = A.first; static llvm::PHINode *GetInductionVariable(const Loop &L, ScalarEvolution &SE,
// Only update those that are now undef. const LSRInstance &LSR) {
if (!DVI->isUndef()) // 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; continue;
// The dbg.value may have had its value or expression changed during LSR by
// a failed salvage attempt; refresh them from the map. assert(isa<PHINode>(&*IV) && "Expected PhI node.");
auto *DbgDIExpr = DbgValueToLocation[DVI].first; if (SE.isSCEVable((*IV).getType())) {
DVI->setRawLocation(DbgValueToLocation[DVI].second); PHINode *Phi = dyn_cast<PHINode>(&*IV);
DVI->setExpression(DbgDIExpr); const llvm::SCEV *S = SE.getSCEV(Phi);
assert(DVI->isUndef() && "dbg.value with non-undef location should not " LLVM_DEBUG(dbgs() << "scev-salvage: IV : " << *IV << "with SCEV: " << *S
"have been modified by LSR."); << "\n");
for (auto IdxEV : A.second) { return Phi;
unsigned Idx = IdxEV.first;
for (auto EV : IdxEV.second) {
auto EVHandle = std::get<WeakVH>(EV);
if (!EVHandle)
continue;
int64_t Offset = std::get<int64_t>(EV);
DVI->replaceVariableLocationOp(Idx, EVHandle);
if (Offset) {
SmallVector<uint64_t, 8> Ops;
DIExpression::appendOffset(Ops, Offset);
DbgDIExpr = DIExpression::appendOpsToArg(DbgDIExpr, Ops, Idx, true);
}
DVI->setExpression(DbgDIExpr);
break;
}
} }
} }
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<SCEVAddRecExpr>(PhiSCEV))
if (!Rec->isAffine())
continue;
LLVM_DEBUG(dbgs() << "scev-salvage: Selected IV from loop header: " << Phi
<< " with SCEV: " << *PhiSCEV << "\n");
return &Phi;
}
return nullptr;
} }
static bool ReduceLoopStrength(Loop *L, IVUsers &IU, ScalarEvolution &SE, static bool ReduceLoopStrength(Loop *L, IVUsers &IU, ScalarEvolution &SE,
@ -5948,20 +6259,21 @@ static bool ReduceLoopStrength(Loop *L, IVUsers &IU, ScalarEvolution &SE,
AssumptionCache &AC, TargetLibraryInfo &TLI, AssumptionCache &AC, TargetLibraryInfo &TLI,
MemorySSA *MSSA) { MemorySSA *MSSA) {
// Debug preservation - before we start removing anything identify which DVI
// meet the salvageable criteria and store their DIExpression and SCEVs.
SmallVector<DVIRecoveryRec, 2> SalvageableDVI;
SmallSet<AssertingVH<DbgValueInst>, 2> DVIHandles;
DbgGatherSalvagableDVI(L, SE, SalvageableDVI, DVIHandles);
bool Changed = false; bool Changed = false;
std::unique_ptr<MemorySSAUpdater> MSSAU; std::unique_ptr<MemorySSAUpdater> MSSAU;
if (MSSA) if (MSSA)
MSSAU = std::make_unique<MemorySSAUpdater>(MSSA); MSSAU = std::make_unique<MemorySSAUpdater>(MSSA);
// Run the main LSR transformation. // Run the main LSR transformation.
Changed |= const LSRInstance &Reducer =
LSRInstance(L, IU, SE, DT, LI, TTI, AC, TLI, MSSAU.get()).getChanged(); LSRInstance(L, IU, SE, DT, LI, TTI, AC, TLI, MSSAU.get());
Changed |= Reducer.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);
// Remove any extra phis created by processing inner loops. // Remove any extra phis created by processing inner loops.
Changed |= DeleteDeadPHIs(L->getHeader(), &TLI, MSSAU.get()); Changed |= DeleteDeadPHIs(L->getHeader(), &TLI, MSSAU.get());
@ -5981,8 +6293,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; return Changed;
} }

View File

@ -1393,9 +1393,10 @@ SCEVExpander::getAddRecExprPHILiterally(const SCEVAddRecExpr *Normalized,
// can ensure that IVIncrement dominates the current uses. // can ensure that IVIncrement dominates the current uses.
PostIncLoops = SavedPostIncLoops; 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); InsertedValues.insert(PN);
InsertedIVs.push_back(PN);
return PN; return PN;
} }

View File

@ -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 %i.06, metadata !14, metadata !DIExpression()), !dbg !17
call void @llvm.dbg.value(metadata i8* %p.addr.05, metadata !13, metadata !DIExpression()), !dbg !16 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-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 %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 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-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 store i8 %i.06, i8* %add.ptr, align 1, !dbg !23, !tbaa !24
%inc = add nuw nsw i8 %i.06, 1, !dbg !27 %inc = add nuw nsw i8 %i.06, 1, !dbg !27
call void @llvm.dbg.value(metadata i8 %inc, metadata !14, metadata !DIExpression()), !dbg !17 call void @llvm.dbg.value(metadata i8 %inc, metadata !14, metadata !DIExpression()), !dbg !17

View File

@ -2,7 +2,7 @@
; Test that LSR does not produce invalid debug info when a debug value is ; Test that LSR does not produce invalid debug info when a debug value is
; salvaged during LSR by adding additional location operands, then becomes ; 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" 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 ] %Result.addr.0 = phi i32 [ %Result, %entry ], [ %or, %do.body ]
%Itr.0 = phi i32 [ 0, %entry ], [ %add, %do.body ], !dbg !17 %Itr.0 = phi i32 [ 0, %entry ], [ %add, %do.body ], !dbg !17
; CHECK-NOT: call void @llvm.dbg.value(metadata !DIArgList ; 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 %Itr.0, metadata !16, metadata !DIExpression()), !dbg !17
call void @llvm.dbg.value(metadata i32 %Result.addr.0, metadata !12, 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 %add = add nsw i32 %Itr.0, %Step, !dbg !19

View File

@ -0,0 +1,90 @@
; RUN: opt -S -loop-reduce %s -o - | FileCheck %s
;; 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}

View File

@ -0,0 +1,88 @@
; RUN: opt -S -loop-reduce %s -o - | FileCheck %s
;; 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}

View File

@ -0,0 +1,85 @@
; RUN: opt -S -loop-reduce %s -o - | FileCheck %s
;; 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}

View File

@ -0,0 +1,86 @@
; RUN: opt -S -loop-reduce %s -o - | FileCheck %s
;; 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}

View File

@ -0,0 +1,171 @@
; RUN: opt -S -loop-reduce %s -o - | FileCheck %s
;; 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"}