25#include "llvm/ADT/TypeSwitch.h"
26#include "llvm/Support/Debug.h"
27#include "llvm/Support/raw_ostream.h"
30#define DEBUG_TYPE "affine-analysis"
42 arith::AtomicRMWKind &kind) {
50 if (combinerOps.size() > 1)
53 Operation *combinerOp = combinerOps.back();
54 std::optional<arith::AtomicRMWKind> maybeKind =
56 .Case([](arith::AddFOp) {
return arith::AtomicRMWKind::addf; })
57 .Case([](arith::MulFOp) {
return arith::AtomicRMWKind::mulf; })
58 .Case([](arith::AddIOp) {
return arith::AtomicRMWKind::addi; })
59 .Case([](arith::AndIOp) {
return arith::AtomicRMWKind::andi; })
60 .Case([](arith::OrIOp) {
return arith::AtomicRMWKind::ori; })
61 .Case([](arith::MulIOp) {
return arith::AtomicRMWKind::muli; })
63 [](arith::MinimumFOp) {
return arith::AtomicRMWKind::minimumf; })
65 [](arith::MaximumFOp) {
return arith::AtomicRMWKind::maximumf; })
66 .Case([](arith::MinSIOp) {
return arith::AtomicRMWKind::mins; })
67 .Case([](arith::MaxSIOp) {
return arith::AtomicRMWKind::maxs; })
68 .Case([](arith::MinUIOp) {
return arith::AtomicRMWKind::minu; })
69 .Case([](arith::MaxUIOp) {
return arith::AtomicRMWKind::maxu; })
70 .Case([](arith::XOrIOp) {
return arith::AtomicRMWKind::xori; })
71 .Case([](arith::MaxNumFOp) {
return arith::AtomicRMWKind::maxnumf; })
72 .Case([](arith::MinNumFOp) {
return arith::AtomicRMWKind::minnumf; })
73 .Default([](
Operation *) -> std::optional<arith::AtomicRMWKind> {
86 unsigned numIterArgs = forOp.getNumIterOperands();
89 supportedReductions.reserve(numIterArgs);
90 for (
unsigned i = 0; i < numIterArgs; ++i) {
91 arith::AtomicRMWKind kind;
93 supportedReductions.emplace_back(
LoopReduction{kind, i, value});
100bool mlir::affine::isLoopParallel(
102 unsigned numIterArgs = forOp.getNumIterOperands();
106 if (numIterArgs > 0 && !parallelReductions)
110 if (parallelReductions) {
114 if (parallelReductions->size() != numIterArgs)
134 auto viewOp = dyn_cast<ViewLikeOpInterface>(defOp);
135 return viewOp && isLocallyDefined(viewOp.getViewSource(), enclosingOp);
140 if (llvm::any_of(forOp.getResultTypes(), llvm::IsaPred<BaseMemRefType>))
146 if (
auto readOp = dyn_cast<AffineReadOpInterface>(op)) {
148 if (!isLocallyDefined(readOp.getMemRef(), forOp))
149 loadAndStoreOps.push_back(op);
150 }
else if (
auto writeOp = dyn_cast<AffineWriteOpInterface>(op)) {
152 if (!isLocallyDefined(writeOp.getMemRef(), forOp))
153 loadAndStoreOps.push_back(op);
154 }
else if (!isa<AffineForOp, AffineYieldOp, AffineIfOp>(op) &&
166 if (walkResult.wasInterrupted())
173 for (
auto *srcOp : loadAndStoreOps) {
175 for (
auto *dstOp : loadAndStoreOps) {
197 unsigned operandIndex;
200 for (
auto operand : operands) {
201 worklist.push_back({operand, 0});
204 while (!worklist.empty()) {
205 State &state = worklist.back();
206 auto *opInst = state.value.getDefiningOp();
209 if (!isa_and_nonnull<AffineApplyOp>(opInst)) {
214 if (state.operandIndex == 0) {
216 affineApplyOps.push_back(opInst);
218 if (state.operandIndex < opInst->getNumOperands()) {
221 auto nextOperand = opInst->getOperand(state.operandIndex);
223 ++state.operandIndex;
225 worklist.push_back({nextOperand, 0});
247 if (!isa<AffineForOp, AffineIfOp, AffineParallelOp>(op)) {
248 LLVM_DEBUG(llvm::dbgs() <<
"getIndexSet only handles affine.for/if/"
252 if (AffineForOp forOp = dyn_cast<AffineForOp>(op)) {
253 loopOps.push_back(forOp);
256 }
else if (AffineParallelOp parallelOp = dyn_cast<AffineParallelOp>(op)) {
257 loopOps.push_back(parallelOp);
258 numDims += parallelOp.getNumDims();
267 if (AffineForOp forOp = dyn_cast<AffineForOp>(op)) {
270 }
else if (
auto ifOp = dyn_cast<AffineIfOp>(op)) {
272 }
else if (
auto parallelOp = dyn_cast<AffineParallelOp>(op))
298 unsigned minNumLoops =
300 unsigned numCommonLoops = 0;
301 for (
unsigned i = 0; i < minNumLoops; ++i) {
308 if (commonLoops !=
nullptr)
312 if (commonLoops !=
nullptr)
313 assert(commonLoops->size() == numCommonLoops);
314 return numCommonLoops;
324 auto getChainOfAncestorBlocks =
331 ancestorBlocks.push_back(currBlock);
335 "parent op starting an affine scope is always expected");
336 ancestorBlocks.push_back(currBlock);
341 getChainOfAncestorBlocks(opA, srcAncestorBlocks);
342 getChainOfAncestorBlocks(opB, dstAncestorBlocks);
344 Block *commonBlock =
nullptr;
345 for (
int i = srcAncestorBlocks.size() - 1,
j = dstAncestorBlocks.size() - 1;
346 i >= 0 &&
j >= 0 && srcAncestorBlocks[i] == dstAncestorBlocks[
j];
348 commonBlock = srcAncestorBlocks[i];
362 assert(commonBlock &&
363 "ops expected to have a common surrounding block in affine scope");
368 assert(srcOp &&
"src access op must lie in common block");
370 assert(dstOp &&
"dest access op must lie in common block");
387 unsigned numCols = dependenceDomain->
getNumCols();
391 unsigned numCommonLoopConstraints = std::min(numCommonLoops, loopDepth);
392 for (
unsigned i = 0; i < numCommonLoopConstraints; ++i) {
395 eq[i + numSrcDims] = 1;
396 if (i == loopDepth - 1) {
397 eq[numCols - 1] = -1;
416 unsigned numCommonLoops =
418 if (numCommonLoops == 0)
421 unsigned numIdsToEliminate = dependenceDomain->
getNumVars();
424 dependenceDomain->
insertVar(VarKind::SetDim, 0,
434 for (
unsigned j = 0;
j < numCommonLoops; ++
j) {
437 eq[
j + numCommonLoops] = 1;
438 eq[
j + numCommonLoops + numSrcDims] = -1;
443 dependenceDomain->
projectOut(numCommonLoops, numIdsToEliminate);
447 dependenceComponents->resize(numCommonLoops);
448 for (
unsigned j = 0;
j < numCommonLoops; ++
j) {
449 (*dependenceComponents)[
j].op = commonLoops[
j].getOperation();
451 (*dependenceComponents)[
j].lb =
452 lbConst.value_or(std::numeric_limits<int64_t>::min());
454 (*dependenceComponents)[
j].ub =
455 ubConst.value_or(std::numeric_limits<int64_t>::max());
470 unsigned inserts = 0;
471 for (
unsigned i = 0, e = domain.
getNumDimVars(); i < e; ++i) {
475 const Identifier *itr = std::find(findBegin, findEnd, domainIdi);
476 if (itr != findEnd) {
477 rel.
swapVar(i, i + std::distance(findBegin, itr));
481 rel.
setId(VarKind::SetDim, i, domainIdi);
519 if (
auto loadOp = dyn_cast<AffineReadOpInterface>(
opInst))
520 map = loadOp.getAffineMap();
522 map = cast<AffineWriteOpInterface>(
opInst).getAffineMap();
528 accessMap->
reset(map, operands);
621 LLVM_DEBUG(llvm::dbgs() <<
"Checking for dependence at depth: "
622 << Twine(loopDepth) <<
" between:\n";);
632 if (!allowRAR && !isa<AffineWriteOpInterface>(srcAccess.
opInst) &&
633 !isa<AffineWriteOpInterface>(dstAccess.
opInst))
661 assert(loopDepth <= numCommonLoops + 1 &&
"Invalid depth");
662 if (!allowRAR && loopDepth > numCommonLoops &&
668 dependenceComponents);
699 if (dependenceDomain.
isEmpty())
703 if (dependenceComponents !=
nullptr)
705 dependenceComponents);
707 LLVM_DEBUG(llvm::dbgs() <<
"Dependence polyhedron:\n");
708 LLVM_DEBUG(dependenceDomain.
dump());
711 if (dependenceConstraints)
712 *dependenceConstraints =
result;
719 AffineForOp forOp,
unsigned maxLoopDepth,
724 if (isa<AffineReadOpInterface, AffineWriteOpInterface>(op))
725 loadAndStoreOps.push_back(op);
728 unsigned numOps = loadAndStoreOps.size();
729 for (
unsigned d = 1; d <= maxLoopDepth; ++d) {
730 for (
unsigned i = 0; i < numOps; ++i) {
731 auto *srcOp = loadAndStoreOps[i];
733 for (
unsigned j = 0;
j < numOps; ++
j) {
734 auto *dstOp = loadAndStoreOps[
j];
741 srcAccess, dstAccess, d,
nullptr,
744 depCompsVec->push_back(depComps);
static LogicalResult getOpIndexSet(Operation *op, FlatAffineValueConstraints *indexSet)
Computes the iteration domain for 'op' and populates 'indexSet', which encapsulates the constraints i...
static Value getSupportedReduction(AffineForOp forOp, unsigned pos, arith::AtomicRMWKind &kind)
Get the value that is being reduced by pos-th reduction in the loop if such a reduction can be perfor...
static void computeDirectionVector(const FlatAffineValueConstraints &srcDomain, const FlatAffineValueConstraints &dstDomain, unsigned loopDepth, IntegerPolyhedron *dependenceDomain, SmallVector< DependenceComponent, 2 > *dependenceComponents)
static unsigned getNumCommonLoops(const FlatAffineValueConstraints &srcDomain, const FlatAffineValueConstraints &dstDomain, SmallVectorImpl< AffineForOp > *commonLoops=nullptr)
static bool srcAppearsBeforeDstInAncestralBlock(const MemRefAccess &srcAccess, const MemRefAccess &dstAccess)
Returns true if the ancestor operation of 'srcAccess' appears before the ancestor operation of 'dstAc...
static Block * getCommonBlockInAffineScope(Operation *opA, Operation *opB)
Returns the closest surrounding block common to opA and opB. opA and opB should be in the same affine...
static void addOrderingConstraints(const FlatAffineValueConstraints &srcDomain, const FlatAffineValueConstraints &dstDomain, unsigned loopDepth, IntegerRelation *dependenceDomain)
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Block represents an ordered list of Operations.
Operation * findAncestorOpInBlock(Operation &op)
Returns 'op' if 'op' lies in this block, or otherwise finds the ancestor operation of 'op' that lies ...
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Value getValue(unsigned pos) const
Returns the Value associated with the pos^th variable.
A trait of region holding operations that defines a new scope for polyhedral optimization purposes.
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
Block * getBlock()
Returns the operation block that contains this operation.
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
static WalkResult interrupt()
An AffineValueMap is an affine map plus its ML value operands and results for analysis purposes.
unsigned getNumDims() const
void reset(AffineMap map, ValueRange operands, ValueRange results={})
unsigned getNumResults() const
FlatAffineValueConstraints is an extension of FlatLinearValueConstraints with helper functions for Af...
void addAffineIfOpDomain(AffineIfOp ifOp)
Adds constraints imposed by the affine.if operation.
LogicalResult addAffineParallelOpDomain(AffineParallelOp parallelOp)
Add constraints (lower and upper bounds) for the specified 'affine.parallel' operation's Value using ...
LogicalResult addAffineForOpDomain(AffineForOp forOp)
Adds constraints (lower and upper bounds) for the specified 'affine.for' operation's Value using IR i...
An Identifier stores a pointer to an object, such as a Value or an Operation.
An IntegerPolyhedron represents the set of points from a PresburgerSpace that satisfy a list of affin...
unsigned insertVar(VarKind kind, unsigned pos, unsigned num=1) override
Insert num variables of the specified kind at position pos.
An IntegerRelation represents the set of points from a PresburgerSpace that satisfy a list of affine ...
void setId(VarKind kind, unsigned i, Identifier id)
Set the identifier for the ith variable of the specified kind of the IntegerRelation's PresburgerSpac...
virtual void swapVar(unsigned posA, unsigned posB)
Swap the posA^th variable with the posB^th variable.
ArrayRef< Identifier > getIds(VarKind kind)
Get the identifiers for the variables of specified varKind.
void convertVarKind(VarKind srcKind, unsigned varStart, unsigned varLimit, VarKind dstKind, unsigned pos)
Converts variables of kind srcKind in the range [varStart, varLimit) to variables of kind dstKind.
unsigned appendVar(VarKind kind, unsigned num=1)
Append num variables of the specified kind after the last variable of that kind.
virtual unsigned insertVar(VarKind kind, unsigned pos, unsigned num=1)
Insert num variables of the specified kind at position pos.
unsigned getNumDomainVars() const
const PresburgerSpace & getSpace() const
Returns a reference to the underlying space.
unsigned getNumVars() const
void inverse()
Invert the relation i.e., swap its domain and range.
void append(const IntegerRelation &other)
Appends constraints from other into this.
void addEquality(ArrayRef< DynamicAPInt > eq)
Adds an equality from the coefficients specified in eq.
bool isEmpty() const
Checks for emptiness by performing variable elimination on all variables, running the GCD test on eac...
unsigned getNumCols() const
Returns the number of columns in the constraint system.
void mergeAndCompose(const IntegerRelation &other)
Given a relation other: (A -> B), this operation merges the symbol and local variables and then takes...
IntegerPolyhedron getDomainSet() const
Return a set corresponding to all points in the domain of the relation.
void projectOut(unsigned pos, unsigned num)
Projects out (aka eliminates) num variables starting at position pos.
void addInequality(ArrayRef< DynamicAPInt > inEq)
Adds an inequality (>= 0) from the coefficients specified in inEq.
void mergeAndAlignSymbols(IntegerRelation &other)
Merge and align symbol variables of this and other with respect to identifiers.
unsigned mergeLocalVars(IntegerRelation &other)
Adds additional local vars to the sets such that they both have the union of the local vars in each s...
std::optional< int64_t > getConstantBound64(BoundType type, unsigned pos) const
The same, but casts to int64_t.
unsigned getNumDimVars() const
PresburgerSpace is the space of all possible values of a tuple of integer valued variables/variables.
bool isUsingIds() const
Returns if identifiers are being used.
static PresburgerSpace getRelationSpace(unsigned numDomain=0, unsigned numRange=0, unsigned numSymbols=0, unsigned numLocals=0)
LogicalResult getAccessRelation(const AffineValueMap &accessValueMap, const FlatAffineValueConstraints &domain, presburger::IntegerRelation &rel)
Builds in rel the access relation of an access that reads or writes accessValueMap over the iteration...
void getEnclosingAffineOps(Operation &op, SmallVectorImpl< Operation * > *ops)
Populates 'ops' with affine operations enclosing op ordered from outermost to innermost while stoppin...
LogicalResult getIndexSet(MutableArrayRef< Operation * > ops, FlatAffineValueConstraints *domain)
Builds a system of constraints with dimensional variables corresponding to the loop IVs of the forOps...
AffineForOp getForInductionVarOwner(Value val)
Returns the loop parent of an induction variable.
void getSupportedReductions(AffineForOp forOp, SmallVectorImpl< LoopReduction > &supportedReductions)
Populate supportedReductions with descriptors of the supported reductions.
DependenceResult checkMemrefAccessDependence(const MemRefAccess &srcAccess, const MemRefAccess &dstAccess, unsigned loopDepth, FlatAffineValueConstraints *dependenceConstraints=nullptr, SmallVector< DependenceComponent, 2 > *dependenceComponents=nullptr, bool allowRAR=false)
void canonicalizeMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands)
Modifies both map and operands in-place so as to:
void getReachableAffineApplyOps(ArrayRef< Value > operands, SmallVectorImpl< Operation * > &affineApplyOps)
Returns in affineApplyOps, the sequence of those AffineApplyOp Operations that are reachable via a se...
bool isAffineForInductionVar(Value val)
Returns true if the provided value is the induction variable of an AffineForOp.
void getDependenceComponents(AffineForOp forOp, unsigned maxLoopDepth, std::vector< SmallVector< DependenceComponent, 2 > > *depCompsVec)
Returns in 'depCompsVec', dependence components for dependences between all load and store ops in loo...
bool isLoopMemoryParallel(AffineForOp forOp)
Returns true if ‘forOp’ is a parallel loop.
Region * getAffineAnalysisScope(Operation *op)
Returns the closest region enclosing op that is held by a non-affine operation; nullptr if there is n...
void fullyComposeAffineMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands, bool composeAffineMin=false)
Given an affine map map and its input operands, this method composes into map, maps of AffineApplyOps...
LogicalResult getRelationFromMap(AffineMap &map, presburger::IntegerRelation &rel)
Builds a relation from the given AffineMap/AffineValueMap map, containing all pairs of the form opera...
void extractInductionVars(ArrayRef< Operation * > affineOps, SmallVectorImpl< Value > &ivs)
Extracts the induction variables from a list of either AffineForOp or AffineParallelOp and places the...
bool hasDependence(DependenceResult result)
Utility function that returns true if the provided DependenceResult corresponds to a dependence resul...
unsigned getNestingDepth(Operation *op)
Returns the nesting depth of this operation, i.e., the number of loops surrounding this operation.
DependenceResult checkAccessDependence(presburger::IntegerRelation srcRel, presburger::IntegerRelation dstRel, unsigned loopDepth, FlatAffineValueConstraints *dependenceConstraints=nullptr, SmallVector< DependenceComponent, 2 > *dependenceComponents=nullptr)
Checks whether the accesses two relations describe touch the same element, i.e.
bool isAffineParallelInductionVar(Value val)
Returns true if val is the induction variable of an AffineParallelOp.
Include the generated interface declarations.
AffineMap simplifyAffineMap(AffineMap map)
Simplifies an affine map by simplifying its underlying AffineExpr results.
bool hasSingleEffect(Operation *op)
Returns "true" if op has only an effect of type EffectTy.
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
Value matchReduction(ArrayRef< BlockArgument > iterCarriedArgs, unsigned redPos, SmallVectorImpl< Operation * > &combinerOps)
Utility to match a generic reduction given a list of iteration-carried arguments, iterCarriedArgs and...
llvm::TypeSwitch< T, ResultT > TypeSwitch
Checks whether two accesses to the same memref access the same element.
A description of a (parallelizable) reduction in an affine loop.
Encapsulates a memref load or store access information.
SmallVector< Value, 4 > indices
LogicalResult getAccessRelation(presburger::IntegerRelation &accessRel) const
Creates an access relation for the access.
void getAccessMap(AffineValueMap *accessMap) const
Populates 'accessMap' with composition of AffineApplyOps reachable from 'indices'.
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.