`BoundType` is no longer a nested member of `IntegerRelation` but a top-level enum in the `presburger` namespace.
This allows `BoundType` to be predeclared in header files. Nested members cannot be predeclared.
Differential Revision: https://reviews.llvm.org/D146210
///
/// Note: The dimensions/symbols of this FlatLinearConstraints must match the
/// dimensions/symbols of the affine map.
- LogicalResult addBound(BoundType type, unsigned pos, AffineMap boundMap,
- bool isClosedBound);
+ LogicalResult addBound(presburger::BoundType type, unsigned pos,
+ AffineMap boundMap, bool isClosedBound);
/// Adds a bound for the variable at the specified position with constraints
/// being drawn from the specified bound map. In case of an EQ bound, the
/// Note: The dimensions/symbols of this FlatLinearConstraints must match the
/// dimensions/symbols of the affine map. By default the lower bound is closed
/// and the upper bound is open.
- LogicalResult addBound(BoundType type, unsigned pos, AffineMap boundMap);
+ LogicalResult addBound(presburger::BoundType type, unsigned pos,
+ AffineMap boundMap);
/// The `addBound` overload above hides the inherited overloads by default, so
/// we explicitly introduce them here.
void clearAndCopyFrom(const IntegerRelation &other) override;
/// Adds a constant bound for the variable associated with the given Value.
- void addBound(BoundType type, Value val, int64_t value);
+ void addBound(presburger::BoundType type, Value val, int64_t value);
using FlatLinearConstraints::addBound;
/// Returns the Value associated with the pos^th variable. Asserts if
class PresburgerRelation;
struct SymbolicLexMin;
+/// The type of bound: equal, lower bound or upper bound.
+enum class BoundType { EQ, LB, UB };
+
/// An IntegerRelation represents the set of points from a PresburgerSpace that
/// satisfy a list of affine constraints. Affine constraints can be inequalities
/// or equalities in the form:
/// to None.
DivisionRepr getLocalReprs(std::vector<MaybeLocalRepr> *repr = nullptr) const;
- /// The type of bound: equal, lower bound or upper bound.
- enum BoundType { EQ, LB, UB };
-
/// Adds a constant bound for the specified variable.
void addBound(BoundType type, unsigned pos, const MPInt &value);
void addBound(BoundType type, unsigned pos, int64_t value) {
/// EQ bound, the bound map is expected to have exactly one result. In case
/// of a LB/UB, the bound map may have more than one result, for each of which
/// an inequality is added.
- LogicalResult addBound(BoundType type, unsigned pos, AffineMap boundMap,
- ValueRange operands);
+ LogicalResult addBound(presburger::BoundType type, unsigned pos,
+ AffineMap boundMap, ValueRange operands);
using FlatLinearValueConstraints::addBound;
/// Add the specified values as a dim or symbol var depending on its nature,
// Express `var_r` as `var_n % divisor` and store the expression in `memo`.
if (quotientCount >= 1) {
- auto ub = cst.getConstantBound64(FlatLinearConstraints::BoundType::UB,
- dimExpr.getPosition());
+ auto ub = cst.getConstantBound64(BoundType::UB, dimExpr.getPosition());
// If `var_n` has an upperbound that is less than the divisor, mod can be
// eliminated altogether.
if (ub && *ub < divisor)
// outA - outB <= -1
// outA <= outB - 1
// outA < outB
- levelSet.addBound(IntegerPolyhedron::BoundType::UB, subExpr, MPInt(-1));
+ levelSet.addBound(BoundType::UB, subExpr, MPInt(-1));
break;
case OrderingKind::GT:
// For greater than, we add a lower bound of 1:
// outA - outB >= 1
// outA > outB + 1
// outA > outB
- levelSet.addBound(IntegerPolyhedron::BoundType::LB, subExpr, MPInt(1));
+ levelSet.addBound(BoundType::LB, subExpr, MPInt(1));
break;
case OrderingKind::GE:
case OrderingKind::LE:
dependenceComponents->resize(numCommonLoops);
for (unsigned j = 0; j < numCommonLoops; ++j) {
(*dependenceComponents)[j].op = commonLoops[j].getOperation();
- auto lbConst =
- dependenceDomain->getConstantBound64(IntegerPolyhedron::LB, j);
+ auto lbConst = dependenceDomain->getConstantBound64(BoundType::LB, j);
(*dependenceComponents)[j].lb =
lbConst.value_or(std::numeric_limits<int64_t>::min());
- auto ubConst =
- dependenceDomain->getConstantBound64(IntegerPolyhedron::UB, j);
+ auto ubConst = dependenceDomain->getConstantBound64(BoundType::UB, j);
(*dependenceComponents)[j].ub =
ubConst.value_or(std::numeric_limits<int64_t>::max());
}
if (isValidSymbol(value)) {
// Check if the symbol is a constant.
if (auto cOp = value.getDefiningOp<arith::ConstantIndexOp>())
- cst->addBound(FlatAffineValueConstraints::EQ, value, cOp.value());
+ cst->addBound(BoundType::EQ, value, cOp.value());
} else if (auto loop = getForInductionVarOwner(value)) {
if (failed(cst->addAffineForOpDomain(loop)))
return failure();
// that will need non-trivials means to eliminate.
FlatAffineValueConstraints cstWithShapeBounds(cst);
for (unsigned r = 0; r < rank; r++) {
- cstWithShapeBounds.addBound(FlatAffineValueConstraints::LB, r, 0);
+ cstWithShapeBounds.addBound(BoundType::LB, r, 0);
int64_t dimSize = memRefType.getDimSize(r);
if (ShapedType::isDynamic(dimSize))
continue;
- cstWithShapeBounds.addBound(FlatAffineValueConstraints::UB, r, dimSize - 1);
+ cstWithShapeBounds.addBound(BoundType::UB, r, dimSize - 1);
}
// Find a constant upper bound on the extent of this memref region along each
// Check if the symbol is a constant.
Value symbol = operand;
if (auto constVal = getConstantIntValue(symbol))
- cst.addBound(FlatAffineValueConstraints::EQ, symbol, constVal.value());
+ cst.addBound(BoundType::EQ, symbol, constVal.value());
} else {
LLVM_DEBUG(llvm::dbgs() << "unknown affine dimensional value");
return failure();
if (addMemRefDimBounds) {
auto memRefType = memref.getType().cast<MemRefType>();
for (unsigned r = 0; r < rank; r++) {
- cst.addBound(FlatAffineValueConstraints::LB, /*pos=*/r, /*value=*/0);
+ cst.addBound(BoundType::LB, /*pos=*/r, /*value=*/0);
if (memRefType.isDynamicDim(r))
continue;
- cst.addBound(FlatAffineValueConstraints::UB, /*pos=*/r,
- memRefType.getDimSize(r) - 1);
+ cst.addBound(BoundType::UB, /*pos=*/r, memRefType.getDimSize(r) - 1);
}
}
cst.removeTrivialRedundancy();
continue;
// Check for overflow: d_i >= memref dim size.
- ucst.addBound(FlatAffineValueConstraints::LB, r, dimSize);
+ ucst.addBound(BoundType::LB, r, dimSize);
outOfBounds = !ucst.isEmpty();
if (outOfBounds && emitError) {
loadOrStoreOp.emitOpError()
FlatAffineValueConstraints lcst(*region.getConstraints());
std::fill(ineq.begin(), ineq.end(), 0);
// d_i <= -1;
- lcst.addBound(FlatAffineValueConstraints::UB, r, -1);
+ lcst.addBound(BoundType::UB, r, -1);
outOfBounds = !lcst.isEmpty();
if (outOfBounds && emitError) {
loadOrStoreOp.emitOpError()
/// Note: This function adds a new symbol column to the `constraints` for each
/// dimension/symbol that exists in the affine map but not in `constraints`.
static LogicalResult alignAndAddBound(FlatAffineValueConstraints &constraints,
- IntegerPolyhedron::BoundType type,
- unsigned pos, AffineMap map,
- ValueRange operands) {
+ BoundType type, unsigned pos,
+ AffineMap map, ValueRange operands) {
SmallVector<Value> dims, syms, newSyms;
unpackOptionalValues(constraints.getMaybeValues(VarKind::SetDim), dims);
unpackOptionalValues(constraints.getMaybeValues(VarKind::Symbol), syms);
// Add an inequality for each result expr_i of map:
// isMin: op <= expr_i, !isMin: op >= expr_i
- auto boundType = isMin ? IntegerPolyhedron::UB : IntegerPolyhedron::LB;
+ auto boundType = isMin ? BoundType::UB : BoundType::LB;
// Upper bounds are exclusive, so add 1. (`affine.min` ops are inclusive.)
AffineMap mapLbUb = isMin ? addConstToResults(map, 1) : map;
if (failed(
// Add an equality: Set dimOpBound to computed bound.
// Add back dimension for op. (Was removed by `getSliceBounds`.)
AffineMap alignedBoundMap = boundMap.shiftDims(/*shift=*/1, /*offset=*/dimOp);
- if (failed(constraints.addBound(IntegerPolyhedron::EQ, dimOpBound,
- alignedBoundMap)))
+ if (failed(constraints.addBound(BoundType::EQ, dimOpBound, alignedBoundMap)))
return failure();
// If the constraint system is empty, there is an inconsistency. (E.g., this
// Note: These equalities could have been added earlier and used to express
// minOp <= expr_i. However, then we run the risk that `getSliceBounds`
// computes minOpUb in terms of r_i dims, which is not desired.
- if (failed(alignAndAddBound(newConstr, IntegerPolyhedron::EQ, i,
+ if (failed(alignAndAddBound(newConstr, BoundType::EQ, i,
map.getSubMap({i - resultDimStart}), operands)))
return failure();
// Skip unused operands and operands that are already constants.
if (!newOperands[i] || getConstantIntValue(newOperands[i]))
continue;
- if (auto bound = constraints.getConstantBound64(IntegerPolyhedron::EQ, i)) {
+ if (auto bound = constraints.getConstantBound64(BoundType::EQ, i)) {
AffineExpr expr =
i < newMap.getNumDims()
? builder.getAffineDimExpr(i)
unsigned pos;
if (!cstr.findVar(std::get<0>(it), &pos))
pos = cstr.appendSymbolVar(std::get<0>(it));
- cstr.addBound(FlatAffineValueConstraints::BoundType::LB, pos,
- std::get<1>(it));
+ cstr.addBound(presburger::BoundType::LB, pos, std::get<1>(it));
// Note: addBound bounds are inclusive, but specified UB is exclusive.
- cstr.addBound(FlatAffineValueConstraints::BoundType::UB, pos,
- std::get<2>(it) - 1);
+ cstr.addBound(presburger::BoundType::UB, pos, std::get<2>(it) - 1);
}
// Transform all targets.
for (unsigned d = 0; d < rank; d++) {
auto dimSize = memRefType.getDimSize(d);
assert(dimSize > 0 && "filtered dynamic shapes above");
- regionCst->addBound(IntegerPolyhedron::LB, d, 0);
- regionCst->addBound(IntegerPolyhedron::UB, d, dimSize - 1);
+ regionCst->addBound(BoundType::LB, d, 0);
+ regionCst->addBound(BoundType::UB, d, dimSize - 1);
}
return true;
}
for (unsigned d = 0; d < rank; ++d) {
// Use constraint system only in static dimensions.
if (shape[d] > 0) {
- fac.addBound(IntegerPolyhedron::LB, d, 0);
- fac.addBound(IntegerPolyhedron::UB, d, shape[d] - 1);
+ fac.addBound(BoundType::LB, d, 0);
+ fac.addBound(BoundType::UB, d, shape[d] - 1);
} else {
memrefTypeDynDims.emplace_back(d);
}
newShape[d] = ShapedType::kDynamic;
} else {
// The lower bound for the shape is always zero.
- std::optional<int64_t> ubConst =
- fac.getConstantBound64(IntegerPolyhedron::UB, d);
+ std::optional<int64_t> ubConst = fac.getConstantBound64(BoundType::UB, d);
// For a static memref and an affine map with no symbols, this is
// always bounded. However, when we have symbols, we may not be able to
// obtain a constant upper bound. Also, mapping to a negative space is
if (auto applyOp = dyn_cast<AffineApplyOp>(op)) {
AffineMap map = constraints.computeAlignedMap(applyOp.getAffineMap(),
applyOp.getOperands());
- if (failed(constraints.addBound(IntegerPolyhedron::EQ,
+ if (failed(constraints.addBound(BoundType::EQ,
getPosition(applyOp.getResult()), map)))
return;
continue;
auto minOp = cast<AffineMinOp>(op);
AffineMap map = constraints.computeAlignedMap(minOp.getAffineMap(),
minOp.getOperands());
- if (failed(constraints.addBound(IntegerPolyhedron::UB,
+ if (failed(constraints.addBound(BoundType::UB,
getPosition(minOp.getResult()), map,
/*isClosedBound=*/true)))
return;
// of the terminals of the index computation.
unsigned pos = getPosition(value);
if (constantRequired) {
- auto ubConst = constraints.getConstantBound64(
- FlatAffineValueConstraints::BoundType::UB, pos);
+ auto ubConst = constraints.getConstantBound64(BoundType::UB, pos);
if (!ubConst)
return;
std::optional<int64_t> lbInt = getConstantIntValue(lb);
std::optional<int64_t> ubInt = getConstantIntValue(ub);
if (lbInt)
- cstr.addBound(IntegerPolyhedron::EQ, symLb, *lbInt);
+ cstr.addBound(BoundType::EQ, symLb, *lbInt);
if (ubInt)
- cstr.addBound(IntegerPolyhedron::EQ, symUb, *ubInt);
+ cstr.addBound(BoundType::EQ, symUb, *ubInt);
// Lower bound: iv >= lb (equiv.: iv - lb >= 0)
SmallVector<int64_t> ineqLb(cstr.getNumCols(), 0);
/*dimCount=*/cstr.getNumDimVars(),
/*symbolCount=*/cstr.getNumSymbolVars(), /*result=*/ivUb);
- return cstr.addBound(IntegerPolyhedron::UB, dimIv, map);
+ return cstr.addBound(BoundType::UB, dimIv, map);
}
/// Canonicalize min/max operations in the context of for loops with a known
constraints.appendDimVar({iv});
constraints.appendSymbolVar({ub, step});
if (auto constUb = getConstantIntValue(ub))
- constraints.addBound(IntegerPolyhedron::EQ, 1, *constUb);
+ constraints.addBound(BoundType::EQ, 1, *constUb);
if (auto constStep = getConstantIntValue(step))
- constraints.addBound(IntegerPolyhedron::EQ, 2, *constStep);
+ constraints.addBound(BoundType::EQ, 2, *constStep);
// Add loop peeling invariant. This is the main piece of knowledge that
// enables AffineMinOp simplification.
TEST(IntegerPolyhedronTest, addConstantUpperBound) {
IntegerPolyhedron poly(PresburgerSpace::getSetSpace(2));
- poly.addBound(IntegerPolyhedron::UB, 0, 1);
+ poly.addBound(BoundType::UB, 0, 1);
EXPECT_EQ(poly.atIneq(0, 0), -1);
EXPECT_EQ(poly.atIneq(0, 1), 0);
EXPECT_EQ(poly.atIneq(0, 2), 1);
- poly.addBound(IntegerPolyhedron::UB, {1, 2, 3}, 1);
+ poly.addBound(BoundType::UB, {1, 2, 3}, 1);
EXPECT_EQ(poly.atIneq(1, 0), -1);
EXPECT_EQ(poly.atIneq(1, 1), -2);
EXPECT_EQ(poly.atIneq(1, 2), -2);
TEST(IntegerPolyhedronTest, addConstantLowerBound) {
IntegerPolyhedron poly(PresburgerSpace::getSetSpace(2));
- poly.addBound(IntegerPolyhedron::LB, 0, 1);
+ poly.addBound(BoundType::LB, 0, 1);
EXPECT_EQ(poly.atIneq(0, 0), 1);
EXPECT_EQ(poly.atIneq(0, 1), 0);
EXPECT_EQ(poly.atIneq(0, 2), -1);
- poly.addBound(IntegerPolyhedron::LB, {1, 2, 3}, 1);
+ poly.addBound(BoundType::LB, {1, 2, 3}, 1);
EXPECT_EQ(poly.atIneq(1, 0), 1);
EXPECT_EQ(poly.atIneq(1, 1), 2);
EXPECT_EQ(poly.atIneq(1, 2), 2);