diff --git a/lib/Analysis/ScalarEvolution.cpp b/lib/Analysis/ScalarEvolution.cpp index a2ab6e63da6..16dc281ae17 100644 --- a/lib/Analysis/ScalarEvolution.cpp +++ b/lib/Analysis/ScalarEvolution.cpp @@ -988,6 +988,102 @@ SCEVHandle ScalarEvolution::getAnyExtendExpr(const SCEVHandle &Op, return ZExt; } +/// CollectAddOperandsWithScales - Process the given Ops list, which is +/// a list of operands to be added under the given scale, update the given +/// map. This is a helper function for getAddRecExpr. As an example of +/// what it does, given a sequence of operands that would form an add +/// expression like this: +/// +/// m + n + 13 + (A * (o + p + (B * q + m + 29))) + r + (-1 * r) +/// +/// where A and B are constants, update the map with these values: +/// +/// (m, 1+A*B), (n, 1), (o, A), (p, A), (q, A*B), (r, 0) +/// +/// and add 13 + A*B*29 to AccumulatedConstant. +/// This will allow getAddRecExpr to produce this: +/// +/// 13+A*B*29 + n + (m * (1+A*B)) + ((o + p) * A) + (q * A*B) +/// +/// This form often exposes folding opportunities that are hidden in +/// the original operand list. +/// +/// Return true iff it appears that any interesting folding opportunities +/// may be exposed. This helps getAddRecExpr short-circuit extra work in +/// the common case where no interesting opportunities are present, and +/// is also used as a check to avoid infinite recursion. +/// +static bool +CollectAddOperandsWithScales(DenseMap &M, + SmallVector &NewOps, + APInt &AccumulatedConstant, + const SmallVectorImpl &Ops, + const APInt &Scale, + ScalarEvolution &SE) { + bool Interesting = false; + + // Iterate over the add operands. + for (unsigned i = 0, e = Ops.size(); i != e; ++i) { + const SCEVMulExpr *Mul = dyn_cast(Ops[i]); + if (Mul && isa(Mul->getOperand(0))) { + APInt NewScale = + Scale * cast(Mul->getOperand(0))->getValue()->getValue(); + if (Mul->getNumOperands() == 2 && isa(Mul->getOperand(1))) { + // A multiplication of a constant with another add; recurse. + Interesting |= + CollectAddOperandsWithScales(M, NewOps, AccumulatedConstant, + cast(Mul->getOperand(1)) + ->getOperands(), + NewScale, SE); + } else { + // A multiplication of a constant with some other value. Update + // the map. + SmallVector MulOps(Mul->op_begin()+1, Mul->op_end()); + SCEVHandle Key = SE.getMulExpr(MulOps); + std::pair::iterator, bool> Pair = + M.insert(std::make_pair(Key, APInt())); + if (Pair.second) { + Pair.first->second = NewScale; + NewOps.push_back(Pair.first->first); + } else { + Pair.first->second += NewScale; + // The map already had an entry for this value, which may indicate + // a folding opportunity. + Interesting = true; + } + } + } else if (const SCEVConstant *C = dyn_cast(Ops[i])) { + // Pull a buried constant out to the outside. + if (Scale != 1 || AccumulatedConstant != 0 || C->isZero()) + Interesting = true; + AccumulatedConstant += Scale * C->getValue()->getValue(); + } else { + // An ordinary operand. Update the map. + std::pair::iterator, bool> Pair = + M.insert(std::make_pair(Ops[i], APInt())); + if (Pair.second) { + Pair.first->second = Scale; + NewOps.push_back(Pair.first->first); + } else { + Pair.first->second += Scale; + // The map already had an entry for this value, which may indicate + // a folding opportunity. + Interesting = true; + } + } + } + + return Interesting; +} + +namespace { + struct APIntCompare { + bool operator()(const APInt &LHS, const APInt &RHS) const { + return LHS.ult(RHS); + } + }; +} + /// getAddExpr - Get a canonical add expression, or something simpler if /// possible. SCEVHandle ScalarEvolution::getAddExpr(SmallVectorImpl &Ops) { @@ -1128,6 +1224,38 @@ SCEVHandle ScalarEvolution::getAddExpr(SmallVectorImpl &Ops) { while (Idx < Ops.size() && Ops[Idx]->getSCEVType() < scMulExpr) ++Idx; + // Check to see if there are any folding opportunities present with + // operands multiplied by constant values. + if (Idx < Ops.size() && isa(Ops[Idx])) { + uint64_t BitWidth = getTypeSizeInBits(Ty); + DenseMap M; + SmallVector NewOps; + APInt AccumulatedConstant(BitWidth, 0); + if (CollectAddOperandsWithScales(M, NewOps, AccumulatedConstant, + Ops, APInt(BitWidth, 1), *this)) { + // Some interesting folding opportunity is present, so its worthwhile to + // re-generate the operands list. Group the operands by constant scale, + // to avoid multiplying by the same constant scale multiple times. + std::map, APIntCompare> MulOpLists; + for (SmallVector::iterator I = NewOps.begin(), + E = NewOps.end(); I != E; ++I) + MulOpLists[M.find(*I)->second].push_back(*I); + // Re-generate the operands list. + Ops.clear(); + if (AccumulatedConstant != 0) + Ops.push_back(getConstant(AccumulatedConstant)); + for (std::map, APIntCompare>::iterator I = + MulOpLists.begin(), E = MulOpLists.end(); I != E; ++I) + if (I->first != 0) + Ops.push_back(getMulExpr(getConstant(I->first), getAddExpr(I->second))); + if (Ops.empty()) + return getIntegerSCEV(0, Ty); + if (Ops.size() == 1) + return Ops[0]; + return getAddExpr(Ops); + } + } + // If we are adding something to a multiply expression, make sure the // something is not already an operand of the multiply. If so, merge it into // the multiply. diff --git a/test/Transforms/IndVarSimplify/pointer.ll b/test/Transforms/IndVarSimplify/pointer.ll new file mode 100644 index 00000000000..7ad11619192 --- /dev/null +++ b/test/Transforms/IndVarSimplify/pointer.ll @@ -0,0 +1,38 @@ +; RUN: llvm-as < %s | opt -indvars | llvm-dis > %t +; RUN: grep {%exitcond = icmp eq i64 %indvar.next, %n} %t +; RUN: grep {getelementptr i8\\* %A, i64 %indvar} %t +; RUN: grep getelementptr %t | count 1 +; RUN: grep add %t | count 1 +; RUN: not grep scevgep %t +; RUN: not grep ptrtoint %t + +; Indvars should be able to expand the pointer-arithmetic +; IV into an integer IV indexing into a simple getelementptr. + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64" + +define void @foo(i8* %A, i64 %n) nounwind { +entry: + %0 = icmp eq i64 %n, 0 ; [#uses=1] + br i1 %0, label %return, label %bb.nph + +bb.nph: ; preds = %entry + %1 = getelementptr i8* %A, i64 %n ; [#uses=1] + br label %bb + +bb: ; preds = %bb1, %bb.nph + %q.01 = phi i8* [ %2, %bb1 ], [ %A, %bb.nph ] ; [#uses=2] + store i8 0, i8* %q.01, align 1 + %2 = getelementptr i8* %q.01, i64 1 ; [#uses=2] + br label %bb1 + +bb1: ; preds = %bb + %3 = icmp eq i8* %1, %2 ; [#uses=1] + br i1 %3, label %bb1.return_crit_edge, label %bb + +bb1.return_crit_edge: ; preds = %bb1 + br label %return + +return: ; preds = %bb1.return_crit_edge, %entry + ret void +}