Skip to content

Commit 85c9883

Browse files
author
Max Kazantsev
committed
[SCEV][NFC] Introduces expression sizes estimation
This patch introduces the field `ExpressionSize` in SCEV. This field is calculated only once on SCEV creation, and it represents the complexity of this SCEV from arithmetical point of view (not from the point of the number of actual different SCEV nodes that are used in the expression). Roughly saying, it is the number of operands and operations symbols when we print this SCEV. A formal definition is following: if SCEV `X` has operands `Op1`, `Op2`, ..., `OpN`, then Size(X) = 1 + Size(Op1) + Size(Op2) + ... + Size(OpN). Size of SCEVConstant and SCEVUnknown is one. Expression size may be used as a universal way to limit SCEV transformations for huge SCEVs. Currently, we have a bunch of options that represents various limits (such as recursion depth limit) that may not make any sense from the point of view of a LLVM users who is not familiar with SCEV internals, and all these different options pursue one goal. A more general rule that may potentially allow us to get rid of this redundancy in options is "do not make transformations with SCEVs of huge size". It can apply to all SCEV traversals and transformations that may need to visit a SCEV node more than once, hence they are prone to combinatorial explosions. This patch only introduces SCEV sizes calculation as NFC, its utilization will be introduced in follow-up patches. Differential Revision: https://reviews.llvm.org/D35989 Reviewed By: reames llvm-svn: 351725
1 parent 5e8798f commit 85c9883

File tree

4 files changed

+86
-10
lines changed

4 files changed

+86
-10
lines changed

llvm/include/llvm/Analysis/ScalarEvolution.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ class SCEV : public FoldingSetNode {
8484
const unsigned short SCEVType;
8585

8686
protected:
87+
// Estimated complexity of this node's expression tree size.
88+
const unsigned short ExpressionSize;
89+
8790
/// This field is initialized to zero and may be used in subclasses to store
8891
/// miscellaneous information.
8992
unsigned short SubclassData = 0;
@@ -115,8 +118,9 @@ class SCEV : public FoldingSetNode {
115118
NoWrapMask = (1 << 3) - 1
116119
};
117120

118-
explicit SCEV(const FoldingSetNodeIDRef ID, unsigned SCEVTy)
119-
: FastID(ID), SCEVType(SCEVTy) {}
121+
explicit SCEV(const FoldingSetNodeIDRef ID, unsigned SCEVTy,
122+
unsigned short ExpressionSize)
123+
: FastID(ID), SCEVType(SCEVTy), ExpressionSize(ExpressionSize) {}
120124
SCEV(const SCEV &) = delete;
121125
SCEV &operator=(const SCEV &) = delete;
122126

@@ -137,6 +141,19 @@ class SCEV : public FoldingSetNode {
137141
/// Return true if the specified scev is negated, but not a constant.
138142
bool isNonConstantNegative() const;
139143

144+
// Returns estimated size of the mathematical expression represented by this
145+
// SCEV. The rules of its calculation are following:
146+
// 1) Size of a SCEV without operands (like constants and SCEVUnknown) is 1;
147+
// 2) Size SCEV with operands Op1, Op2, ..., OpN is calculated by formula:
148+
// (1 + Size(Op1) + ... + Size(OpN)).
149+
// This value gives us an estimation of time we need to traverse through this
150+
// SCEV and all its operands recursively. We may use it to avoid performing
151+
// heavy transformations on SCEVs of excessive size for sake of saving the
152+
// compilation time.
153+
unsigned getExpressionSize() const {
154+
return ExpressionSize;
155+
}
156+
140157
/// Print out the internal representation of this scalar to the specified
141158
/// stream. This should really only be used for debugging purposes.
142159
void print(raw_ostream &OS) const;

llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Type;
5050
ConstantInt *V;
5151

5252
SCEVConstant(const FoldingSetNodeIDRef ID, ConstantInt *v) :
53-
SCEV(ID, scConstant), V(v) {}
53+
SCEV(ID, scConstant, 1), V(v) {}
5454

5555
public:
5656
ConstantInt *getValue() const { return V; }
@@ -64,6 +64,13 @@ class Type;
6464
}
6565
};
6666

67+
static unsigned short computeExpressionSize(ArrayRef<const SCEV *> Args) {
68+
APInt Size(16, 1);
69+
for (auto *Arg : Args)
70+
Size = Size.uadd_sat(APInt(16, Arg->getExpressionSize()));
71+
return (unsigned short)Size.getZExtValue();
72+
}
73+
6774
/// This is the base class for unary cast operator classes.
6875
class SCEVCastExpr : public SCEV {
6976
protected:
@@ -141,9 +148,10 @@ class Type;
141148
const SCEV *const *Operands;
142149
size_t NumOperands;
143150

144-
SCEVNAryExpr(const FoldingSetNodeIDRef ID,
145-
enum SCEVTypes T, const SCEV *const *O, size_t N)
146-
: SCEV(ID, T), Operands(O), NumOperands(N) {}
151+
SCEVNAryExpr(const FoldingSetNodeIDRef ID, enum SCEVTypes T,
152+
const SCEV *const *O, size_t N)
153+
: SCEV(ID, T, computeExpressionSize(makeArrayRef(O, N))), Operands(O),
154+
NumOperands(N) {}
147155

148156
public:
149157
size_t getNumOperands() const { return NumOperands; }
@@ -257,7 +265,8 @@ class Type;
257265
const SCEV *RHS;
258266

259267
SCEVUDivExpr(const FoldingSetNodeIDRef ID, const SCEV *lhs, const SCEV *rhs)
260-
: SCEV(ID, scUDivExpr), LHS(lhs), RHS(rhs) {}
268+
: SCEV(ID, scUDivExpr, computeExpressionSize({lhs, rhs})), LHS(lhs),
269+
RHS(rhs) {}
261270

262271
public:
263272
const SCEV *getLHS() const { return LHS; }
@@ -410,7 +419,7 @@ class Type;
410419

411420
SCEVUnknown(const FoldingSetNodeIDRef ID, Value *V,
412421
ScalarEvolution *se, SCEVUnknown *next) :
413-
SCEV(ID, scUnknown), CallbackVH(V), SE(se), Next(next) {}
422+
SCEV(ID, scUnknown, 1), CallbackVH(V), SE(se), Next(next) {}
414423

415424
// Implement CallbackVH.
416425
void deleted() override;

llvm/lib/Analysis/ScalarEvolution.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ bool SCEV::isNonConstantNegative() const {
392392
}
393393

394394
SCEVCouldNotCompute::SCEVCouldNotCompute() :
395-
SCEV(FoldingSetNodeIDRef(), scCouldNotCompute) {}
395+
SCEV(FoldingSetNodeIDRef(), scCouldNotCompute, 0) {}
396396

397397
bool SCEVCouldNotCompute::classof(const SCEV *S) {
398398
return S->getSCEVType() == scCouldNotCompute;
@@ -421,7 +421,7 @@ ScalarEvolution::getConstant(Type *Ty, uint64_t V, bool isSigned) {
421421

422422
SCEVCastExpr::SCEVCastExpr(const FoldingSetNodeIDRef ID,
423423
unsigned SCEVTy, const SCEV *op, Type *ty)
424-
: SCEV(ID, SCEVTy), Op(op), Ty(ty) {}
424+
: SCEV(ID, SCEVTy, computeExpressionSize(op)), Op(op), Ty(ty) {}
425425

426426
SCEVTruncateExpr::SCEVTruncateExpr(const FoldingSetNodeIDRef ID,
427427
const SCEV *op, Type *ty)

llvm/unittests/Analysis/ScalarEvolutionTest.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,5 +1389,55 @@ TEST_F(ScalarEvolutionsTest, SCEVCacheNSW) {
13891389
EXPECT_FALSE(I->hasNoSignedWrap());
13901390
}
13911391

1392+
// Check logic of SCEV expression size computation.
1393+
TEST_F(ScalarEvolutionsTest, SCEVComputeExpressionSize) {
1394+
/*
1395+
* Create the following code:
1396+
* void func(i64 %a, i64 %b)
1397+
* entry:
1398+
* %s1 = add i64 %a, 1
1399+
* %s2 = udiv i64 %s1, %b
1400+
* br label %exit
1401+
* exit:
1402+
* ret
1403+
*/
1404+
1405+
// Create a module.
1406+
Module M("SCEVComputeExpressionSize", Context);
1407+
1408+
Type *T_int64 = Type::getInt64Ty(Context);
1409+
1410+
FunctionType *FTy =
1411+
FunctionType::get(Type::getVoidTy(Context), { T_int64, T_int64 }, false);
1412+
Function *F = cast<Function>(M.getOrInsertFunction("func", FTy));
1413+
Argument *A = &*F->arg_begin();
1414+
Argument *B = &*std::next(F->arg_begin());
1415+
ConstantInt *C = ConstantInt::get(Context, APInt(64, 1));
1416+
1417+
BasicBlock *Entry = BasicBlock::Create(Context, "entry", F);
1418+
BasicBlock *Exit = BasicBlock::Create(Context, "exit", F);
1419+
1420+
IRBuilder<> Builder(Entry);
1421+
auto *S1 = cast<Instruction>(Builder.CreateAdd(A, C, "s1"));
1422+
auto *S2 = cast<Instruction>(Builder.CreateUDiv(S1, B, "s2"));
1423+
Builder.CreateBr(Exit);
1424+
1425+
Builder.SetInsertPoint(Exit);
1426+
auto *R = cast<Instruction>(Builder.CreateRetVoid());
1427+
1428+
ScalarEvolution SE = buildSE(*F);
1429+
// Get S2 first to move it to cache.
1430+
const SCEV *AS = SE.getSCEV(A);
1431+
const SCEV *BS = SE.getSCEV(B);
1432+
const SCEV *CS = SE.getSCEV(C);
1433+
const SCEV *S1S = SE.getSCEV(S1);
1434+
const SCEV *S2S = SE.getSCEV(S2);
1435+
EXPECT_EQ(AS->getExpressionSize(), 1);
1436+
EXPECT_EQ(BS->getExpressionSize(), 1);
1437+
EXPECT_EQ(CS->getExpressionSize(), 1);
1438+
EXPECT_EQ(S1S->getExpressionSize(), 3);
1439+
EXPECT_EQ(S2S->getExpressionSize(), 5);
1440+
}
1441+
13921442
} // end anonymous namespace
13931443
} // end namespace llvm

0 commit comments

Comments
 (0)