forked from OSchip/llvm-project
[SCEV] Disable canonical expansion for non-affine addrecs.
Reviewed By: apilipenko Differential Revision: https://reviews.llvm.org/D65276 Patch by Evgeniy Brevnov (ybrevnov@azul.com) llvm-svn: 372789
This commit is contained in:
parent
56eae602da
commit
5c1447cd43
|
@ -77,9 +77,13 @@ namespace llvm {
|
|||
/// Phis that complete an IV chain. Reuse
|
||||
DenseSet<AssertingVH<PHINode>> ChainedPhis;
|
||||
|
||||
/// When true, expressions are expanded in "canonical" form. In particular,
|
||||
/// addrecs are expanded as arithmetic based on a canonical induction
|
||||
/// variable. When false, expression are expanded in a more literal form.
|
||||
/// When true, SCEVExpander tries to expand expressions in "canonical" form.
|
||||
/// When false, expressions are expanded in a more literal form.
|
||||
///
|
||||
/// In "canonical" form addrecs are expanded as arithmetic based on a
|
||||
/// canonical induction variable. Note that CanonicalMode doesn't guarantee
|
||||
/// that all expressions are expanded in "canonical" form. For some
|
||||
/// expressions literal mode can be preferred.
|
||||
bool CanonicalMode;
|
||||
|
||||
/// When invoked from LSR, the expander is in "strength reduction" mode. The
|
||||
|
|
|
@ -1486,7 +1486,18 @@ Value *SCEVExpander::expandAddRecExprLiterally(const SCEVAddRecExpr *S) {
|
|||
}
|
||||
|
||||
Value *SCEVExpander::visitAddRecExpr(const SCEVAddRecExpr *S) {
|
||||
if (!CanonicalMode) return expandAddRecExprLiterally(S);
|
||||
// In canonical mode we compute the addrec as an expression of a canonical IV
|
||||
// using evaluateAtIteration and expand the resulting SCEV expression. This
|
||||
// way we avoid introducing new IVs to carry on the comutation of the addrec
|
||||
// throughout the loop.
|
||||
//
|
||||
// For nested addrecs evaluateAtIteration might need a canonical IV of a
|
||||
// type wider than the addrec itself. Emitting a canonical IV of the
|
||||
// proper type might produce non-legal types, for example expanding an i64
|
||||
// {0,+,2,+,1} addrec would need an i65 canonical IV. To avoid this just fall
|
||||
// back to non-canonical mode for nested addrecs.
|
||||
if (!CanonicalMode || (S->getNumOperands() > 2))
|
||||
return expandAddRecExprLiterally(S);
|
||||
|
||||
Type *Ty = SE.getEffectiveSCEVType(S->getType());
|
||||
const Loop *L = S->getLoop();
|
||||
|
|
|
@ -1745,4 +1745,186 @@ TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) {
|
|||
});
|
||||
}
|
||||
|
||||
// Test expansion of nested addrecs in CanonicalMode.
|
||||
// Expanding nested addrecs in canonical mode requiers a canonical IV of a
|
||||
// type wider than the type of the addrec itself. Currently, SCEVExpander
|
||||
// just falls back to literal mode for nested addrecs.
|
||||
TEST_F(ScalarEvolutionsTest, SCEVExpandNonAffineAddRec) {
|
||||
LLVMContext C;
|
||||
SMDiagnostic Err;
|
||||
|
||||
// Expand the addrec produced by GetAddRec into a loop without a canonical IV.
|
||||
auto TestNoCanonicalIV = [&](std::function<const SCEVAddRecExpr *(
|
||||
ScalarEvolution & SE, Loop * L)> GetAddRec) {
|
||||
std::unique_ptr<Module> M =
|
||||
parseAssemblyString("define i32 @test(i32 %limit) { "
|
||||
"entry: "
|
||||
" br label %loop "
|
||||
"loop: "
|
||||
" %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] "
|
||||
" %i.inc = add nsw i32 %i, 1 "
|
||||
" %cont = icmp slt i32 %i.inc, %limit "
|
||||
" br i1 %cont, label %loop, label %exit "
|
||||
"exit: "
|
||||
" ret i32 %i.inc "
|
||||
"}",
|
||||
Err, C);
|
||||
|
||||
assert(M && "Could not parse module?");
|
||||
assert(!verifyModule(*M) && "Must have been well formed!");
|
||||
|
||||
runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
|
||||
auto &I = GetInstByName(F, "i");
|
||||
auto *Loop = LI.getLoopFor(I.getParent());
|
||||
EXPECT_FALSE(Loop->getCanonicalInductionVariable());
|
||||
|
||||
auto *AR = GetAddRec(SE, Loop);
|
||||
EXPECT_FALSE(AR->isAffine());
|
||||
|
||||
SCEVExpander Exp(SE, M->getDataLayout(), "expander");
|
||||
auto *InsertAt = I.getNextNode();
|
||||
Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt);
|
||||
auto *ExpandedAR = SE.getSCEV(V);
|
||||
// Check that the expansion happened literally.
|
||||
EXPECT_EQ(AR, ExpandedAR);
|
||||
});
|
||||
};
|
||||
|
||||
// Expand the addrec produced by GetAddRec into a loop with a canonical IV
|
||||
// which is narrower than addrec type.
|
||||
auto TestNarrowCanonicalIV = [&](
|
||||
std::function<const SCEVAddRecExpr *(ScalarEvolution & SE, Loop * L)>
|
||||
GetAddRec) {
|
||||
std::unique_ptr<Module> M = parseAssemblyString(
|
||||
"define i32 @test(i32 %limit) { "
|
||||
"entry: "
|
||||
" br label %loop "
|
||||
"loop: "
|
||||
" %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] "
|
||||
" %canonical.iv = phi i8 [ 0, %entry ], [ %canonical.iv.inc, %loop ] "
|
||||
" %i.inc = add nsw i32 %i, 1 "
|
||||
" %canonical.iv.inc = add i8 %canonical.iv, 1 "
|
||||
" %cont = icmp slt i32 %i.inc, %limit "
|
||||
" br i1 %cont, label %loop, label %exit "
|
||||
"exit: "
|
||||
" ret i32 %i.inc "
|
||||
"}",
|
||||
Err, C);
|
||||
|
||||
assert(M && "Could not parse module?");
|
||||
assert(!verifyModule(*M) && "Must have been well formed!");
|
||||
|
||||
runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
|
||||
auto &I = GetInstByName(F, "i");
|
||||
|
||||
auto *LoopHeaderBB = I.getParent();
|
||||
auto *Loop = LI.getLoopFor(LoopHeaderBB);
|
||||
PHINode *CanonicalIV = Loop->getCanonicalInductionVariable();
|
||||
EXPECT_EQ(CanonicalIV, &GetInstByName(F, "canonical.iv"));
|
||||
|
||||
auto *AR = GetAddRec(SE, Loop);
|
||||
EXPECT_FALSE(AR->isAffine());
|
||||
|
||||
unsigned ExpectedCanonicalIVWidth = SE.getTypeSizeInBits(AR->getType());
|
||||
unsigned CanonicalIVBitWidth =
|
||||
cast<IntegerType>(CanonicalIV->getType())->getBitWidth();
|
||||
EXPECT_LT(CanonicalIVBitWidth, ExpectedCanonicalIVWidth);
|
||||
|
||||
SCEVExpander Exp(SE, M->getDataLayout(), "expander");
|
||||
auto *InsertAt = I.getNextNode();
|
||||
Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt);
|
||||
auto *ExpandedAR = SE.getSCEV(V);
|
||||
// Check that the expansion happened literally.
|
||||
EXPECT_EQ(AR, ExpandedAR);
|
||||
});
|
||||
};
|
||||
|
||||
// Expand the addrec produced by GetAddRec into a loop with a canonical IV
|
||||
// of addrec width.
|
||||
auto TestMatchingCanonicalIV = [&](
|
||||
std::function<const SCEVAddRecExpr *(ScalarEvolution & SE, Loop * L)>
|
||||
GetAddRec,
|
||||
unsigned ARBitWidth) {
|
||||
auto ARBitWidthTypeStr = "i" + std::to_string(ARBitWidth);
|
||||
std::unique_ptr<Module> M = parseAssemblyString(
|
||||
"define i32 @test(i32 %limit) { "
|
||||
"entry: "
|
||||
" br label %loop "
|
||||
"loop: "
|
||||
" %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] "
|
||||
" %canonical.iv = phi " + ARBitWidthTypeStr +
|
||||
" [ 0, %entry ], [ %canonical.iv.inc, %loop ] "
|
||||
" %i.inc = add nsw i32 %i, 1 "
|
||||
" %canonical.iv.inc = add " + ARBitWidthTypeStr +
|
||||
" %canonical.iv, 1 "
|
||||
" %cont = icmp slt i32 %i.inc, %limit "
|
||||
" br i1 %cont, label %loop, label %exit "
|
||||
"exit: "
|
||||
" ret i32 %i.inc "
|
||||
"}",
|
||||
Err, C);
|
||||
|
||||
assert(M && "Could not parse module?");
|
||||
assert(!verifyModule(*M) && "Must have been well formed!");
|
||||
|
||||
runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
|
||||
auto &I = GetInstByName(F, "i");
|
||||
auto &CanonicalIV = GetInstByName(F, "canonical.iv");
|
||||
|
||||
auto *LoopHeaderBB = I.getParent();
|
||||
auto *Loop = LI.getLoopFor(LoopHeaderBB);
|
||||
EXPECT_EQ(&CanonicalIV, Loop->getCanonicalInductionVariable());
|
||||
unsigned CanonicalIVBitWidth =
|
||||
cast<IntegerType>(CanonicalIV.getType())->getBitWidth();
|
||||
|
||||
auto *AR = GetAddRec(SE, Loop);
|
||||
EXPECT_FALSE(AR->isAffine());
|
||||
EXPECT_EQ(ARBitWidth, SE.getTypeSizeInBits(AR->getType()));
|
||||
EXPECT_EQ(CanonicalIVBitWidth, ARBitWidth);
|
||||
|
||||
SCEVExpander Exp(SE, M->getDataLayout(), "expander");
|
||||
auto *InsertAt = I.getNextNode();
|
||||
Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt);
|
||||
auto *ExpandedAR = SE.getSCEV(V);
|
||||
// Check that the expansion happened literally.
|
||||
EXPECT_EQ(AR, ExpandedAR);
|
||||
});
|
||||
};
|
||||
|
||||
unsigned ARBitWidth = 16;
|
||||
Type *ARType = IntegerType::get(C, ARBitWidth);
|
||||
|
||||
// Expand {5,+,1,+,1}
|
||||
auto GetAR3 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * {
|
||||
SmallVector<const SCEV *, 3> Ops = {SE.getConstant(APInt(ARBitWidth, 5)),
|
||||
SE.getOne(ARType), SE.getOne(ARType)};
|
||||
return cast<SCEVAddRecExpr>(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap));
|
||||
};
|
||||
TestNoCanonicalIV(GetAR3);
|
||||
TestNarrowCanonicalIV(GetAR3);
|
||||
TestMatchingCanonicalIV(GetAR3, ARBitWidth);
|
||||
|
||||
// Expand {5,+,1,+,1,+,1}
|
||||
auto GetAR4 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * {
|
||||
SmallVector<const SCEV *, 4> Ops = {SE.getConstant(APInt(ARBitWidth, 5)),
|
||||
SE.getOne(ARType), SE.getOne(ARType),
|
||||
SE.getOne(ARType)};
|
||||
return cast<SCEVAddRecExpr>(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap));
|
||||
};
|
||||
TestNoCanonicalIV(GetAR4);
|
||||
TestNarrowCanonicalIV(GetAR4);
|
||||
TestMatchingCanonicalIV(GetAR4, ARBitWidth);
|
||||
|
||||
// Expand {5,+,1,+,1,+,1,+,1}
|
||||
auto GetAR5 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * {
|
||||
SmallVector<const SCEV *, 5> Ops = {SE.getConstant(APInt(ARBitWidth, 5)),
|
||||
SE.getOne(ARType), SE.getOne(ARType),
|
||||
SE.getOne(ARType), SE.getOne(ARType)};
|
||||
return cast<SCEVAddRecExpr>(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap));
|
||||
};
|
||||
TestNoCanonicalIV(GetAR5);
|
||||
TestNarrowCanonicalIV(GetAR5);
|
||||
TestMatchingCanonicalIV(GetAR5, ARBitWidth);
|
||||
}
|
||||
|
||||
} // end namespace llvm
|
||||
|
|
Loading…
Reference in New Issue