[LSR] Canonicalize formula and put recursive Reg related with current loop in ScaledReg.

After rL294814, LSR formula can have multiple SCEVAddRecExprs inside of its BaseRegs.
Previous canonicalization will swap the first SCEVAddRecExpr in BaseRegs with ScaledReg.
But now we want to swap the SCEVAddRecExpr Reg related with current loop with ScaledReg.
Otherwise, we may generate code like this: RegA + lsr.iv + RegB, where loop invariant
parts RegA and RegB are not grouped together and cannot be promoted outside of loop.
With this patch, it will ensure lsr.iv to be generated later in the expr:
RegA + RegB + lsr.iv, so that RegA + RegB can be promoted outside of loop.

Differential Revision: https://reviews.llvm.org/D26781

llvm-svn: 295884
This commit is contained in:
Wei Mi 2017-02-22 21:47:08 +00:00
parent 3596a81c69
commit 74d5a90fa6
2 changed files with 148 additions and 39 deletions

View File

@ -307,9 +307,13 @@ struct Formula {
/// canonical representation of a formula is /// canonical representation of a formula is
/// 1. BaseRegs.size > 1 implies ScaledReg != NULL and /// 1. BaseRegs.size > 1 implies ScaledReg != NULL and
/// 2. ScaledReg != NULL implies Scale != 1 || !BaseRegs.empty(). /// 2. ScaledReg != NULL implies Scale != 1 || !BaseRegs.empty().
/// 3. The reg containing recurrent expr related with currect loop in the
/// formula should be put in the ScaledReg.
/// #1 enforces that the scaled register is always used when at least two /// #1 enforces that the scaled register is always used when at least two
/// registers are needed by the formula: e.g., reg1 + reg2 is reg1 + 1 * reg2. /// registers are needed by the formula: e.g., reg1 + reg2 is reg1 + 1 * reg2.
/// #2 enforces that 1 * reg is reg. /// #2 enforces that 1 * reg is reg.
/// #3 ensures invariant regs with respect to current loop can be combined
/// together in LSR codegen.
/// This invariant can be temporarly broken while building a formula. /// This invariant can be temporarly broken while building a formula.
/// However, every formula inserted into the LSRInstance must be in canonical /// However, every formula inserted into the LSRInstance must be in canonical
/// form. /// form.
@ -330,9 +334,9 @@ struct Formula {
void initialMatch(const SCEV *S, Loop *L, ScalarEvolution &SE); void initialMatch(const SCEV *S, Loop *L, ScalarEvolution &SE);
bool isCanonical() const; bool isCanonical(const Loop &L) const;
void canonicalize(); void canonicalize(const Loop &L);
bool unscale(); bool unscale();
@ -424,16 +428,35 @@ void Formula::initialMatch(const SCEV *S, Loop *L, ScalarEvolution &SE) {
BaseRegs.push_back(Sum); BaseRegs.push_back(Sum);
HasBaseReg = true; HasBaseReg = true;
} }
canonicalize(); canonicalize(*L);
} }
/// \brief Check whether or not this formula statisfies the canonical /// \brief Check whether or not this formula statisfies the canonical
/// representation. /// representation.
/// \see Formula::BaseRegs. /// \see Formula::BaseRegs.
bool Formula::isCanonical() const { bool Formula::isCanonical(const Loop &L) const {
if (ScaledReg) if (!ScaledReg)
return Scale != 1 || !BaseRegs.empty(); return BaseRegs.size() <= 1;
return BaseRegs.size() <= 1;
if (Scale != 1)
return true;
if (Scale == 1 && BaseRegs.empty())
return false;
const SCEVAddRecExpr *SAR = dyn_cast<const SCEVAddRecExpr>(ScaledReg);
if (SAR && SAR->getLoop() == &L)
return true;
// If ScaledReg is not a recurrent expr, or it is but its loop is not current
// loop, meanwhile BaseRegs contains a recurrent expr reg related with current
// loop, we want to swap the reg in BaseRegs with ScaledReg.
auto I =
find_if(make_range(BaseRegs.begin(), BaseRegs.end()), [&](const SCEV *S) {
return isa<const SCEVAddRecExpr>(S) &&
(cast<SCEVAddRecExpr>(S)->getLoop() == &L);
});
return I == BaseRegs.end();
} }
/// \brief Helper method to morph a formula into its canonical representation. /// \brief Helper method to morph a formula into its canonical representation.
@ -442,21 +465,33 @@ bool Formula::isCanonical() const {
/// field. Otherwise, we would have to do special cases everywhere in LSR /// field. Otherwise, we would have to do special cases everywhere in LSR
/// to treat reg1 + reg2 + ... the same way as reg1 + 1*reg2 + ... /// to treat reg1 + reg2 + ... the same way as reg1 + 1*reg2 + ...
/// On the other hand, 1*reg should be canonicalized into reg. /// On the other hand, 1*reg should be canonicalized into reg.
void Formula::canonicalize() { void Formula::canonicalize(const Loop &L) {
if (isCanonical()) if (isCanonical(L))
return; return;
// So far we did not need this case. This is easy to implement but it is // So far we did not need this case. This is easy to implement but it is
// useless to maintain dead code. Beside it could hurt compile time. // useless to maintain dead code. Beside it could hurt compile time.
assert(!BaseRegs.empty() && "1*reg => reg, should not be needed."); assert(!BaseRegs.empty() && "1*reg => reg, should not be needed.");
// Keep the invariant sum in BaseRegs and one of the variant sum in ScaledReg. // Keep the invariant sum in BaseRegs and one of the variant sum in ScaledReg.
ScaledReg = BaseRegs.back(); if (!ScaledReg) {
BaseRegs.pop_back(); ScaledReg = BaseRegs.back();
Scale = 1; BaseRegs.pop_back();
size_t BaseRegsSize = BaseRegs.size(); Scale = 1;
size_t Try = 0; }
// If ScaledReg is an invariant, try to find a variant expression.
while (Try < BaseRegsSize && !isa<SCEVAddRecExpr>(ScaledReg)) // If ScaledReg is an invariant with respect to L, find the reg from
std::swap(ScaledReg, BaseRegs[Try++]); // BaseRegs containing the recurrent expr related with Loop L. Swap the
// reg with ScaledReg.
const SCEVAddRecExpr *SAR = dyn_cast<const SCEVAddRecExpr>(ScaledReg);
if (!SAR || SAR->getLoop() != &L) {
auto I = find_if(make_range(BaseRegs.begin(), BaseRegs.end()),
[&](const SCEV *S) {
return isa<const SCEVAddRecExpr>(S) &&
(cast<SCEVAddRecExpr>(S)->getLoop() == &L);
});
if (I != BaseRegs.end())
std::swap(ScaledReg, *I);
}
} }
/// \brief Get rid of the scale in the formula. /// \brief Get rid of the scale in the formula.
@ -908,7 +943,8 @@ static bool isAMCompletelyFolded(const TargetTransformInfo &TTI,
const LSRUse &LU, const Formula &F); const LSRUse &LU, const Formula &F);
// Get the cost of the scaling factor used in F for LU. // Get the cost of the scaling factor used in F for LU.
static unsigned getScalingFactorCost(const TargetTransformInfo &TTI, static unsigned getScalingFactorCost(const TargetTransformInfo &TTI,
const LSRUse &LU, const Formula &F); const LSRUse &LU, const Formula &F,
const Loop &L);
namespace { namespace {
@ -1102,7 +1138,7 @@ public:
bool HasFormulaWithSameRegs(const Formula &F) const; bool HasFormulaWithSameRegs(const Formula &F) const;
float getNotSelectedProbability(const SCEV *Reg) const; float getNotSelectedProbability(const SCEV *Reg) const;
bool InsertFormula(const Formula &F); bool InsertFormula(const Formula &F, const Loop &L);
void DeleteFormula(Formula &F); void DeleteFormula(Formula &F);
void RecomputeRegs(size_t LUIdx, RegUseTracker &Reguses); void RecomputeRegs(size_t LUIdx, RegUseTracker &Reguses);
@ -1191,7 +1227,7 @@ void Cost::RateFormula(const TargetTransformInfo &TTI,
ScalarEvolution &SE, DominatorTree &DT, ScalarEvolution &SE, DominatorTree &DT,
const LSRUse &LU, const LSRUse &LU,
SmallPtrSetImpl<const SCEV *> *LoserRegs) { SmallPtrSetImpl<const SCEV *> *LoserRegs) {
assert(F.isCanonical() && "Cost is accurate only for canonical formula"); assert(F.isCanonical(*L) && "Cost is accurate only for canonical formula");
// Tally up the registers. // Tally up the registers.
unsigned PrevAddRecCost = AddRecCost; unsigned PrevAddRecCost = AddRecCost;
unsigned PrevNumRegs = NumRegs; unsigned PrevNumRegs = NumRegs;
@ -1237,7 +1273,7 @@ void Cost::RateFormula(const TargetTransformInfo &TTI,
NumBaseAdds += (F.UnfoldedOffset != 0); NumBaseAdds += (F.UnfoldedOffset != 0);
// Accumulate non-free scaling amounts. // Accumulate non-free scaling amounts.
ScaleCost += getScalingFactorCost(TTI, LU, F); ScaleCost += getScalingFactorCost(TTI, LU, F, *L);
// Tally up the non-zero immediates. // Tally up the non-zero immediates.
for (const LSRFixup &Fixup : LU.Fixups) { for (const LSRFixup &Fixup : LU.Fixups) {
@ -1391,8 +1427,8 @@ float LSRUse::getNotSelectedProbability(const SCEV *Reg) const {
/// If the given formula has not yet been inserted, add it to the list, and /// If the given formula has not yet been inserted, add it to the list, and
/// return true. Return false otherwise. The formula must be in canonical form. /// return true. Return false otherwise. The formula must be in canonical form.
bool LSRUse::InsertFormula(const Formula &F) { bool LSRUse::InsertFormula(const Formula &F, const Loop &L) {
assert(F.isCanonical() && "Invalid canonical representation"); assert(F.isCanonical(L) && "Invalid canonical representation");
if (!Formulae.empty() && RigidFormula) if (!Formulae.empty() && RigidFormula)
return false; return false;
@ -1562,7 +1598,7 @@ static bool isAMCompletelyFolded(const TargetTransformInfo &TTI,
static bool isAMCompletelyFolded(const TargetTransformInfo &TTI, static bool isAMCompletelyFolded(const TargetTransformInfo &TTI,
int64_t MinOffset, int64_t MaxOffset, int64_t MinOffset, int64_t MaxOffset,
LSRUse::KindType Kind, MemAccessTy AccessTy, LSRUse::KindType Kind, MemAccessTy AccessTy,
const Formula &F) { const Formula &F, const Loop &L) {
// For the purpose of isAMCompletelyFolded either having a canonical formula // For the purpose of isAMCompletelyFolded either having a canonical formula
// or a scale not equal to zero is correct. // or a scale not equal to zero is correct.
// Problems may arise from non canonical formulae having a scale == 0. // Problems may arise from non canonical formulae having a scale == 0.
@ -1570,7 +1606,7 @@ static bool isAMCompletelyFolded(const TargetTransformInfo &TTI,
// However, when we generate the scaled formulae, we first check that the // However, when we generate the scaled formulae, we first check that the
// scaling factor is profitable before computing the actual ScaledReg for // scaling factor is profitable before computing the actual ScaledReg for
// compile time sake. // compile time sake.
assert((F.isCanonical() || F.Scale != 0)); assert((F.isCanonical(L) || F.Scale != 0));
return isAMCompletelyFolded(TTI, MinOffset, MaxOffset, Kind, AccessTy, return isAMCompletelyFolded(TTI, MinOffset, MaxOffset, Kind, AccessTy,
F.BaseGV, F.BaseOffset, F.HasBaseReg, F.Scale); F.BaseGV, F.BaseOffset, F.HasBaseReg, F.Scale);
} }
@ -1605,14 +1641,15 @@ static bool isAMCompletelyFolded(const TargetTransformInfo &TTI,
} }
static unsigned getScalingFactorCost(const TargetTransformInfo &TTI, static unsigned getScalingFactorCost(const TargetTransformInfo &TTI,
const LSRUse &LU, const Formula &F) { const LSRUse &LU, const Formula &F,
const Loop &L) {
if (!F.Scale) if (!F.Scale)
return 0; return 0;
// If the use is not completely folded in that instruction, we will have to // If the use is not completely folded in that instruction, we will have to
// pay an extra cost only for scale != 1. // pay an extra cost only for scale != 1.
if (!isAMCompletelyFolded(TTI, LU.MinOffset, LU.MaxOffset, LU.Kind, if (!isAMCompletelyFolded(TTI, LU.MinOffset, LU.MaxOffset, LU.Kind,
LU.AccessTy, F)) LU.AccessTy, F, L))
return F.Scale != 1; return F.Scale != 1;
switch (LU.Kind) { switch (LU.Kind) {
@ -3206,7 +3243,8 @@ bool LSRInstance::InsertFormula(LSRUse &LU, unsigned LUIdx, const Formula &F) {
// Do not insert formula that we will not be able to expand. // Do not insert formula that we will not be able to expand.
assert(isLegalUse(TTI, LU.MinOffset, LU.MaxOffset, LU.Kind, LU.AccessTy, F) && assert(isLegalUse(TTI, LU.MinOffset, LU.MaxOffset, LU.Kind, LU.AccessTy, F) &&
"Formula is illegal"); "Formula is illegal");
if (!LU.InsertFormula(F))
if (!LU.InsertFormula(F, *L))
return false; return false;
CountRegisters(F, LUIdx); CountRegisters(F, LUIdx);
@ -3445,7 +3483,7 @@ void LSRInstance::GenerateReassociationsImpl(LSRUse &LU, unsigned LUIdx,
F.BaseRegs.push_back(*J); F.BaseRegs.push_back(*J);
// We may have changed the number of register in base regs, adjust the // We may have changed the number of register in base regs, adjust the
// formula accordingly. // formula accordingly.
F.canonicalize(); F.canonicalize(*L);
if (InsertFormula(LU, LUIdx, F)) if (InsertFormula(LU, LUIdx, F))
// If that formula hadn't been seen before, recurse to find more like // If that formula hadn't been seen before, recurse to find more like
@ -3457,7 +3495,7 @@ void LSRInstance::GenerateReassociationsImpl(LSRUse &LU, unsigned LUIdx,
/// Split out subexpressions from adds and the bases of addrecs. /// Split out subexpressions from adds and the bases of addrecs.
void LSRInstance::GenerateReassociations(LSRUse &LU, unsigned LUIdx, void LSRInstance::GenerateReassociations(LSRUse &LU, unsigned LUIdx,
Formula Base, unsigned Depth) { Formula Base, unsigned Depth) {
assert(Base.isCanonical() && "Input must be in the canonical form"); assert(Base.isCanonical(*L) && "Input must be in the canonical form");
// Arbitrarily cap recursion to protect compile time. // Arbitrarily cap recursion to protect compile time.
if (Depth >= 3) if (Depth >= 3)
return; return;
@ -3498,7 +3536,7 @@ void LSRInstance::GenerateCombinations(LSRUse &LU, unsigned LUIdx,
// rather than proceed with zero in a register. // rather than proceed with zero in a register.
if (!Sum->isZero()) { if (!Sum->isZero()) {
F.BaseRegs.push_back(Sum); F.BaseRegs.push_back(Sum);
F.canonicalize(); F.canonicalize(*L);
(void)InsertFormula(LU, LUIdx, F); (void)InsertFormula(LU, LUIdx, F);
} }
} }
@ -3555,7 +3593,7 @@ void LSRInstance::GenerateConstantOffsetsImpl(
F.ScaledReg = nullptr; F.ScaledReg = nullptr;
} else } else
F.deleteBaseReg(F.BaseRegs[Idx]); F.deleteBaseReg(F.BaseRegs[Idx]);
F.canonicalize(); F.canonicalize(*L);
} else if (IsScaledReg) } else if (IsScaledReg)
F.ScaledReg = NewG; F.ScaledReg = NewG;
else else
@ -3718,10 +3756,10 @@ void LSRInstance::GenerateScales(LSRUse &LU, unsigned LUIdx, Formula Base) {
if (LU.Kind == LSRUse::ICmpZero && if (LU.Kind == LSRUse::ICmpZero &&
!Base.HasBaseReg && Base.BaseOffset == 0 && !Base.BaseGV) !Base.HasBaseReg && Base.BaseOffset == 0 && !Base.BaseGV)
continue; continue;
// For each addrec base reg, apply the scale, if possible. // For each addrec base reg, if its loop is current loop, apply the scale.
for (size_t i = 0, e = Base.BaseRegs.size(); i != e; ++i) for (size_t i = 0, e = Base.BaseRegs.size(); i != e; ++i) {
if (const SCEVAddRecExpr *AR = const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(Base.BaseRegs[i]);
dyn_cast<SCEVAddRecExpr>(Base.BaseRegs[i])) { if (AR && (AR->getLoop() == L || LU.AllFixupsOutsideLoop)) {
const SCEV *FactorS = SE.getConstant(IntTy, Factor); const SCEV *FactorS = SE.getConstant(IntTy, Factor);
if (FactorS->isZero()) if (FactorS->isZero())
continue; continue;
@ -3735,11 +3773,17 @@ void LSRInstance::GenerateScales(LSRUse &LU, unsigned LUIdx, Formula Base) {
// The canonical representation of 1*reg is reg, which is already in // The canonical representation of 1*reg is reg, which is already in
// Base. In that case, do not try to insert the formula, it will be // Base. In that case, do not try to insert the formula, it will be
// rejected anyway. // rejected anyway.
if (F.Scale == 1 && F.BaseRegs.empty()) if (F.Scale == 1 && (F.BaseRegs.empty() ||
(AR->getLoop() != L && LU.AllFixupsOutsideLoop)))
continue; continue;
// If AllFixupsOutsideLoop is true and F.Scale is 1, we may generate
// non canonical Formula with ScaledReg's loop not being L.
if (F.Scale == 1 && LU.AllFixupsOutsideLoop)
F.canonicalize(*L);
(void)InsertFormula(LU, LUIdx, F); (void)InsertFormula(LU, LUIdx, F);
} }
} }
}
} }
} }
@ -3920,7 +3964,7 @@ void LSRInstance::GenerateCrossUseConstantOffsets() {
continue; continue;
// OK, looks good. // OK, looks good.
NewF.canonicalize(); NewF.canonicalize(*this->L);
(void)InsertFormula(LU, LUIdx, NewF); (void)InsertFormula(LU, LUIdx, NewF);
} else { } else {
// Use the immediate in a base register. // Use the immediate in a base register.
@ -3952,7 +3996,7 @@ void LSRInstance::GenerateCrossUseConstantOffsets() {
goto skip_formula; goto skip_formula;
// Ok, looks good. // Ok, looks good.
NewF.canonicalize(); NewF.canonicalize(*this->L);
(void)InsertFormula(LU, LUIdx, NewF); (void)InsertFormula(LU, LUIdx, NewF);
break; break;
skip_formula:; skip_formula:;

View File

@ -0,0 +1,65 @@
; RUN: opt -mtriple=x86_64-unknown-linux-gnu -loop-reduce -S < %s | FileCheck %s
; Check LSR formula canonicalization will put loop invariant regs before
; induction variable of current loop, so exprs involving loop invariant regs
; can be promoted outside of current loop.
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define void @foo(i32 %size, i32 %nsteps, i8* nocapture %maxarray, i8* nocapture readnone %buffer, i32 %init) local_unnamed_addr #0 {
entry:
%cmp25 = icmp sgt i32 %nsteps, 0
br i1 %cmp25, label %for.cond1.preheader.lr.ph, label %for.end12
for.cond1.preheader.lr.ph: ; preds = %entry
%cmp223 = icmp sgt i32 %size, 1
%t0 = sext i32 %init to i64
%wide.trip.count = zext i32 %size to i64
%wide.trip.count31 = zext i32 %nsteps to i64
br label %for.cond1.preheader
for.cond1.preheader: ; preds = %for.inc10, %for.cond1.preheader.lr.ph
%indvars.iv28 = phi i64 [ 0, %for.cond1.preheader.lr.ph ], [ %indvars.iv.next29, %for.inc10 ]
br i1 %cmp223, label %for.body3.lr.ph, label %for.inc10
for.body3.lr.ph: ; preds = %for.cond1.preheader
%t1 = add nsw i64 %indvars.iv28, %t0
%t2 = trunc i64 %indvars.iv28 to i8
br label %for.body3
; Make sure loop invariant items are grouped together so that load address can
; be represented in one getelementptr.
; CHECK-LABEL: for.body3:
; CHECK-NEXT: [[LSR:%[^,]+]] = phi i64 [ 1, %for.body3.lr.ph ], [ {{.*}}, %for.body3 ]
; CHECK-NOT: = phi i64
; CHECK-NEXT: [[LOADADDR:%[^,]+]] = getelementptr i8, i8* {{.*}}, i64 [[LSR]]
; CHECK-NEXT: = load i8, i8* [[LOADADDR]], align 1
; CHECK: br i1 %exitcond, label %for.inc10.loopexit, label %for.body3
for.body3: ; preds = %for.body3, %for.body3.lr.ph
%indvars.iv = phi i64 [ 1, %for.body3.lr.ph ], [ %indvars.iv.next, %for.body3 ]
%t5 = trunc i64 %indvars.iv to i8
%t3 = add nsw i64 %t1, %indvars.iv
%arrayidx = getelementptr inbounds i8, i8* %maxarray, i64 %t3
%t4 = load i8, i8* %arrayidx, align 1
%add5 = add i8 %t4, %t5
%add6 = add i8 %add5, %t2
%arrayidx9 = getelementptr inbounds i8, i8* %maxarray, i64 %indvars.iv
store i8 %add6, i8* %arrayidx9, align 1
%indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
%exitcond = icmp eq i64 %indvars.iv.next, %wide.trip.count
br i1 %exitcond, label %for.inc10.loopexit, label %for.body3
for.inc10.loopexit: ; preds = %for.body3
br label %for.inc10
for.inc10: ; preds = %for.inc10.loopexit, %for.cond1.preheader
%indvars.iv.next29 = add nuw nsw i64 %indvars.iv28, 1
%exitcond32 = icmp eq i64 %indvars.iv.next29, %wide.trip.count31
br i1 %exitcond32, label %for.end12.loopexit, label %for.cond1.preheader
for.end12.loopexit: ; preds = %for.inc10
br label %for.end12
for.end12: ; preds = %for.end12.loopexit, %entry
ret void
}