[mlir][Analysis][NFC] Split FlatAffineValueConstraints into multiple classes

The new class hierarchy is as follows:

* `IntegerRelation` (no change)
* `IntegerPolyhedron` (no change)
* `FlatLinearConstraints`: provides an AffineExpr-based API
* `FlatLinearValueConstraints`: stores an additional mapping of non-local vars to SSA values
* `FlatAffineValueConstraints`: provides additional helper functions for Affine dialect ops
* `FlatAffineRelation` (no change)

`FlatConstraints` and `FlatValueConstraints` are moved from `MLIRAffineAnalysis` to `MLIRAnalysis` and can be used without depending on the Affine dialect.

This change is in preparation of D145681, which adds an MLIR interface that depends on `FlatConstraints` (and cannot depend on the Affine dialect or any other dialect).

Differential Revision: https://reviews.llvm.org/D146201
This commit is contained in:
Matthias Springer 2023-03-23 09:25:01 +01:00
parent b08d35f826
commit 5b0055a4ae
13 changed files with 1983 additions and 1807 deletions

View File

@ -235,9 +235,9 @@ if (auto *dimOp = inst->dyn_cast<DimOp>()) {
It is much better to eliminate them entirely, and just pass around `DimOp`
directly. For example, instead of:
```C++
```c++
LogicalResult mlir::getIndexSet(MutableArrayRef<OpPointer<AffineForOp>> forOps,
FlatAffineConstraints *domain) {
FlatAffineValueConstraints *domain) {
```
@ -245,7 +245,7 @@ It is a lot nicer to just have:
```c++
LogicalResult mlir::getIndexSet(MutableArrayRef<AffineForOp> forOps,
FlatAffineConstraints *domain) {
FlatAffineValueConstraints *domain) {
```
Particularly since all of the `FooOp` classes are already semantically a smart

View File

@ -0,0 +1,560 @@
//===- FlatLinearValueConstraints.h - Linear Constraints --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_ANALYSIS_FLATLINEARVALUECONSTRAINTS_H
#define MLIR_ANALYSIS_FLATLINEARVALUECONSTRAINTS_H
#include "mlir/Analysis/Presburger/IntegerRelation.h"
#include "mlir/Analysis/Presburger/Matrix.h"
#include "mlir/IR/AffineExpr.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/Support/LogicalResult.h"
#include <optional>
namespace mlir {
class AffineMap;
class IntegerSet;
class MLIRContext;
class Value;
class MemRefType;
struct MutableAffineMap;
namespace presburger {
class MultiAffineFunction;
} // namespace presburger
/// FlatLinearConstraints is an extension of IntegerPolyhedron. It provides an
/// AffineExpr-based API.
class FlatLinearConstraints : public presburger::IntegerPolyhedron {
public:
/// Constructs a constraint system reserving memory for the specified number
/// of constraints and variables. `valArgs` are the optional SSA values
/// associated with each dimension/symbol. These must either be empty or match
/// the number of dimensions and symbols.
FlatLinearConstraints(unsigned numReservedInequalities,
unsigned numReservedEqualities,
unsigned numReservedCols, unsigned numDims,
unsigned numSymbols, unsigned numLocals)
: IntegerPolyhedron(numReservedInequalities, numReservedEqualities,
numReservedCols,
presburger::PresburgerSpace::getSetSpace(
numDims, numSymbols, numLocals)) {
assert(numReservedCols >= getNumVars() + 1);
}
/// Constructs a constraint system with the specified number of dimensions
/// and symbols. `valArgs` are the optional SSA values associated with each
/// dimension/symbol. These must either be empty or match the number of
/// dimensions and symbols.
FlatLinearConstraints(unsigned numDims = 0, unsigned numSymbols = 0,
unsigned numLocals = 0)
: FlatLinearConstraints(/*numReservedInequalities=*/0,
/*numReservedEqualities=*/0,
/*numReservedCols=*/numDims + numSymbols +
numLocals + 1,
numDims, numSymbols, numLocals) {}
FlatLinearConstraints(const IntegerPolyhedron &fac)
: IntegerPolyhedron(fac) {}
/// Return the kind of this object.
Kind getKind() const override { return Kind::FlatLinearConstraints; }
static bool classof(const IntegerRelation *cst) {
return cst->getKind() >= Kind::FlatLinearConstraints &&
cst->getKind() <= Kind::FlatAffineRelation;
}
/// Clones this object.
std::unique_ptr<FlatLinearConstraints> clone() const;
/// 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
/// 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.
///
/// The bound can be added as open or closed by specifying isClosedBound. In
/// case of a LB/UB, isClosedBound = false means the bound is added internally
/// as a closed bound by +1/-1 respectively. In case of an EQ bound, it can
/// only be added as a closed bound.
///
/// 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);
/// 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
/// 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.
/// 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);
/// The `addBound` overload above hides the inherited overloads by default, so
/// we explicitly introduce them here.
using IntegerPolyhedron::addBound;
/// Returns the constraint system as an integer set. Returns a null integer
/// set if the system has no constraints, or if an integer set couldn't be
/// constructed as a result of a local variable's explicit representation not
/// being known and such a local variable appearing in any of the constraints.
IntegerSet getAsIntegerSet(MLIRContext *context) const;
/// Computes the lower and upper bounds of the first `num` dimensional
/// variables (starting at `offset`) as an affine map of the remaining
/// variables (dimensional and symbolic). This method is able to detect
/// variables as floordiv's and mod's of affine expressions of other
/// variables with respect to (positive) constants. Sets bound map to a
/// null AffineMap if such a bound can't be found (or yet unimplemented).
///
/// By default the returned lower bounds are closed and upper bounds are open.
/// If `closedUb` is true, the upper bound is closed.
void getSliceBounds(unsigned offset, unsigned num, MLIRContext *context,
SmallVectorImpl<AffineMap> *lbMaps,
SmallVectorImpl<AffineMap> *ubMaps,
bool closedUB = false);
/// Composes an affine map whose dimensions and symbols match one to one with
/// the dimensions and symbols of this FlatLinearConstraints. The results of
/// the map `other` are added as the leading dimensions of this constraint
/// system. Returns failure if `other` is a semi-affine map.
LogicalResult composeMatchingMap(AffineMap other);
/// Gets the lower and upper bound of the `offset` + `pos`th variable
/// treating [0, offset) U [offset + num, symStartPos) as dimensions and
/// [symStartPos, getNumDimAndSymbolVars) as symbols, and `pos` lies in
/// [0, num). The multi-dimensional maps in the returned pair represent the
/// max and min of potentially multiple affine expressions. `localExprs` holds
/// pre-computed AffineExpr's for all local variables in the system.
///
/// By default the returned lower bounds are closed and upper bounds are open.
/// If `closedUb` is true, the upper bound is closed.
std::pair<AffineMap, AffineMap>
getLowerAndUpperBound(unsigned pos, unsigned offset, unsigned num,
unsigned symStartPos, ArrayRef<AffineExpr> localExprs,
MLIRContext *context, bool closedUB = false) const;
/// Insert variables of the specified kind at position `pos`. Positions are
/// relative to the kind of variable. The coefficient columns corresponding
/// to the added variables are initialized to zero. `vals` are the Values
/// corresponding to the variables. Values should not be used with
/// VarKind::Local since values can only be attached to non-local variables.
/// Return the absolute column position (i.e., not relative to the kind of
/// variable) of the first added variable.
///
/// Note: Empty Values are allowed in `vals`.
unsigned insertDimVar(unsigned pos, unsigned num = 1) {
return insertVar(VarKind::SetDim, pos, num);
}
unsigned insertSymbolVar(unsigned pos, unsigned num = 1) {
return insertVar(VarKind::Symbol, pos, num);
}
unsigned insertLocalVar(unsigned pos, unsigned num = 1) {
return insertVar(VarKind::Local, pos, num);
}
/// Append variables of the specified kind after the last variable of that
/// kind. The coefficient columns corresponding to the added variables are
/// initialized to zero. `vals` are the Values corresponding to the
/// variables. Return the absolute column position (i.e., not relative to the
/// kind of variable) of the first appended variable.
///
/// Note: Empty Values are allowed in `vals`.
unsigned appendDimVar(unsigned num = 1) {
return appendVar(VarKind::SetDim, num);
}
unsigned appendSymbolVar(unsigned num = 1) {
return appendVar(VarKind::Symbol, num);
}
unsigned appendLocalVar(unsigned num = 1) {
return appendVar(VarKind::Local, num);
}
protected:
using VarKind = presburger::VarKind;
/// Compute an explicit representation for local vars. For all systems coming
/// from MLIR integer sets, maps, or expressions where local vars were
/// introduced to model floordivs and mods, this always succeeds.
LogicalResult computeLocalVars(SmallVectorImpl<AffineExpr> &memo,
MLIRContext *context) const;
/// Given an affine map that is aligned with this constraint system:
/// * Flatten the map.
/// * Add newly introduced local columns at the beginning of this constraint
/// system (local column pos 0).
/// * Add equalities that define the new local columns to this constraint
/// system.
/// * Return the flattened expressions via `flattenedExprs`.
///
/// Note: This is a shared helper function of `addLowerOrUpperBound` and
/// `composeMatchingMap`.
LogicalResult flattenAlignedMapAndMergeLocals(
AffineMap map, std::vector<SmallVector<int64_t, 8>> *flattenedExprs);
/// Prints the number of constraints, dimensions, symbols and locals in the
/// FlatLinearConstraints. Also, prints for each variable whether there is
/// an SSA Value attached to it.
void printSpace(raw_ostream &os) const override;
};
/// FlatLinearValueConstraints represents an extension of FlatLinearConstraints
/// where each non-local variable can have an SSA Value attached to it.
class FlatLinearValueConstraints : public FlatLinearConstraints {
public:
/// Constructs a constraint system reserving memory for the specified number
/// of constraints and variables. `valArgs` are the optional SSA values
/// associated with each dimension/symbol. These must either be empty or match
/// the number of dimensions and symbols.
FlatLinearValueConstraints(unsigned numReservedInequalities,
unsigned numReservedEqualities,
unsigned numReservedCols, unsigned numDims,
unsigned numSymbols, unsigned numLocals,
ArrayRef<std::optional<Value>> valArgs)
: FlatLinearConstraints(numReservedInequalities, numReservedEqualities,
numReservedCols, numDims, numSymbols, numLocals) {
assert(valArgs.empty() || valArgs.size() == getNumDimAndSymbolVars());
values.reserve(numReservedCols);
if (valArgs.empty())
values.resize(getNumDimAndSymbolVars(), std::nullopt);
else
values.append(valArgs.begin(), valArgs.end());
}
/// Constructs a constraint system reserving memory for the specified number
/// of constraints and variables. `valArgs` are the optional SSA values
/// associated with each dimension/symbol. These must either be empty or match
/// the number of dimensions and symbols.
FlatLinearValueConstraints(unsigned numReservedInequalities,
unsigned numReservedEqualities,
unsigned numReservedCols, unsigned numDims,
unsigned numSymbols, unsigned numLocals,
ArrayRef<Value> valArgs)
: FlatLinearConstraints(numReservedInequalities, numReservedEqualities,
numReservedCols, numDims, numSymbols, numLocals) {
assert(valArgs.empty() || valArgs.size() == getNumDimAndSymbolVars());
values.reserve(numReservedCols);
if (valArgs.empty())
values.resize(getNumDimAndSymbolVars(), std::nullopt);
else
values.append(valArgs.begin(), valArgs.end());
}
/// Constructs a constraint system with the specified number of dimensions
/// and symbols. `valArgs` are the optional SSA values associated with each
/// dimension/symbol. These must either be empty or match the number of
/// dimensions and symbols.
FlatLinearValueConstraints(unsigned numDims, unsigned numSymbols,
unsigned numLocals,
ArrayRef<std::optional<Value>> valArgs)
: FlatLinearValueConstraints(/*numReservedInequalities=*/0,
/*numReservedEqualities=*/0,
/*numReservedCols=*/numDims + numSymbols +
numLocals + 1,
numDims, numSymbols, numLocals, valArgs) {}
/// Constructs a constraint system with the specified number of dimensions
/// and symbols. `valArgs` are the optional SSA values associated with each
/// dimension/symbol. These must either be empty or match the number of
/// dimensions and symbols.
FlatLinearValueConstraints(unsigned numDims = 0, unsigned numSymbols = 0,
unsigned numLocals = 0,
ArrayRef<Value> valArgs = {})
: FlatLinearValueConstraints(/*numReservedInequalities=*/0,
/*numReservedEqualities=*/0,
/*numReservedCols=*/numDims + numSymbols +
numLocals + 1,
numDims, numSymbols, numLocals, valArgs) {}
FlatLinearValueConstraints(const IntegerPolyhedron &fac,
ArrayRef<std::optional<Value>> valArgs = {})
: FlatLinearConstraints(fac) {
assert(valArgs.empty() || valArgs.size() == getNumDimAndSymbolVars());
if (valArgs.empty())
values.resize(getNumDimAndSymbolVars(), std::nullopt);
else
values.append(valArgs.begin(), valArgs.end());
}
/// Creates an affine constraint system from an IntegerSet.
explicit FlatLinearValueConstraints(IntegerSet set, ValueRange operands = {});
// Construct a hyperrectangular constraint set from ValueRanges that represent
// induction variables, lower and upper bounds. `ivs`, `lbs` and `ubs` are
// expected to match one to one. The order of variables and constraints is:
//
// ivs | lbs | ubs | eq/ineq
// ----+-----+-----+---------
// 1 -1 0 >= 0
// ----+-----+-----+---------
// -1 0 1 >= 0
//
// All dimensions as set as VarKind::SetDim.
static FlatLinearValueConstraints
getHyperrectangular(ValueRange ivs, ValueRange lbs, ValueRange ubs);
/// Return the kind of this object.
Kind getKind() const override { return Kind::FlatLinearValueConstraints; }
static bool classof(const IntegerRelation *cst) {
return cst->getKind() >= Kind::FlatLinearValueConstraints &&
cst->getKind() <= Kind::FlatAffineRelation;
}
/// Replaces the contents of this FlatLinearValueConstraints with `other`.
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);
using FlatLinearConstraints::addBound;
/// Returns the Value associated with the pos^th variable. Asserts if
/// no Value variable was associated.
inline Value getValue(unsigned pos) const {
assert(pos < getNumDimAndSymbolVars() && "Invalid position");
assert(hasValue(pos) && "variable's Value not set");
return *values[pos];
}
/// Returns the Values associated with variables in range [start, end).
/// Asserts if no Value was associated with one of these variables.
inline void getValues(unsigned start, unsigned end,
SmallVectorImpl<Value> *values) const {
assert(end <= getNumDimAndSymbolVars() && "invalid end position");
assert(start <= end && "invalid start position");
values->clear();
values->reserve(end - start);
for (unsigned i = start; i < end; i++)
values->push_back(getValue(i));
}
inline void getAllValues(SmallVectorImpl<Value> *values) const {
getValues(0, getNumDimAndSymbolVars(), values);
}
inline ArrayRef<std::optional<Value>> getMaybeValues() const {
return {values.data(), values.size()};
}
inline ArrayRef<std::optional<Value>>
getMaybeValues(presburger::VarKind kind) const {
assert(kind != VarKind::Local &&
"Local variables do not have any value attached to them.");
return {values.data() + getVarKindOffset(kind), getNumVarKind(kind)};
}
/// Returns true if the pos^th variable has an associated Value.
inline bool hasValue(unsigned pos) const {
assert(pos < getNumDimAndSymbolVars() && "Invalid position");
return values[pos].has_value();
}
/// Returns true if at least one variable has an associated Value.
bool hasValues() const;
unsigned appendDimVar(ValueRange vals);
using FlatLinearConstraints::appendDimVar;
unsigned appendSymbolVar(ValueRange vals);
using FlatLinearConstraints::appendSymbolVar;
unsigned insertDimVar(unsigned pos, ValueRange vals);
using FlatLinearConstraints::insertDimVar;
unsigned insertSymbolVar(unsigned pos, ValueRange vals);
using FlatLinearConstraints::insertSymbolVar;
unsigned insertVar(presburger::VarKind kind, unsigned pos,
unsigned num = 1) override;
unsigned insertVar(presburger::VarKind kind, unsigned pos, ValueRange vals);
/// Removes variables in the column range [varStart, varLimit), and copies any
/// remaining valid data into place, updates member variables, and resizes
/// arrays as needed.
void removeVarRange(presburger::VarKind kind, unsigned varStart,
unsigned varLimit) override;
using IntegerPolyhedron::removeVarRange;
/// Sets the Value associated with the pos^th variable.
inline void setValue(unsigned pos, Value val) {
assert(pos < getNumDimAndSymbolVars() && "invalid var position");
values[pos] = val;
}
/// Sets the Values associated with the variables in the range [start, end).
/// The range must contain only dim and symbol variables.
void setValues(unsigned start, unsigned end, ArrayRef<Value> values) {
assert(end <= getNumVars() && "invalid end position");
assert(start <= end && "invalid start position");
assert(values.size() == end - start &&
"value should be provided for each variable in the range.");
for (unsigned i = start; i < end; ++i)
setValue(i, values[i - start]);
}
/// Looks up the position of the variable with the specified Value. Returns
/// true if found (false otherwise). `pos` is set to the (column) position of
/// the variable.
bool findVar(Value val, unsigned *pos) const;
/// Returns true if a variable with the specified Value exists, false
/// otherwise.
bool containsVar(Value val) const;
/// Projects out the variable that is associate with Value.
void projectOut(Value val);
using IntegerPolyhedron::projectOut;
/// Swap the posA^th variable with the posB^th variable.
void swapVar(unsigned posA, unsigned posB) override;
/// Prints the number of constraints, dimensions, symbols and locals in the
/// FlatAffineValueConstraints. Also, prints for each variable whether there
/// is an SSA Value attached to it.
void printSpace(raw_ostream &os) const override;
/// Align `map` with this constraint system based on `operands`. Each operand
/// must already have a corresponding dim/symbol in this constraint system.
AffineMap computeAlignedMap(AffineMap map, ValueRange operands) const;
/// Merge and align the variables of `this` and `other` starting at
/// `offset`, so that both constraint systems get the union of the contained
/// variables that is dimension-wise and symbol-wise unique; both
/// constraint systems are updated so that they have the union of all
/// variables, with `this`'s original variables appearing first followed
/// by any of `other`'s variables that didn't appear in `this`. Local
/// variables in `other` that have the same division representation as local
/// variables in `this` are merged into one.
// E.g.: Input: `this` has (%i, %j) [%M, %N]
// `other` has (%k, %j) [%P, %N, %M]
// Output: both `this`, `other` have (%i, %j, %k) [%M, %N, %P]
//
void mergeAndAlignVarsWithOther(unsigned offset,
FlatLinearValueConstraints *other);
/// Merge and align symbols of `this` and `other` such that both get union of
/// of symbols that are unique. Symbols in `this` and `other` should be
/// unique. Symbols with Value as `None` are considered to be inequal to all
/// other symbols.
void mergeSymbolVars(FlatLinearValueConstraints &other);
/// Returns true if this constraint system and `other` are in the same
/// space, i.e., if they are associated with the same set of variables,
/// appearing in the same order. Returns false otherwise.
bool areVarsAlignedWithOther(const FlatLinearConstraints &other);
/// Updates the constraints to be the smallest bounding (enclosing) box that
/// contains the points of `this` set and that of `other`, with the symbols
/// being treated specially. For each of the dimensions, the min of the lower
/// bounds (symbolic) and the max of the upper bounds (symbolic) is computed
/// to determine such a bounding box. `other` is expected to have the same
/// dimensional variables as this constraint system (in the same order).
///
/// E.g.:
/// 1) this = {0 <= d0 <= 127},
/// other = {16 <= d0 <= 192},
/// output = {0 <= d0 <= 192}
/// 2) this = {s0 + 5 <= d0 <= s0 + 20},
/// other = {s0 + 1 <= d0 <= s0 + 9},
/// output = {s0 + 1 <= d0 <= s0 + 20}
/// 3) this = {0 <= d0 <= 5, 1 <= d1 <= 9}
/// other = {2 <= d0 <= 6, 5 <= d1 <= 15},
/// output = {0 <= d0 <= 6, 1 <= d1 <= 15}
LogicalResult unionBoundingBox(const FlatLinearValueConstraints &other);
using IntegerPolyhedron::unionBoundingBox;
protected:
/// Eliminates the variable at the specified position using Fourier-Motzkin
/// variable elimination, but uses Gaussian elimination if there is an
/// equality involving that variable. If the result of the elimination is
/// integer exact, `*isResultIntegerExact` is set to true. If `darkShadow` is
/// set to true, a potential under approximation (subset) of the rational
/// shadow / exact integer shadow is computed.
// See implementation comments for more details.
void fourierMotzkinEliminate(unsigned pos, bool darkShadow = false,
bool *isResultIntegerExact = nullptr) override;
/// Returns false if the fields corresponding to various variable counts, or
/// equality/inequality buffer sizes aren't consistent; true otherwise. This
/// is meant to be used within an assert internally.
bool hasConsistentState() const override;
/// Values corresponding to the (column) non-local variables of this
/// constraint system appearing in the order the variables correspond to
/// columns. Variables that aren't associated with any Value are set to
/// None.
SmallVector<std::optional<Value>, 8> values;
};
/// Flattens 'expr' into 'flattenedExpr', which contains the coefficients of the
/// dimensions, symbols, and additional variables that represent floor divisions
/// of dimensions, symbols, and in turn other floor divisions. Returns failure
/// if 'expr' could not be flattened (i.e., semi-affine is not yet handled).
/// 'cst' contains constraints that connect newly introduced local variables
/// to existing dimensional and symbolic variables. See documentation for
/// AffineExprFlattener on how mod's and div's are flattened.
LogicalResult getFlattenedAffineExpr(AffineExpr expr, unsigned numDims,
unsigned numSymbols,
SmallVectorImpl<int64_t> *flattenedExpr,
FlatLinearConstraints *cst = nullptr);
/// Flattens the result expressions of the map to their corresponding flattened
/// forms and set in 'flattenedExprs'. Returns failure if any expression in the
/// map could not be flattened (i.e., semi-affine is not yet handled). 'cst'
/// contains constraints that connect newly introduced local variables to
/// existing dimensional and / symbolic variables. See documentation for
/// AffineExprFlattener on how mod's and div's are flattened. For all affine
/// expressions that share the same operands (like those of an affine map), this
/// method should be used instead of repeatedly calling getFlattenedAffineExpr
/// since local variables added to deal with div's and mod's will be reused
/// across expressions.
LogicalResult
getFlattenedAffineExprs(AffineMap map,
std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
FlatLinearConstraints *cst = nullptr);
LogicalResult
getFlattenedAffineExprs(IntegerSet set,
std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
FlatLinearConstraints *cst = nullptr);
LogicalResult
getMultiAffineFunctionFromMap(AffineMap map,
presburger::MultiAffineFunction &multiAff);
/// Re-indexes the dimensions and symbols of an affine map with given `operands`
/// values to align with `dims` and `syms` values.
///
/// Each dimension/symbol of the map, bound to an operand `o`, is replaced with
/// dimension `i`, where `i` is the position of `o` within `dims`. If `o` is not
/// in `dims`, replace it with symbol `i`, where `i` is the position of `o`
/// within `syms`. If `o` is not in `syms` either, replace it with a new symbol.
///
/// Note: If a value appears multiple times as a dimension/symbol (or both), all
/// corresponding dim/sym expressions are replaced with the first dimension
/// bound to that value (or first symbol if no such dimension exists).
///
/// The resulting affine map has `dims.size()` many dimensions and at least
/// `syms.size()` many symbols.
///
/// The SSA values of the symbols of the resulting map are optionally returned
/// via `newSyms`. This is a concatenation of `syms` with the SSA values of the
/// newly added symbols.
///
/// Note: As part of this re-indexing, dimensions may turn into symbols, or vice
/// versa.
AffineMap alignAffineMapWithValues(AffineMap map, ValueRange operands,
ValueRange dims, ValueRange syms,
SmallVector<Value> *newSyms = nullptr);
} // namespace mlir
#endif // MLIR_ANALYSIS_FLATLINEARVALUECONSTRAINTS_H

View File

@ -54,10 +54,12 @@ class IntegerRelation {
public:
/// All derived classes of IntegerRelation.
enum class Kind {
FlatAffineConstraints,
FlatAffineValueConstraints,
IntegerRelation,
IntegerPolyhedron,
FlatLinearConstraints,
FlatLinearValueConstraints,
FlatAffineValueConstraints,
FlatAffineRelation
};
/// Constructs a relation reserving memory for the specified number
@ -848,7 +850,8 @@ public:
Kind getKind() const override { return Kind::IntegerPolyhedron; }
static bool classof(const IntegerRelation *cst) {
return cst->getKind() == Kind::IntegerPolyhedron;
return cst->getKind() >= Kind::IntegerPolyhedron &&
cst->getKind() <= Kind::FlatAffineRelation;
}
// Clones this object.

View File

@ -13,6 +13,7 @@
#ifndef MLIR_DIALECT_AFFINE_ANALYSIS_AFFINESTRUCTURES_H
#define MLIR_DIALECT_AFFINE_ANALYSIS_AFFINESTRUCTURES_H
#include "mlir/Analysis/FlatLinearValueConstraints.h"
#include "mlir/Analysis/Presburger/IntegerRelation.h"
#include "mlir/Analysis/Presburger/Matrix.h"
#include "mlir/IR/AffineExpr.h"
@ -38,117 +39,20 @@ namespace presburger {
class MultiAffineFunction;
} // namespace presburger
/// FlatAffineValueConstraints represents an extension of IntegerPolyhedron
/// where each non-local variable can have an SSA Value attached to it.
class FlatAffineValueConstraints : public presburger::IntegerPolyhedron {
/// FlatAffineValueConstraints is an extension of FlatLinearValueConstraints
/// with helper functions for Affine dialect ops.
class FlatAffineValueConstraints : public FlatLinearValueConstraints {
public:
/// Constructs a constraint system reserving memory for the specified number
/// of constraints and variables. `valArgs` are the optional SSA values
/// associated with each dimension/symbol. These must either be empty or match
/// the number of dimensions and symbols.
FlatAffineValueConstraints(unsigned numReservedInequalities,
unsigned numReservedEqualities,
unsigned numReservedCols, unsigned numDims,
unsigned numSymbols, unsigned numLocals,
ArrayRef<std::optional<Value>> valArgs)
: IntegerPolyhedron(numReservedInequalities, numReservedEqualities,
numReservedCols,
presburger::PresburgerSpace::getSetSpace(
numDims, numSymbols, numLocals)) {
assert(numReservedCols >= getNumVars() + 1);
assert(valArgs.empty() || valArgs.size() == getNumDimAndSymbolVars());
values.reserve(numReservedCols);
if (valArgs.empty())
values.resize(getNumDimAndSymbolVars(), std::nullopt);
else
values.append(valArgs.begin(), valArgs.end());
}
using FlatLinearValueConstraints::FlatLinearValueConstraints;
/// Constructs a constraint system reserving memory for the specified number
/// of constraints and variables. `valArgs` are the optional SSA values
/// associated with each dimension/symbol. These must either be empty or match
/// the number of dimensions and symbols.
FlatAffineValueConstraints(unsigned numReservedInequalities,
unsigned numReservedEqualities,
unsigned numReservedCols, unsigned numDims,
unsigned numSymbols, unsigned numLocals,
ArrayRef<Value> valArgs = {})
: IntegerPolyhedron(numReservedInequalities, numReservedEqualities,
numReservedCols,
presburger::PresburgerSpace::getSetSpace(
numDims, numSymbols, numLocals)) {
assert(numReservedCols >= getNumVars() + 1);
assert(valArgs.empty() || valArgs.size() == getNumDimAndSymbolVars());
values.reserve(numReservedCols);
if (valArgs.empty())
values.resize(getNumDimAndSymbolVars(), std::nullopt);
else
values.append(valArgs.begin(), valArgs.end());
}
/// Constructs a constraint system with the specified number of dimensions
/// and symbols. `valArgs` are the optional SSA values associated with each
/// dimension/symbol. These must either be empty or match the number of
/// dimensions and symbols.
FlatAffineValueConstraints(unsigned numDims, unsigned numSymbols,
unsigned numLocals,
ArrayRef<std::optional<Value>> valArgs)
: FlatAffineValueConstraints(/*numReservedInequalities=*/0,
/*numReservedEqualities=*/0,
/*numReservedCols=*/numDims + numSymbols +
numLocals + 1,
numDims, numSymbols, numLocals, valArgs) {}
/// Constructs a constraint system with the specified number of dimensions
/// and symbols. `valArgs` are the optional SSA values associated with each
/// dimension/symbol. These must either be empty or match the number of
/// dimensions and symbols.
FlatAffineValueConstraints(unsigned numDims = 0, unsigned numSymbols = 0,
unsigned numLocals = 0,
ArrayRef<Value> valArgs = {})
: FlatAffineValueConstraints(/*numReservedInequalities=*/0,
/*numReservedEqualities=*/0,
/*numReservedCols=*/numDims + numSymbols +
numLocals + 1,
numDims, numSymbols, numLocals, valArgs) {}
FlatAffineValueConstraints(const IntegerPolyhedron &fac,
ArrayRef<std::optional<Value>> valArgs = {})
: IntegerPolyhedron(fac) {
assert(valArgs.empty() || valArgs.size() == getNumDimAndSymbolVars());
if (valArgs.empty())
values.resize(getNumDimAndSymbolVars(), std::nullopt);
else
values.append(valArgs.begin(), valArgs.end());
}
/// Creates an affine constraint system from an IntegerSet.
explicit FlatAffineValueConstraints(IntegerSet set, ValueRange operands = {});
// Construct a hyperrectangular constraint set from ValueRanges that represent
// induction variables, lower and upper bounds. `ivs`, `lbs` and `ubs` are
// expected to match one to one. The order of variables and constraints is:
//
// ivs | lbs | ubs | eq/ineq
// ----+-----+-----+---------
// 1 -1 0 >= 0
// ----+-----+-----+---------
// -1 0 1 >= 0
//
// All dimensions as set as VarKind::SetDim.
static FlatAffineValueConstraints
getHyperrectangular(ValueRange ivs, ValueRange lbs, ValueRange ubs);
/// Return the kind of this FlatAffineConstraints.
/// Return the kind of this object.
Kind getKind() const override { return Kind::FlatAffineValueConstraints; }
static bool classof(const IntegerRelation *cst) {
return cst->getKind() == Kind::FlatAffineValueConstraints;
return cst->getKind() >= Kind::FlatAffineValueConstraints &&
cst->getKind() <= Kind::FlatAffineRelation;
}
/// Clones this object.
std::unique_ptr<FlatAffineValueConstraints> clone() const;
/// Adds constraints (lower and upper bounds) for the specified 'affine.for'
/// operation's Value using IR information stored in its bound maps. The
/// right variable is first looked up using `forOp`'s Value. Asserts if the
@ -191,32 +95,6 @@ public:
/// the columns in the current one regarding numbers and values.
void addAffineIfOpDomain(AffineIfOp ifOp);
/// 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
/// 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.
///
/// The bound can be added as open or closed by specifying isClosedBound. In
/// case of a LB/UB, isClosedBound = false means the bound is added internally
/// as a closed bound by +1/-1 respectively. In case of an EQ bound, it can
/// only be added as a closed bound.
///
/// Note: The dimensions/symbols of this FlatAffineConstraints must match the
/// dimensions/symbols of the affine map.
LogicalResult addBound(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
/// 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.
/// Note: The dimensions/symbols of this FlatAffineConstraints 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);
/// Adds a bound for the variable at the specified position with constraints
/// being drawn from the specified bound map and operands. In case of an
/// EQ bound, the bound map is expected to have exactly one result. In case
@ -224,62 +102,15 @@ public:
/// an inequality is added.
LogicalResult addBound(BoundType type, unsigned pos, AffineMap boundMap,
ValueRange operands);
using FlatLinearValueConstraints::addBound;
/// Adds a constant bound for the variable associated with the given Value.
void addBound(BoundType type, Value val, int64_t value);
/// The `addBound` overload above hides the inherited overloads by default, so
/// we explicitly introduce them here.
using IntegerPolyhedron::addBound;
/// Returns the constraint system as an integer set. Returns a null integer
/// set if the system has no constraints, or if an integer set couldn't be
/// constructed as a result of a local variable's explicit representation not
/// being known and such a local variable appearing in any of the constraints.
IntegerSet getAsIntegerSet(MLIRContext *context) const;
/// Computes the lower and upper bounds of the first `num` dimensional
/// variables (starting at `offset`) as an affine map of the remaining
/// variables (dimensional and symbolic). This method is able to detect
/// variables as floordiv's and mod's of affine expressions of other
/// variables with respect to (positive) constants. Sets bound map to a
/// null AffineMap if such a bound can't be found (or yet unimplemented).
///
/// By default the returned lower bounds are closed and upper bounds are open.
/// If `closedUb` is true, the upper bound is closed.
void getSliceBounds(unsigned offset, unsigned num, MLIRContext *context,
SmallVectorImpl<AffineMap> *lbMaps,
SmallVectorImpl<AffineMap> *ubMaps,
bool closedUB = false);
/// Composes an affine map whose dimensions and symbols match one to one with
/// the dimensions and symbols of this FlatAffineConstraints. The results of
/// the map `other` are added as the leading dimensions of this constraint
/// system. Returns failure if `other` is a semi-affine map.
LogicalResult composeMatchingMap(AffineMap other);
/// Gets the lower and upper bound of the `offset` + `pos`th variable
/// treating [0, offset) U [offset + num, symStartPos) as dimensions and
/// [symStartPos, getNumDimAndSymbolVars) as symbols, and `pos` lies in
/// [0, num). The multi-dimensional maps in the returned pair represent the
/// max and min of potentially multiple affine expressions. `localExprs` holds
/// pre-computed AffineExpr's for all local variables in the system.
///
/// By default the returned lower bounds are closed and upper bounds are open.
/// If `closedUb` is true, the upper bound is closed.
std::pair<AffineMap, AffineMap>
getLowerAndUpperBound(unsigned pos, unsigned offset, unsigned num,
unsigned symStartPos, ArrayRef<AffineExpr> localExprs,
MLIRContext *context, bool closedUB = false) const;
/// Returns the bound for the variable at `pos` from the inequality at
/// `ineqPos` as a 1-d affine value map (affine map + operands). The returned
/// affine value map can either be a lower bound or an upper bound depending
/// on the sign of atIneq(ineqPos, pos). Asserts if the row at `ineqPos` does
/// not involve the `pos`th variable.
void getIneqAsAffineValueMap(unsigned pos, unsigned ineqPos,
AffineValueMap &vmap,
MLIRContext *context) const;
/// Add the specified values as a dim or symbol var depending on its nature,
/// if it already doesn't exist in the system. `val` has to be either a
/// terminal symbol or a loop IV, i.e., it cannot be the result affine.apply
/// of any symbols or loop IVs. The variable is added to the end of the
/// existing dims or symbols. Additional information on the variable is
/// extracted from the IR and added to the constraint system.
void addInductionVarOrTerminalSymbol(Value val);
/// Adds slice lower bounds represented by lower bounds in `lbMaps` and upper
/// bounds in `ubMaps` to each variable in the constraint system which has
@ -292,79 +123,17 @@ public:
ArrayRef<AffineMap> ubMaps,
ArrayRef<Value> operands);
/// Looks up the position of the variable with the specified Value. Returns
/// true if found (false otherwise). `pos` is set to the (column) position of
/// the variable.
bool findVar(Value val, unsigned *pos) const;
/// Changes all symbol variables which are loop IVs to dim variables.
void convertLoopIVSymbolsToDims();
/// Returns true if an variable with the specified Value exists, false
/// otherwise.
bool containsVar(Value val) const;
/// Swap the posA^th variable with the posB^th variable.
void swapVar(unsigned posA, unsigned posB) override;
/// Insert variables of the specified kind at position `pos`. Positions are
/// relative to the kind of variable. The coefficient columns corresponding
/// to the added variables are initialized to zero. `vals` are the Values
/// corresponding to the variables. Values should not be used with
/// VarKind::Local since values can only be attached to non-local variables.
/// Return the absolute column position (i.e., not relative to the kind of
/// variable) of the first added variable.
///
/// Note: Empty Values are allowed in `vals`.
unsigned insertDimVar(unsigned pos, unsigned num = 1) {
return insertVar(VarKind::SetDim, pos, num);
}
unsigned insertSymbolVar(unsigned pos, unsigned num = 1) {
return insertVar(VarKind::Symbol, pos, num);
}
unsigned insertLocalVar(unsigned pos, unsigned num = 1) {
return insertVar(VarKind::Local, pos, num);
}
unsigned insertDimVar(unsigned pos, ValueRange vals);
unsigned insertSymbolVar(unsigned pos, ValueRange vals);
unsigned insertVar(presburger::VarKind kind, unsigned pos,
unsigned num = 1) override;
unsigned insertVar(presburger::VarKind kind, unsigned pos, ValueRange vals);
/// Append variables of the specified kind after the last variable of that
/// kind. The coefficient columns corresponding to the added variables are
/// initialized to zero. `vals` are the Values corresponding to the
/// variables. Return the absolute column position (i.e., not relative to the
/// kind of variable) of the first appended variable.
///
/// Note: Empty Values are allowed in `vals`.
unsigned appendDimVar(ValueRange vals);
unsigned appendSymbolVar(ValueRange vals);
unsigned appendDimVar(unsigned num = 1) {
return appendVar(VarKind::SetDim, num);
}
unsigned appendSymbolVar(unsigned num = 1) {
return appendVar(VarKind::Symbol, num);
}
unsigned appendLocalVar(unsigned num = 1) {
return appendVar(VarKind::Local, num);
}
/// Removes variables in the column range [varStart, varLimit), and copies any
/// remaining valid data into place, updates member variables, and resizes
/// arrays as needed.
void removeVarRange(presburger::VarKind kind, unsigned varStart,
unsigned varLimit) override;
using IntegerPolyhedron::removeVarRange;
/// Add the specified values as a dim or symbol var depending on its nature,
/// if it already doesn't exist in the system. `val` has to be either a
/// terminal symbol or a loop IV, i.e., it cannot be the result affine.apply
/// of any symbols or loop IVs. The variable is added to the end of the
/// existing dims or symbols. Additional information on the variable is
/// extracted from the IR and added to the constraint system.
void addInductionVarOrTerminalSymbol(Value val);
/// Align `map` with this constraint system based on `operands`. Each operand
/// must already have a corresponding dim/symbol in this constraint system.
AffineMap computeAlignedMap(AffineMap map, ValueRange operands) const;
/// Returns the bound for the variable at `pos` from the inequality at
/// `ineqPos` as a 1-d affine value map (affine map + operands). The returned
/// affine value map can either be a lower bound or an upper bound depending
/// on the sign of atIneq(ineqPos, pos). Asserts if the row at `ineqPos` does
/// not involve the `pos`th variable.
void getIneqAsAffineValueMap(unsigned pos, unsigned ineqPos,
AffineValueMap &vmap,
MLIRContext *context) const;
/// Composes the affine value map with this FlatAffineValueConstrains, adding
/// the results of the map as dimensions at the front
@ -373,168 +142,10 @@ public:
///
/// Returns failure if the composition fails (when vMap is a semi-affine map).
/// The vMap's operand Value's are used to look up the right positions in
/// the FlatAffineConstraints with which to associate. Every operand of vMap
/// should have a matching dim/symbol column in this constraint system (with
/// the same associated Value).
/// the FlatAffineValueConstraints with which to associate. Every operand of
/// vMap should have a matching dim/symbol column in this constraint system
/// (with the same associated Value).
LogicalResult composeMap(const AffineValueMap *vMap);
/// Projects out the variable that is associate with Value.
void projectOut(Value val);
using IntegerPolyhedron::projectOut;
/// Changes all symbol variables which are loop IVs to dim variables.
void convertLoopIVSymbolsToDims();
/// Updates the constraints to be the smallest bounding (enclosing) box that
/// contains the points of `this` set and that of `other`, with the symbols
/// being treated specially. For each of the dimensions, the min of the lower
/// bounds (symbolic) and the max of the upper bounds (symbolic) is computed
/// to determine such a bounding box. `other` is expected to have the same
/// dimensional variables as this constraint system (in the same order).
///
/// E.g.:
/// 1) this = {0 <= d0 <= 127},
/// other = {16 <= d0 <= 192},
/// output = {0 <= d0 <= 192}
/// 2) this = {s0 + 5 <= d0 <= s0 + 20},
/// other = {s0 + 1 <= d0 <= s0 + 9},
/// output = {s0 + 1 <= d0 <= s0 + 20}
/// 3) this = {0 <= d0 <= 5, 1 <= d1 <= 9}
/// other = {2 <= d0 <= 6, 5 <= d1 <= 15},
/// output = {0 <= d0 <= 6, 1 <= d1 <= 15}
LogicalResult unionBoundingBox(const FlatAffineValueConstraints &other);
using IntegerPolyhedron::unionBoundingBox;
/// Merge and align the variables of `this` and `other` starting at
/// `offset`, so that both constraint systems get the union of the contained
/// variables that is dimension-wise and symbol-wise unique; both
/// constraint systems are updated so that they have the union of all
/// variables, with `this`'s original variables appearing first followed
/// by any of `other`'s variables that didn't appear in `this`. Local
/// variables in `other` that have the same division representation as local
/// variables in `this` are merged into one.
// E.g.: Input: `this` has (%i, %j) [%M, %N]
// `other` has (%k, %j) [%P, %N, %M]
// Output: both `this`, `other` have (%i, %j, %k) [%M, %N, %P]
//
void mergeAndAlignVarsWithOther(unsigned offset,
FlatAffineValueConstraints *other);
/// Returns true if this constraint system and `other` are in the same
/// space, i.e., if they are associated with the same set of variables,
/// appearing in the same order. Returns false otherwise.
bool areVarsAlignedWithOther(const FlatAffineValueConstraints &other);
/// Replaces the contents of this FlatAffineValueConstraints with `other`.
void clearAndCopyFrom(const IntegerRelation &other) override;
/// Returns the Value associated with the pos^th variable. Asserts if
/// no Value variable was associated.
inline Value getValue(unsigned pos) const {
assert(pos < getNumDimAndSymbolVars() && "Invalid position");
assert(hasValue(pos) && "variable's Value not set");
return *values[pos];
}
/// Returns true if the pos^th variable has an associated Value.
inline bool hasValue(unsigned pos) const {
assert(pos < getNumDimAndSymbolVars() && "Invalid position");
return values[pos].has_value();
}
/// Returns true if at least one variable has an associated Value.
bool hasValues() const;
/// Returns the Values associated with variables in range [start, end).
/// Asserts if no Value was associated with one of these variables.
inline void getValues(unsigned start, unsigned end,
SmallVectorImpl<Value> *values) const {
assert(end <= getNumDimAndSymbolVars() && "invalid end position");
assert(start <= end && "invalid start position");
values->clear();
values->reserve(end - start);
for (unsigned i = start; i < end; i++)
values->push_back(getValue(i));
}
inline void getAllValues(SmallVectorImpl<Value> *values) const {
getValues(0, getNumDimAndSymbolVars(), values);
}
inline ArrayRef<std::optional<Value>> getMaybeValues() const {
return {values.data(), values.size()};
}
inline ArrayRef<std::optional<Value>>
getMaybeValues(presburger::VarKind kind) const {
assert(kind != VarKind::Local &&
"Local variables do not have any value attached to them.");
return {values.data() + getVarKindOffset(kind), getNumVarKind(kind)};
}
/// Sets the Value associated with the pos^th variable.
inline void setValue(unsigned pos, Value val) {
assert(pos < getNumDimAndSymbolVars() && "invalid var position");
values[pos] = val;
}
/// Sets the Values associated with the variables in the range [start, end).
/// The range must contain only dim and symbol variables.
void setValues(unsigned start, unsigned end, ArrayRef<Value> values) {
assert(end <= getNumVars() && "invalid end position");
assert(start <= end && "invalid start position");
assert(values.size() == end - start &&
"value should be provided for each variable in the range.");
for (unsigned i = start; i < end; ++i)
setValue(i, values[i - start]);
}
/// Merge and align symbols of `this` and `other` such that both get union of
/// of symbols that are unique. Symbols in `this` and `other` should be
/// unique. Symbols with Value as `None` are considered to be inequal to all
/// other symbols.
void mergeSymbolVars(FlatAffineValueConstraints &other);
protected:
using VarKind = presburger::VarKind;
/// Returns false if the fields corresponding to various variable counts, or
/// equality/inequality buffer sizes aren't consistent; true otherwise. This
/// is meant to be used within an assert internally.
bool hasConsistentState() const override;
/// Given an affine map that is aligned with this constraint system:
/// * Flatten the map.
/// * Add newly introduced local columns at the beginning of this constraint
/// system (local column pos 0).
/// * Add equalities that define the new local columns to this constraint
/// system.
/// * Return the flattened expressions via `flattenedExprs`.
///
/// Note: This is a shared helper function of `addLowerOrUpperBound` and
/// `composeMatchingMap`.
LogicalResult flattenAlignedMapAndMergeLocals(
AffineMap map, std::vector<SmallVector<int64_t, 8>> *flattenedExprs);
/// Eliminates the variable at the specified position using Fourier-Motzkin
/// variable elimination, but uses Gaussian elimination if there is an
/// equality involving that variable. If the result of the elimination is
/// integer exact, `*isResultIntegerExact` is set to true. If `darkShadow` is
/// set to true, a potential under approximation (subset) of the rational
/// shadow / exact integer shadow is computed.
// See implementation comments for more details.
void fourierMotzkinEliminate(unsigned pos, bool darkShadow = false,
bool *isResultIntegerExact = nullptr) override;
/// Prints the number of constraints, dimensions, symbols and locals in the
/// FlatAffineConstraints. Also, prints for each variable whether there is
/// an SSA Value attached to it.
void printSpace(raw_ostream &os) const override;
/// Values corresponding to the (column) non-local variables of this
/// constraint system appearing in the order the variables correspond to
/// columns. Variables that aren't associated with any Value are set to
/// None.
SmallVector<std::optional<Value>, 8> values;
};
/// A FlatAffineRelation represents a set of ordered pairs (domain -> range)
@ -570,6 +181,13 @@ public:
: FlatAffineValueConstraints(fac), numDomainDims(numDomainDims),
numRangeDims(numRangeDims) {}
/// Return the kind of this object.
Kind getKind() const override { return Kind::FlatAffineRelation; }
static bool classof(const IntegerRelation *cst) {
return cst->getKind() == Kind::FlatAffineRelation;
}
/// Returns a set corresponding to the domain/range of the affine relation.
FlatAffineValueConstraints getDomainSet() const;
FlatAffineValueConstraints getRangeSet() const;
@ -616,66 +234,6 @@ protected:
unsigned numRangeDims;
};
/// Flattens 'expr' into 'flattenedExpr', which contains the coefficients of the
/// dimensions, symbols, and additional variables that represent floor divisions
/// of dimensions, symbols, and in turn other floor divisions. Returns failure
/// if 'expr' could not be flattened (i.e., semi-affine is not yet handled).
/// 'cst' contains constraints that connect newly introduced local variables
/// to existing dimensional and symbolic variables. See documentation for
/// AffineExprFlattener on how mod's and div's are flattened.
LogicalResult getFlattenedAffineExpr(AffineExpr expr, unsigned numDims,
unsigned numSymbols,
SmallVectorImpl<int64_t> *flattenedExpr,
FlatAffineValueConstraints *cst = nullptr);
/// Flattens the result expressions of the map to their corresponding flattened
/// forms and set in 'flattenedExprs'. Returns failure if any expression in the
/// map could not be flattened (i.e., semi-affine is not yet handled). 'cst'
/// contains constraints that connect newly introduced local variables to
/// existing dimensional and / symbolic variables. See documentation for
/// AffineExprFlattener on how mod's and div's are flattened. For all affine
/// expressions that share the same operands (like those of an affine map), this
/// method should be used instead of repeatedly calling getFlattenedAffineExpr
/// since local variables added to deal with div's and mod's will be reused
/// across expressions.
LogicalResult
getFlattenedAffineExprs(AffineMap map,
std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
FlatAffineValueConstraints *cst = nullptr);
LogicalResult
getFlattenedAffineExprs(IntegerSet set,
std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
FlatAffineValueConstraints *cst = nullptr);
LogicalResult
getMultiAffineFunctionFromMap(AffineMap map,
presburger::MultiAffineFunction &multiAff);
/// Re-indexes the dimensions and symbols of an affine map with given `operands`
/// values to align with `dims` and `syms` values.
///
/// Each dimension/symbol of the map, bound to an operand `o`, is replaced with
/// dimension `i`, where `i` is the position of `o` within `dims`. If `o` is not
/// in `dims`, replace it with symbol `i`, where `i` is the position of `o`
/// within `syms`. If `o` is not in `syms` either, replace it with a new symbol.
///
/// Note: If a value appears multiple times as a dimension/symbol (or both), all
/// corresponding dim/sym expressions are replaced with the first dimension
/// bound to that value (or first symbol if no such dimension exists).
///
/// The resulting affine map has `dims.size()` many dimensions and at least
/// `syms.size()` many symbols.
///
/// The SSA values of the symbols of the resulting map are optionally returned
/// via `newSyms`. This is a concatenation of `syms` with the SSA values of the
/// newly added symbols.
///
/// Note: As part of this re-indexing, dimensions may turn into symbols, or vice
/// versa.
AffineMap alignAffineMapWithValues(AffineMap map, ValueRange operands,
ValueRange dims, ValueRange syms,
SmallVector<Value> *newSyms = nullptr);
/// Builds a relation from the given AffineMap/AffineValueMap `map`, containing
/// all pairs of the form `operands -> result` that satisfy `map`. `rel` is set
/// to the relation built. For example, give the AffineMap:
@ -696,6 +254,6 @@ LogicalResult getRelationFromMap(AffineMap &map, FlatAffineRelation &rel);
LogicalResult getRelationFromMap(const AffineValueMap &map,
FlatAffineRelation &rel);
} // namespace mlir.
} // namespace mlir
#endif // MLIR_DIALECT_AFFINE_ANALYSIS_AFFINESTRUCTURES_H

View File

@ -324,7 +324,7 @@ private:
// A floordiv is thus flattened by introducing a new local variable q, and
// replacing that expression with 'q' while adding the constraints
// c * q <= expr <= c * q + c - 1 to localVarCst (done by
// FlatAffineConstraints::addLocalFloorDiv).
// IntegerRelation::addLocalFloorDiv).
//
// A ceildiv is similarly flattened:
// t = expr ceildiv c <=> t = (expr + c - 1) floordiv c

View File

@ -17,7 +17,7 @@
// This class is not meant for affine analysis and operations like set
// operations, emptiness checks, or other math operations for analysis and
// transformation. For the latter, use FlatAffineConstraints.
// transformation. For the latter, use FlatAffineValueConstraints.
//
//===----------------------------------------------------------------------===//

View File

@ -2,6 +2,7 @@ set(LLVM_OPTIONAL_SOURCES
AliasAnalysis.cpp
CallGraph.cpp
DataLayoutAnalysis.cpp
FlatLinearValueConstraints.cpp
Liveness.cpp
SliceAnalysis.cpp
@ -14,11 +15,14 @@ set(LLVM_OPTIONAL_SOURCES
DataFlow/SparseAnalysis.cpp
)
add_subdirectory(Presburger)
add_mlir_library(MLIRAnalysis
AliasAnalysis.cpp
CallGraph.cpp
DataFlowFramework.cpp
DataLayoutAnalysis.cpp
FlatLinearValueConstraints.cpp
Liveness.cpp
SliceAnalysis.cpp
@ -43,8 +47,8 @@ add_mlir_library(MLIRAnalysis
MLIRInferIntRangeInterface
MLIRInferTypeOpInterface
MLIRLoopLikeInterface
MLIRPresburger
MLIRSideEffectInterfaces
MLIRViewLikeInterface
)
add_subdirectory(Presburger)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1290,7 +1290,7 @@ void SimpleAffineExprFlattener::addLocalVariableSemiAffine(
// A floordiv is thus flattened by introducing a new local variable q, and
// replacing that expression with 'q' while adding the constraints
// c * q <= expr <= c * q + c - 1 to localVarCst (done by
// FlatAffineConstraints::addLocalFloorDiv).
// IntegerRelation::addLocalFloorDiv).
//
// A ceildiv is similarly flattened:
// t = expr ceildiv c <=> t = (expr + c - 1) floordiv c

View File

@ -829,7 +829,7 @@ bool MutableAffineMap::isMultipleOf(unsigned idx, int64_t factor) const {
if (results[idx].isMultipleOf(factor))
return true;
// TODO: use simplifyAffineExpr and FlatAffineConstraints to
// TODO: use simplifyAffineExpr and FlatAffineValueConstraints to
// complete this (for a more powerful analysis).
return false;
}

View File

@ -201,7 +201,7 @@ func.func @out_of_bounds() {
// This test case accesses within bounds. Without removal of a certain type of
// trivially redundant constraints (those differing only in their constant
// term), the number of constraints here explodes, and this would return out of
// bounds errors conservatively due to FlatAffineConstraints::kExplosionFactor.
// bounds errors conservatively due to IntegerRelation::kExplosionFactor.
#map3 = affine_map<(d0, d1) -> ((d0 * 72 + d1) floordiv 2304 + ((((d0 * 72 + d1) mod 2304) mod 1152) mod 9) floordiv 3)>
#map4 = affine_map<(d0, d1) -> ((d0 * 72 + d1) mod 2304 - (((d0 * 72 + d1) mod 2304) floordiv 1152) * 1151 - ((((d0 * 72 + d1) mod 2304) mod 1152) floordiv 9) * 9 - (((((d0 * 72 + d1) mod 2304) mod 1152) mod 9) floordiv 3) * 3)>
#map5 = affine_map<(d0, d1) -> (((((d0 * 72 + d1) mod 2304) mod 1152) floordiv 9) floordiv 8)>

View File

@ -636,7 +636,7 @@ func.func @mod_deps() {
affine.for %i0 = 0 to 10 {
%a0 = affine.apply affine_map<(d0) -> (d0 mod 2)> (%i0)
// Results are conservative here since we currently don't have a way to
// represent strided sets in FlatAffineConstraints.
// represent strided sets in FlatAffineValueConstraints.
%v0 = affine.load %m[%a0] : memref<100xf32>
// expected-remark@above {{dependence from 0 to 0 at depth 1 = false}}
// expected-remark@above {{dependence from 0 to 0 at depth 2 = false}}