#include "mlir/Dialect/Affine/Analysis/AffineStructures.h"
#include "mlir/Analysis/Presburger/IntegerRelation.h"
#include "mlir/Analysis/Presburger/LinearTransform.h"
#include "mlir/Analysis/Presburger/Simplex.h"
#include "mlir/Analysis/Presburger/Utils.h"
#include "mlir/Dialect/Affine/IR/AffineOps.h"
#include "mlir/Dialect/Affine/IR/AffineValueMap.h"
#include "mlir/Dialect/Utils/StaticValueUtils.h"
#include "mlir/IR/AffineExprVisitor.h"
#include "mlir/IR/IntegerSet.h"
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>
#define DEBUG_TYPE "affine-structures"
using namespace mlir;
using namespace affine;
using namespace presburger;
void FlatAffineValueConstraints::addInductionVarOrTerminalSymbol(Value val) {
if (containsVar(val))
return;
assert((isTopLevelValue(val) || isAffineInductionVar(val)) &&
"non-terminal symbol / loop IV expected");
if (auto loop = getForInductionVarOwner(val)) {
appendDimVar(val);
if (failed(this->addAffineForOpDomain(loop)))
LLVM_DEBUG(
loop.emitWarning("failed to add domain info to constraint system"));
return;
}
if (auto parallel = getAffineParallelInductionVarOwner(val)) {
appendDimVar(parallel.getIVs());
if (failed(this->addAffineParallelOpDomain(parallel)))
LLVM_DEBUG(parallel.emitWarning(
"failed to add domain info to constraint system"));
return;
}
appendSymbolVar(val);
if (std::optional<int64_t> constOp = getConstantIntValue(val))
addBound(BoundType::EQ, val, constOp.value());
}
LogicalResult
FlatAffineValueConstraints::addAffineForOpDomain(AffineForOp forOp) {
unsigned pos;
if (!findVar(forOp.getInductionVar(), &pos)) {
assert(false && "Value not found");
return failure();
}
int64_t step = forOp.getStepAsInt();
if (step != 1) {
if (!forOp.hasConstantLowerBound())
LLVM_DEBUG(forOp.emitWarning("domain conservatively approximated"));
else {
SmallVector<int64_t, 8> dividend(getNumCols(), 0);
int64_t lb = forOp.getConstantLowerBound();
dividend[pos] = 1;
dividend.back() -= lb;
addLocalFloorDiv(dividend, step);
SmallVector<int64_t, 8> eq(getNumCols(), 0);
eq[pos] = 1;
eq.back() -= lb;
eq[getNumCols() - 2] = -step;
addEquality(eq);
}
}
if (forOp.hasConstantLowerBound()) {
addBound(BoundType::LB, pos, forOp.getConstantLowerBound());
} else {
if (failed(addBound(BoundType::LB, pos, forOp.getLowerBoundMap(),
forOp.getLowerBoundOperands())))
return failure();
}
if (forOp.hasConstantUpperBound()) {
addBound(BoundType::UB, pos, forOp.getConstantUpperBound() - 1);
return success();
}
return addBound(BoundType::UB, pos, forOp.getUpperBoundMap(),
forOp.getUpperBoundOperands());
}
LogicalResult FlatAffineValueConstraints::addAffineParallelOpDomain(
AffineParallelOp parallelOp) {
size_t ivPos = 0;
for (Value iv : parallelOp.getIVs()) {
unsigned pos;
if (!findVar(iv, &pos)) {
assert(false && "variable expected for the IV value");
return failure();
}
AffineMap lowerBound = parallelOp.getLowerBoundMap(ivPos);
if (lowerBound.isConstant())
addBound(BoundType::LB, pos, lowerBound.getSingleConstantResult());
else if (failed(addBound(BoundType::LB, pos, lowerBound,
parallelOp.getLowerBoundsOperands())))
return failure();
auto upperBound = parallelOp.getUpperBoundMap(ivPos);
if (upperBound.isConstant())
addBound(BoundType::UB, pos, upperBound.getSingleConstantResult() - 1);
else if (failed(addBound(BoundType::UB, pos, upperBound,
parallelOp.getUpperBoundsOperands())))
return failure();
++ivPos;
}
return success();
}
LogicalResult
FlatAffineValueConstraints::addDomainFromSliceMaps(ArrayRef<AffineMap> lbMaps,
ArrayRef<AffineMap> ubMaps,
ArrayRef<Value> operands) {
assert(lbMaps.size() == ubMaps.size());
assert(lbMaps.size() <= getNumDimVars());
for (unsigned i = 0, e = lbMaps.size(); i < e; ++i) {
AffineMap lbMap = lbMaps[i];
AffineMap ubMap = ubMaps[i];
assert(!lbMap || lbMap.getNumInputs() == operands.size());
assert(!ubMap || ubMap.getNumInputs() == operands.size());
if (lbMap && ubMap && lbMap.getNumResults() == 1 &&
ubMap.getNumResults() == 1 &&
lbMap.getResult(0) + 1 == ubMap.getResult(0) &&
!isa<AffineConstantExpr>(lbMap.getResult(0))) {
AffineDimExpr result = dyn_cast<AffineDimExpr>(lbMap.getResult(0));
if (!result)
return failure();
AffineForOp loop =
getForInductionVarOwner(operands[result.getPosition()]);
if (!loop)
return failure();
if (failed(addAffineForOpDomain(loop)))
return failure();
continue;
}
if (lbMap && failed(addBound(BoundType::LB, i, lbMap, operands)))
return failure();
if (ubMap && failed(addBound(BoundType::UB, i, ubMap, operands)))
return failure();
}
return success();
}
void FlatAffineValueConstraints::addAffineIfOpDomain(AffineIfOp ifOp) {
IntegerSet set = ifOp.getIntegerSet();
SmallVector<Value> operands(ifOp.getOperands());
canonicalizeSetAndOperands(&set, &operands);
FlatAffineValueConstraints cst(set, operands);
mergeAndAlignVarsWithOther(0, &cst);
append(cst);
}
LogicalResult FlatAffineValueConstraints::addBound(BoundType type, unsigned pos,
AffineMap boundMap,
ValueRange boundOperands) {
auto map = boundMap;
SmallVector<Value, 4> operands(boundOperands.begin(), boundOperands.end());
fullyComposeAffineMapAndOperands(&map, &operands);
map = simplifyAffineMap(map);
canonicalizeMapAndOperands(&map, &operands);
for (auto operand : operands)
addInductionVarOrTerminalSymbol(operand);
return addBound(type, pos, computeAlignedMap(map, operands));
}
LogicalResult FlatAffineValueConstraints::addSliceBounds(
ArrayRef<Value> values, ArrayRef<AffineMap> lbMaps,
ArrayRef<AffineMap> ubMaps, ArrayRef<Value> operands) {
assert(values.size() == lbMaps.size());
assert(lbMaps.size() == ubMaps.size());
for (unsigned i = 0, e = lbMaps.size(); i < e; ++i) {
unsigned pos;
if (!findVar(values[i], &pos))
continue;
AffineMap lbMap = lbMaps[i];
AffineMap ubMap = ubMaps[i];
assert(!lbMap || lbMap.getNumInputs() == operands.size());
assert(!ubMap || ubMap.getNumInputs() == operands.size());
if (lbMap && ubMap && lbMap.getNumResults() == 1 &&
ubMap.getNumResults() == 1 &&
lbMap.getResult(0) + 1 == ubMap.getResult(0)) {
if (failed(addBound(BoundType::EQ, pos, lbMap, operands)))
return failure();
continue;
}
if (lbMap && lbMap.getNumResults() != 0 && ubMap &&
ubMap.getNumResults() != 0) {
if (failed(addBound(BoundType::LB, pos, lbMap, operands)))
return failure();
if (failed(addBound(BoundType::UB, pos, ubMap, operands)))
return failure();
} else {
auto loop = getForInductionVarOwner(values[i]);
if (failed(this->addAffineForOpDomain(loop)))
return failure();
}
}
return success();
}
LogicalResult
FlatAffineValueConstraints::composeMap(const AffineValueMap *vMap) {
return composeMatchingMap(
computeAlignedMap(vMap->getAffineMap(), vMap->getOperands()));
}
static void turnSymbolIntoDim(FlatAffineValueConstraints *cst, Value value) {
unsigned pos;
if (cst->findVar(value, &pos) && pos >= cst->getNumDimVars() &&
pos < cst->getNumDimAndSymbolVars()) {
cst->swapVar(pos, cst->getNumDimVars());
cst->setDimSymbolSeparation(cst->getNumSymbolVars() - 1);
}
}
void FlatAffineValueConstraints::convertLoopIVSymbolsToDims() {
SmallVector<Value, 4> loopIVs;
for (unsigned i = getNumDimVars(), e = getNumDimAndSymbolVars(); i < e; i++) {
if (hasValue(i) && getForInductionVarOwner(getValue(i)))
loopIVs.push_back(getValue(i));
}
for (auto iv : loopIVs) {
turnSymbolIntoDim(this, iv);
}
}
void FlatAffineValueConstraints::getIneqAsAffineValueMap(
unsigned pos, unsigned ineqPos, AffineValueMap &vmap,
MLIRContext *context) const {
unsigned numDims = getNumDimVars();
unsigned numSyms = getNumSymbolVars();
assert(pos < numDims && "invalid position");
assert(ineqPos < getNumInequalities() && "invalid inequality position");
SmallVector<AffineExpr, 8> memo(getNumVars(), AffineExpr());
if (failed(computeLocalVars(memo, context)))
assert(false &&
"one or more local exprs do not have an explicit representation");
auto localExprs = ArrayRef<AffineExpr>(memo).take_back(getNumLocalVars());
SmallVector<int64_t, 8> inequality = getInequality64(ineqPos);
SmallVector<int64_t, 8> bound;
bound.reserve(getNumCols() - 1);
bound.append(inequality.begin(), inequality.begin() + pos);
bound.append(inequality.begin() + pos + 1, inequality.end());
if (inequality[pos] > 0)
std::transform(bound.begin(), bound.end(), bound.begin(),
std::negate<int64_t>());
else
bound.back() += 1;
auto boundExpr = getAffineExprFromFlatForm(bound, numDims - 1, numSyms,
localExprs, context);
SmallVector<Value, 4> operands;
getValues(0, pos, &operands);
SmallVector<Value, 4> trailingOperands;
getValues(pos + 1, getNumDimAndSymbolVars(), &trailingOperands);
operands.append(trailingOperands.begin(), trailingOperands.end());
vmap.reset(AffineMap::get(numDims - 1, numSyms, boundExpr), operands);
}
FlatAffineValueConstraints FlatAffineRelation::getDomainSet() const {
FlatAffineValueConstraints domain = *this;
domain.convertToLocal(VarKind::SetDim, getNumDomainDims(),
getNumDomainDims() + getNumRangeDims());
return domain;
}
FlatAffineValueConstraints FlatAffineRelation::getRangeSet() const {
FlatAffineValueConstraints range = *this;
range.convertToLocal(VarKind::SetDim, 0, getNumDomainDims());
return range;
}
void FlatAffineRelation::compose(const FlatAffineRelation &other) {
assert(getNumDomainDims() == other.getNumRangeDims() &&
"Domain of this and range of other do not match");
assert(space.getDomainSpace().isAligned(other.getSpace().getRangeSpace()) &&
"Values of domain of this and range of other do not match");
FlatAffineRelation rel = other;
unsigned removeDims = rel.getNumRangeDims();
insertDomainVar(0, rel.getNumDomainDims());
rel.appendRangeVar(getNumRangeDims());
mergeSymbolVars(rel);
mergeLocalVars(rel);
rel.convertToLocal(VarKind::SetDim, rel.getNumDomainDims(),
rel.getNumDomainDims() + removeDims);
convertToLocal(VarKind::SetDim, getNumDomainDims() - removeDims,
getNumDomainDims());
auto thisMaybeValues = getMaybeValues(VarKind::SetDim);
auto relMaybeValues = rel.getMaybeValues(VarKind::SetDim);
for (unsigned i = 0, e = rel.getNumDomainDims(); i < e; ++i)
if (relMaybeValues[i].has_value())
setValue(i, *relMaybeValues[i]);
for (unsigned i = 0, e = getNumRangeDims(); i < e; ++i) {
unsigned rangeIdx = rel.getNumDomainDims() + i;
if (thisMaybeValues[rangeIdx].has_value())
rel.setValue(rangeIdx, *thisMaybeValues[rangeIdx]);
}
rel.append(*this);
rel.removeRedundantLocalVars();
*this = rel;
}
void FlatAffineRelation::inverse() {
unsigned oldDomain = getNumDomainDims();
unsigned oldRange = getNumRangeDims();
appendRangeVar(oldDomain);
for (unsigned i = 0; i < oldDomain; ++i)
swapVar(i, oldDomain + oldRange + i);
removeVarRange(0, oldDomain);
numDomainDims = oldRange;
numRangeDims = oldDomain;
}
void FlatAffineRelation::insertDomainVar(unsigned pos, unsigned num) {
assert(pos <= getNumDomainDims() &&
"Var cannot be inserted at invalid position");
insertDimVar(pos, num);
numDomainDims += num;
}
void FlatAffineRelation::insertRangeVar(unsigned pos, unsigned num) {
assert(pos <= getNumRangeDims() &&
"Var cannot be inserted at invalid position");
insertDimVar(getNumDomainDims() + pos, num);
numRangeDims += num;
}
void FlatAffineRelation::appendDomainVar(unsigned num) {
insertDimVar(getNumDomainDims(), num);
numDomainDims += num;
}
void FlatAffineRelation::appendRangeVar(unsigned num) {
insertDimVar(getNumDimVars(), num);
numRangeDims += num;
}
void FlatAffineRelation::removeVarRange(VarKind kind, unsigned varStart,
unsigned varLimit) {
assert(varLimit <= getNumVarKind(kind));
if (varStart >= varLimit)
return;
FlatAffineValueConstraints::removeVarRange(kind, varStart, varLimit);
if (kind != VarKind::SetDim)
return;
unsigned intersectDomainLHS = std::min(varLimit, getNumDomainDims());
unsigned intersectDomainRHS = varStart;
unsigned intersectRangeLHS = std::min(varLimit, getNumDimVars());
unsigned intersectRangeRHS = std::max(varStart, getNumDomainDims());
if (intersectDomainLHS > intersectDomainRHS)
numDomainDims -= intersectDomainLHS - intersectDomainRHS;
if (intersectRangeLHS > intersectRangeRHS)
numRangeDims -= intersectRangeLHS - intersectRangeRHS;
}
LogicalResult mlir::affine::getRelationFromMap(AffineMap &map,
IntegerRelation &rel) {
std::vector<SmallVector<int64_t, 8>> flatExprs;
FlatAffineValueConstraints localVarCst;
if (failed(getFlattenedAffineExprs(map, &flatExprs, &localVarCst)))
return failure();
const unsigned oldDimNum = localVarCst.getNumDimVars();
const unsigned oldCols = localVarCst.getNumCols();
const unsigned numRangeVars = map.getNumResults();
const unsigned numDomainVars = map.getNumDims();
localVarCst.appendDimVar(numRangeVars);
for (unsigned i = 0, e = localVarCst.getNumDimAndSymbolVars(); i < e; ++i)
localVarCst.setValue(i, Value());
SmallVector<int64_t, 8> eq(localVarCst.getNumCols());
for (unsigned i = 0, e = map.getNumResults(); i < e; ++i) {
std::fill(eq.begin(), eq.end(), 0);
for (unsigned j = 0, f = oldDimNum; j < f; ++j)
eq[j] = flatExprs[i][j];
for (unsigned j = oldDimNum, f = oldCols; j < f; ++j)
eq[j + numRangeVars] = flatExprs[i][j];
eq[numDomainVars + i] = -1;
localVarCst.addEquality(eq);
}
rel = localVarCst;
return success();
}
LogicalResult mlir::affine::getRelationFromMap(const AffineValueMap &map,
IntegerRelation &rel) {
AffineMap affineMap = map.getAffineMap();
if (failed(getRelationFromMap(affineMap, rel)))
return failure();
for (unsigned i = 0, e = affineMap.getNumDims(); i < e; ++i)
rel.setId(VarKind::SetDim, i, Identifier(map.getOperand(i)));
const unsigned mapNumResults = affineMap.getNumResults();
for (unsigned i = 0, e = rel.getNumSymbolVars(); i < e; ++i)
rel.setId(
VarKind::Symbol, i,
Identifier(map.getOperand(rel.getNumDimVars() + i - mapNumResults)));
return success();
}