24 #include "llvm/ADT/TypeSwitch.h"
25 #include "llvm/Support/Debug.h"
26 #include "llvm/Support/raw_ostream.h"
29 #define DEBUG_TYPE "affine-analysis"
32 using namespace affine;
33 using namespace presburger;
41 arith::AtomicRMWKind &
kind) {
49 if (combinerOps.size() > 1)
52 Operation *combinerOp = combinerOps.back();
53 std::optional<arith::AtomicRMWKind> maybeKind =
55 .Case([](arith::AddFOp) {
return arith::AtomicRMWKind::addf; })
56 .Case([](arith::MulFOp) {
return arith::AtomicRMWKind::mulf; })
57 .Case([](arith::AddIOp) {
return arith::AtomicRMWKind::addi; })
58 .Case([](arith::AndIOp) {
return arith::AtomicRMWKind::andi; })
59 .Case([](arith::OrIOp) {
return arith::AtomicRMWKind::ori; })
60 .Case([](arith::MulIOp) {
return arith::AtomicRMWKind::muli; })
62 [](arith::MinimumFOp) {
return arith::AtomicRMWKind::minimumf; })
64 [](arith::MaximumFOp) {
return arith::AtomicRMWKind::maximumf; })
65 .Case([](arith::MinSIOp) {
return arith::AtomicRMWKind::mins; })
66 .Case([](arith::MaxSIOp) {
return arith::AtomicRMWKind::maxs; })
67 .Case([](arith::MinUIOp) {
return arith::AtomicRMWKind::minu; })
68 .Case([](arith::MaxUIOp) {
return arith::AtomicRMWKind::maxu; })
69 .Default([](
Operation *) -> std::optional<arith::AtomicRMWKind> {
84 unsigned numIterArgs = forOp.getNumIterOperands();
87 supportedReductions.reserve(numIterArgs);
88 for (
unsigned i = 0; i < numIterArgs; ++i) {
89 arith::AtomicRMWKind
kind;
100 unsigned numIterArgs = forOp.getNumIterOperands();
104 if (numIterArgs > 0 && !parallelReductions)
108 if (parallelReductions) {
112 if (parallelReductions->size() != numIterArgs)
127 if (hasSingleEffect<MemoryEffects::Allocate>(defOp, v) &&
132 auto viewOp = dyn_cast<ViewLikeOpInterface>(defOp);
138 if (llvm::any_of(forOp.getResultTypes(), llvm::IsaPred<BaseMemRefType>))
144 if (
auto readOp = dyn_cast<AffineReadOpInterface>(op)) {
147 loadAndStoreOps.push_back(op);
148 }
else if (
auto writeOp = dyn_cast<AffineWriteOpInterface>(op)) {
151 loadAndStoreOps.push_back(op);
152 }
else if (!isa<AffineForOp, AffineYieldOp, AffineIfOp>(op) &&
153 !hasSingleEffect<MemoryEffects::Allocate>(op) &&
164 if (walkResult.wasInterrupted())
171 for (
auto *srcOp : loadAndStoreOps) {
173 for (
auto *dstOp : loadAndStoreOps) {
195 unsigned operandIndex;
198 for (
auto operand : operands) {
199 worklist.push_back({operand, 0});
202 while (!worklist.empty()) {
203 State &state = worklist.back();
204 auto *opInst = state.value.getDefiningOp();
207 if (!isa_and_nonnull<AffineApplyOp>(opInst)) {
212 if (state.operandIndex == 0) {
214 affineApplyOps.push_back(opInst);
216 if (state.operandIndex < opInst->getNumOperands()) {
219 auto nextOperand = opInst->getOperand(state.operandIndex);
221 ++state.operandIndex;
223 worklist.push_back({nextOperand, 0});
245 if (!isa<AffineForOp, AffineIfOp, AffineParallelOp>(op)) {
246 LLVM_DEBUG(llvm::dbgs() <<
"getIndexSet only handles affine.for/if/"
250 if (AffineForOp forOp = dyn_cast<AffineForOp>(op)) {
251 loopOps.push_back(forOp);
254 }
else if (AffineParallelOp parallelOp = dyn_cast<AffineParallelOp>(op)) {
255 loopOps.push_back(parallelOp);
256 numDims += parallelOp.getNumDims();
265 if (AffineForOp forOp = dyn_cast<AffineForOp>(op)) {
268 }
else if (
auto ifOp = dyn_cast<AffineIfOp>(op)) {
270 }
else if (
auto parallelOp = dyn_cast<AffineParallelOp>(op))
296 unsigned minNumLoops =
298 unsigned numCommonLoops = 0;
299 for (
unsigned i = 0; i < minNumLoops; ++i) {
306 if (commonLoops !=
nullptr)
310 if (commonLoops !=
nullptr)
311 assert(commonLoops->size() == numCommonLoops);
312 return numCommonLoops;
322 auto getChainOfAncestorBlocks =
333 "parent op starting an affine scope is always expected");
334 ancestorBlocks.push_back(currBlock);
339 getChainOfAncestorBlocks(opA, srcAncestorBlocks);
340 getChainOfAncestorBlocks(opB, dstAncestorBlocks);
342 Block *commonBlock =
nullptr;
343 for (
int i = srcAncestorBlocks.size() - 1,
j = dstAncestorBlocks.size() - 1;
344 i >= 0 &&
j >= 0 && srcAncestorBlocks[i] == dstAncestorBlocks[
j];
346 commonBlock = srcAncestorBlocks[i];
360 assert(commonBlock &&
361 "ops expected to have a common surrounding block in affine scope");
366 assert(srcOp &&
"src access op must lie in common block");
368 assert(dstOp &&
"dest access op must lie in common block");
385 unsigned numCols = dependenceDomain->
getNumCols();
389 unsigned numCommonLoopConstraints =
std::min(numCommonLoops, loopDepth);
390 for (
unsigned i = 0; i < numCommonLoopConstraints; ++i) {
393 eq[i + numSrcDims] = 1;
394 if (i == loopDepth - 1) {
395 eq[numCols - 1] = -1;
414 unsigned numCommonLoops =
416 if (numCommonLoops == 0)
419 unsigned numIdsToEliminate = dependenceDomain->
getNumVars();
422 dependenceDomain->
insertVar(VarKind::SetDim, 0,
432 for (
unsigned j = 0;
j < numCommonLoops; ++
j) {
435 eq[
j + numCommonLoops] = 1;
436 eq[
j + numCommonLoops + numSrcDims] = -1;
441 dependenceDomain->
projectOut(numCommonLoops, numIdsToEliminate);
445 dependenceComponents->resize(numCommonLoops);
446 for (
unsigned j = 0;
j < numCommonLoops; ++
j) {
447 (*dependenceComponents)[
j].op = commonLoops[
j].getOperation();
449 (*dependenceComponents)[
j].lb =
452 (*dependenceComponents)[
j].ub =
465 getAccessMap(&accessValueMap);
472 unsigned inserts = 0;
473 for (
unsigned i = 0, e = domain.
getNumDimVars(); i < e; ++i) {
477 const Identifier *itr = std::find(findBegin, findEnd, domainIdi);
478 if (itr != findEnd) {
479 rel.
swapVar(i, i + std::distance(findBegin, itr));
483 rel.
setId(VarKind::SetDim, i, domainIdi);
510 if (
auto loadOp = dyn_cast<AffineReadOpInterface>(opInst))
511 map = loadOp.getAffineMap();
513 map = cast<AffineWriteOpInterface>(opInst).getAffineMap();
519 accessMap->
reset(map, operands);
612 LLVM_DEBUG(llvm::dbgs() <<
"Checking for dependence at depth: "
613 << Twine(loopDepth) <<
" between:\n";);
623 if (!allowRAR && !isa<AffineWriteOpInterface>(srcAccess.
opInst) &&
624 !isa<AffineWriteOpInterface>(dstAccess.
opInst))
652 assert(loopDepth <= numCommonLoops + 1);
653 if (!allowRAR && loopDepth > numCommonLoops &&
666 if (!srcRel.getSpace().isUsingIds())
677 if (dependenceDomain.
isEmpty())
681 if (dependenceComponents !=
nullptr)
683 dependenceComponents);
685 LLVM_DEBUG(llvm::dbgs() <<
"Dependence polyhedron:\n");
686 LLVM_DEBUG(dependenceDomain.
dump());
689 if (dependenceConstraints)
690 *dependenceConstraints = result;
697 AffineForOp forOp,
unsigned maxLoopDepth,
702 if (isa<AffineReadOpInterface, AffineWriteOpInterface>(op))
703 loadAndStoreOps.push_back(op);
706 unsigned numOps = loadAndStoreOps.size();
707 for (
unsigned d = 1; d <= maxLoopDepth; ++d) {
708 for (
unsigned i = 0; i < numOps; ++i) {
709 auto *srcOp = loadAndStoreOps[i];
711 for (
unsigned j = 0;
j < numOps; ++
j) {
712 auto *dstOp = loadAndStoreOps[
j];
719 srcAccess, dstAccess, d,
nullptr,
722 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 bool isLocallyDefined(Value v, Operation *enclosingOp)
Returns true if v is allocated locally to enclosingOp – i.e., it is allocated by an operation nested ...
static Block * getCommonBlockInAffineScope(Operation *opA, Operation *opB)
Returns the closest surrounding block common to opA and opB.
static void addOrderingConstraints(const FlatAffineValueConstraints &srcDomain, const FlatAffineValueConstraints &dstDomain, unsigned loopDepth, IntegerRelation *dependenceDomain)
union mlir::linalg::@1227::ArityGroupAndKind::Kind kind
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
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 ...
void push_back(Operation *op)
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.
std::optional< int64_t > getConstantBound64(BoundType type, unsigned pos) const
The same, but casts to int64_t.
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
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...
const PresburgerSpace & getSpace() const
Returns a reference to the underlying space.
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.
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...
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.
bool isLoopMemoryParallel(AffineForOp forOp)
Returns true if ‘forOp’ doesn't have memory dependences preventing parallelization.
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.
bool isLoopParallel(AffineForOp forOp, SmallVectorImpl< LoopReduction > *parallelReductions=nullptr)
Returns true if ‘forOp’ is a parallel loop.
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 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...
Checks whether two accesses to the same memref access the same element.
enum mlir::affine::DependenceResult::ResultEnum value
A description of a (parallelizable) reduction in an affine loop.
Encapsulates a memref load or store access information.
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.