blob: d698754a8aab1380959a22437edcd3e795a5b03f [file] [log] [blame]
//===- AffineOps.h - MLIR Affine Operations -------------------------------===//
//
// Copyright 2019 The MLIR Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
//
// This file defines convenience types for working with Affine operations
// in the MLIR operation set.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_AFFINEOPS_AFFINEOPS_H
#define MLIR_AFFINEOPS_AFFINEOPS_H
#include "mlir/IR/AffineMap.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/StandardTypes.h"
namespace mlir {
class AffineBound;
class AffineValueMap;
class FlatAffineConstraints;
class OpBuilder;
/// A utility function to check if a value is defined at the top level of a
/// function. A value defined at the top level is always a valid symbol.
bool isTopLevelSymbol(Value *value);
class AffineOpsDialect : public Dialect {
public:
AffineOpsDialect(MLIRContext *context);
static StringRef getDialectNamespace() { return "affine"; }
};
/// The "affine.apply" operation applies an affine map to a list of operands,
/// yielding a single result. The operand list must be the same size as the
/// number of arguments to the affine mapping. All operands and the result are
/// of type 'Index'. This operation requires a single affine map attribute named
/// "map". For example:
///
/// %y = "affine.apply" (%x) { map: (d0) -> (d0 + 1) } :
/// (index) -> (index)
///
/// equivalently:
///
/// #map42 = (d0)->(d0+1)
/// %y = affine.apply #map42(%x)
///
class AffineApplyOp : public Op<AffineApplyOp, OpTrait::VariadicOperands,
OpTrait::OneResult, OpTrait::HasNoSideEffect> {
public:
using Op::Op;
/// Builds an affine apply op with the specified map and operands.
static void build(Builder *builder, OperationState *result, AffineMap map,
ArrayRef<Value *> operands);
/// Returns the affine map to be applied by this operation.
AffineMap getAffineMap() {
return getAttrOfType<AffineMapAttr>("map").getValue();
}
/// Returns true if the result of this operation can be used as dimension id.
bool isValidDim();
/// Returns true if the result of this operation is a symbol.
bool isValidSymbol();
static StringRef getOperationName() { return "affine.apply"; }
// Hooks to customize behavior of this op.
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
LogicalResult verify();
OpFoldResult fold(ArrayRef<Attribute> operands);
static void getCanonicalizationPatterns(OwningRewritePatternList &results,
MLIRContext *context);
};
/// AffineDmaStartOp starts a non-blocking DMA operation that transfers data
/// from a source memref to a destination memref. The source and destination
/// memref need not be of the same dimensionality, but need to have the same
/// elemental type. The operands include the source and destination memref's
/// each followed by its indices, size of the data transfer in terms of the
/// number of elements (of the elemental type of the memref), a tag memref with
/// its indices, and optionally at the end, a stride and a
/// number_of_elements_per_stride arguments. The tag location is used by an
/// AffineDmaWaitOp to check for completion. The indices of the source memref,
/// destination memref, and the tag memref have the same restrictions as any
/// affine.load/store. In particular, index for each memref dimension must be an
/// affine expression of loop induction variables and symbols.
/// The optional stride arguments should be of 'index' type, and specify a
/// stride for the slower memory space (memory space with a lower memory space
/// id), tranferring chunks of number_of_elements_per_stride every stride until
/// %num_elements are transferred. Either both or no stride arguments should be
/// specified. The value of 'num_elements' must be a multiple of
/// 'number_of_elements_per_stride'.
//
// For example, a DmaStartOp operation that transfers 256 elements of a memref
// '%src' in memory space 0 at indices [%i + 3, %j] to memref '%dst' in memory
// space 1 at indices [%k + 7, %l], would be specified as follows:
//
// %num_elements = constant 256
// %idx = constant 0 : index
// %tag = alloc() : memref<1xi32, 4>
// affine.dma_start %src[%i + 3, %j], %dst[%k + 7, %l], %tag[%idx],
// %num_elements :
// memref<40x128xf32, 0>, memref<2x1024xf32, 1>, memref<1xi32, 2>
//
// If %stride and %num_elt_per_stride are specified, the DMA is expected to
// transfer %num_elt_per_stride elements every %stride elements apart from
// memory space 0 until %num_elements are transferred.
//
// affine.dma_start %src[%i, %j], %dst[%k, %l], %tag[%idx], %num_elements,
// %stride, %num_elt_per_stride : ...
//
// TODO(mlir-team): add additional operands to allow source and destination
// striding, and multiple stride levels (possibly using AffineMaps to specify
// multiple levels of striding).
// TODO(andydavis) Consider replacing src/dst memref indices with view memrefs.
class AffineDmaStartOp : public Op<AffineDmaStartOp, OpTrait::VariadicOperands,
OpTrait::ZeroResult> {
public:
using Op::Op;
static void build(Builder *builder, OperationState *result, Value *srcMemRef,
AffineMap srcMap, ArrayRef<Value *> srcIndices,
Value *destMemRef, AffineMap dstMap,
ArrayRef<Value *> destIndices, Value *tagMemRef,
AffineMap tagMap, ArrayRef<Value *> tagIndices,
Value *numElements, Value *stride = nullptr,
Value *elementsPerStride = nullptr);
/// Returns the operand index of the src memref.
unsigned getSrcMemRefOperandIndex() { return 0; }
/// Returns the source MemRefType for this DMA operation.
Value *getSrcMemRef() { return getOperand(getSrcMemRefOperandIndex()); }
MemRefType getSrcMemRefType() {
return getSrcMemRef()->getType().cast<MemRefType>();
}
/// Returns the rank (number of indices) of the source MemRefType.
unsigned getSrcMemRefRank() { return getSrcMemRefType().getRank(); }
/// Returns the affine map used to access the src memref.
AffineMap getSrcMap() { return getSrcMapAttr().getValue(); }
AffineMapAttr getSrcMapAttr() {
return getAttr(getSrcMapAttrName()).cast<AffineMapAttr>();
}
/// Returns the source memref affine map indices for this DMA operation.
operand_range getSrcIndices() {
return {operand_begin() + getSrcMemRefOperandIndex() + 1,
operand_begin() + getSrcMemRefOperandIndex() + 1 +
getSrcMap().getNumInputs()};
}
/// Returns the memory space of the src memref.
unsigned getSrcMemorySpace() {
return getSrcMemRef()->getType().cast<MemRefType>().getMemorySpace();
}
/// Returns the operand index of the dst memref.
unsigned getDstMemRefOperandIndex() {
return getSrcMemRefOperandIndex() + 1 + getSrcMap().getNumInputs();
}
/// Returns the destination MemRefType for this DMA operations.
Value *getDstMemRef() { return getOperand(getDstMemRefOperandIndex()); }
MemRefType getDstMemRefType() {
return getDstMemRef()->getType().cast<MemRefType>();
}
/// Returns the rank (number of indices) of the destination MemRefType.
unsigned getDstMemRefRank() {
return getDstMemRef()->getType().cast<MemRefType>().getRank();
}
/// Returns the memory space of the src memref.
unsigned getDstMemorySpace() {
return getDstMemRef()->getType().cast<MemRefType>().getMemorySpace();
}
/// Returns the affine map used to access the dst memref.
AffineMap getDstMap() { return getDstMapAttr().getValue(); }
AffineMapAttr getDstMapAttr() {
return getAttr(getDstMapAttrName()).cast<AffineMapAttr>();
}
/// Returns the destination memref indices for this DMA operation.
operand_range getDstIndices() {
return {operand_begin() + getDstMemRefOperandIndex() + 1,
operand_begin() + getDstMemRefOperandIndex() + 1 +
getDstMap().getNumInputs()};
}
/// Returns the operand index of the tag memref.
unsigned getTagMemRefOperandIndex() {
return getDstMemRefOperandIndex() + 1 + getDstMap().getNumInputs();
}
/// Returns the Tag MemRef for this DMA operation.
Value *getTagMemRef() { return getOperand(getTagMemRefOperandIndex()); }
MemRefType getTagMemRefType() {
return getTagMemRef()->getType().cast<MemRefType>();
}
/// Returns the rank (number of indices) of the tag MemRefType.
unsigned getTagMemRefRank() {
return getTagMemRef()->getType().cast<MemRefType>().getRank();
}
/// Returns the affine map used to access the tag memref.
AffineMap getTagMap() { return getTagMapAttr().getValue(); }
AffineMapAttr getTagMapAttr() {
return getAttr(getTagMapAttrName()).cast<AffineMapAttr>();
}
/// Returns the tag memref indices for this DMA operation.
operand_range getTagIndices() {
return {operand_begin() + getTagMemRefOperandIndex() + 1,
operand_begin() + getTagMemRefOperandIndex() + 1 +
getTagMap().getNumInputs()};
}
/// Returns the number of elements being transferred by this DMA operation.
Value *getNumElements() {
return getOperand(getTagMemRefOperandIndex() + 1 +
getTagMap().getNumInputs());
}
/// Returns the AffineMapAttr associated with 'memref'.
NamedAttribute getAffineMapAttrForMemRef(Value *memref) {
if (memref == getSrcMemRef())
return {Identifier::get(getSrcMapAttrName(), getContext()),
getSrcMapAttr()};
else if (memref == getDstMemRef())
return {Identifier::get(getDstMapAttrName(), getContext()),
getDstMapAttr()};
assert(memref == getTagMemRef() &&
"DmaStartOp expected source, destination or tag memref");
return {Identifier::get(getTagMapAttrName(), getContext()),
getTagMapAttr()};
}
/// Returns true if this is a DMA from a faster memory space to a slower one.
bool isDestMemorySpaceFaster() {
return (getSrcMemorySpace() < getDstMemorySpace());
}
/// Returns true if this is a DMA from a slower memory space to a faster one.
bool isSrcMemorySpaceFaster() {
// Assumes that a lower number is for a slower memory space.
return (getDstMemorySpace() < getSrcMemorySpace());
}
/// Given a DMA start operation, returns the operand position of either the
/// source or destination memref depending on the one that is at the higher
/// level of the memory hierarchy. Asserts failure if neither is true.
unsigned getFasterMemPos() {
assert(isSrcMemorySpaceFaster() || isDestMemorySpaceFaster());
return isSrcMemorySpaceFaster() ? 0 : getDstMemRefOperandIndex();
}
static StringRef getSrcMapAttrName() { return "src_map"; }
static StringRef getDstMapAttrName() { return "dst_map"; }
static StringRef getTagMapAttrName() { return "tag_map"; }
static StringRef getOperationName() { return "affine.dma_start"; }
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
LogicalResult verify();
static void getCanonicalizationPatterns(OwningRewritePatternList &results,
MLIRContext *context);
/// Returns true if this DMA operation is strided, returns false otherwise.
bool isStrided() {
return getNumOperands() !=
getTagMemRefOperandIndex() + 1 + getTagMap().getNumInputs() + 1;
}
/// Returns the stride value for this DMA operation.
Value *getStride() {
if (!isStrided())
return nullptr;
return getOperand(getNumOperands() - 1 - 1);
}
/// Returns the number of elements to transfer per stride for this DMA op.
Value *getNumElementsPerStride() {
if (!isStrided())
return nullptr;
return getOperand(getNumOperands() - 1);
}
};
/// AffineDmaWaitOp blocks until the completion of a DMA operation associated
/// with the tag element '%tag[%index]'. %tag is a memref, and %index has to be
/// an index with the same restrictions as any load/store index. In particular,
/// index for each memref dimension must be an affine expression of loop
/// induction variables and symbols. %num_elements is the number of elements
/// associated with the DMA operation. For example:
//
// affine.dma_start %src[%i, %j], %dst[%k, %l], %tag[%index], %num_elements :
// memref<2048xf32, 0>, memref<256xf32, 1>, memref<1xi32, 2>
// ...
// ...
// affine.dma_wait %tag[%index], %num_elements : memref<1xi32, 2>
//
class AffineDmaWaitOp : public Op<AffineDmaWaitOp, OpTrait::VariadicOperands,
OpTrait::ZeroResult> {
public:
using Op::Op;
static void build(Builder *builder, OperationState *result, Value *tagMemRef,
AffineMap tagMap, ArrayRef<Value *> tagIndices,
Value *numElements);
static StringRef getOperationName() { return "affine.dma_wait"; }
// Returns the Tag MemRef associated with the DMA operation being waited on.
Value *getTagMemRef() { return getOperand(0); }
MemRefType getTagMemRefType() {
return getTagMemRef()->getType().cast<MemRefType>();
}
/// Returns the affine map used to access the tag memref.
AffineMap getTagMap() { return getTagMapAttr().getValue(); }
AffineMapAttr getTagMapAttr() {
return getAttr(getTagMapAttrName()).cast<AffineMapAttr>();
}
// Returns the tag memref index for this DMA operation.
operand_range getTagIndices() {
return {operand_begin() + 1,
operand_begin() + 1 + getTagMap().getNumInputs()};
}
// Returns the rank (number of indices) of the tag memref.
unsigned getTagMemRefRank() {
return getTagMemRef()->getType().cast<MemRefType>().getRank();
}
/// Returns the AffineMapAttr associated with 'memref'.
NamedAttribute getAffineMapAttrForMemRef(Value *memref) {
assert(memref == getTagMemRef());
return {Identifier::get(getTagMapAttrName(), getContext()),
getTagMapAttr()};
}
/// Returns the number of elements transferred in the associated DMA op.
Value *getNumElements() { return getOperand(1 + getTagMap().getNumInputs()); }
static StringRef getTagMapAttrName() { return "tag_map"; }
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
LogicalResult verify();
static void getCanonicalizationPatterns(OwningRewritePatternList &results,
MLIRContext *context);
};
/// The "affine.for" operation represents an affine loop nest, defining an SSA
/// value for its induction variable. It has one region capturing the loop body.
/// The induction variable is represented as a argument of this region. This SSA
/// value always has type index, which is the size of the machine word. The
/// stride, represented by step, is a positive constant integer which defaults
/// to "1" if not present. The lower and upper bounds specify a half-open range:
/// the range includes the lower bound but does not include the upper bound.
///
/// The body region must contain exactly one block that terminates with
/// "affine.terminator". Calling AffineForOp::build will create such region
/// and insert the terminator, so will the parsing even in cases if it is absent
/// from the custom format.
///
/// The lower and upper bounds of a for operation are represented as an
/// application of an affine mapping to a list of SSA values passed to the map.
/// The same restrictions hold for these SSA values as for all bindings of SSA
/// values to dimensions and symbols. The affine mappings for the bounds may
/// return multiple results, in which case the max/min keywords are required
/// (for the lower/upper bound respectively), and the bound is the
/// maximum/minimum of the returned values.
///
/// Example:
///
/// affine.for %i = 1 to 10 {
/// ...
/// }
///
class AffineForOp
: public Op<AffineForOp, OpTrait::VariadicOperands, OpTrait::ZeroResult> {
public:
using Op::Op;
// Hooks to customize behavior of this op.
static void build(Builder *builder, OperationState *result,
ArrayRef<Value *> lbOperands, AffineMap lbMap,
ArrayRef<Value *> ubOperands, AffineMap ubMap,
int64_t step = 1);
static void build(Builder *builder, OperationState *result, int64_t lb,
int64_t ub, int64_t step = 1);
LogicalResult verify();
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
static void getCanonicalizationPatterns(OwningRewritePatternList &results,
MLIRContext *context);
static StringRef getOperationName() { return "affine.for"; }
static StringRef getStepAttrName() { return "step"; }
static StringRef getLowerBoundAttrName() { return "lower_bound"; }
static StringRef getUpperBoundAttrName() { return "upper_bound"; }
/// Return a Builder set up to insert operations immediately before the
/// terminator.
OpBuilder getBodyBuilder();
/// Get the body of the AffineForOp.
Block *getBody() { return &getRegion().front(); }
/// Get the body region of the AffineForOp.
Region &getRegion() { return getOperation()->getRegion(0); }
/// Returns the induction variable for this loop.
Value *getInductionVar();
//===--------------------------------------------------------------------===//
// Bounds and step
//===--------------------------------------------------------------------===//
// TODO: provide iterators for the lower and upper bound operands
// if the current access via getLowerBound(), getUpperBound() is too slow.
/// Returns operands for the lower bound map.
operand_range getLowerBoundOperands();
/// Returns operands for the upper bound map.
operand_range getUpperBoundOperands();
/// Returns information about the lower bound as a single object.
AffineBound getLowerBound();
/// Returns information about the upper bound as a single object.
AffineBound getUpperBound();
/// Returns loop step.
int64_t getStep() {
return getAttr(getStepAttrName()).cast<IntegerAttr>().getInt();
}
/// Returns affine map for the lower bound.
AffineMap getLowerBoundMap() { return getLowerBoundMapAttr().getValue(); }
AffineMapAttr getLowerBoundMapAttr() {
return getAttr(getLowerBoundAttrName()).cast<AffineMapAttr>();
}
/// Returns affine map for the upper bound. The upper bound is exclusive.
AffineMap getUpperBoundMap() { return getUpperBoundMapAttr().getValue(); }
AffineMapAttr getUpperBoundMapAttr() {
return getAttr(getUpperBoundAttrName()).cast<AffineMapAttr>();
}
/// Set lower bound. The new bound must have the same number of operands as
/// the current bound map. Otherwise, 'replaceForLowerBound' should be used.
void setLowerBound(ArrayRef<Value *> operands, AffineMap map);
/// Set upper bound. The new bound must not have more operands than the
/// current bound map. Otherwise, 'replaceForUpperBound' should be used.
void setUpperBound(ArrayRef<Value *> operands, AffineMap map);
/// Set the lower bound map without changing operands.
void setLowerBoundMap(AffineMap map);
/// Set the upper bound map without changing operands.
void setUpperBoundMap(AffineMap map);
/// Set loop step.
void setStep(int64_t step) {
assert(step > 0 && "step has to be a positive integer constant");
auto *context = getLowerBoundMap().getContext();
setAttr(Identifier::get(getStepAttrName(), context),
IntegerAttr::get(IndexType::get(context), step));
}
/// Returns true if the lower bound is constant.
bool hasConstantLowerBound();
/// Returns true if the upper bound is constant.
bool hasConstantUpperBound();
/// Returns true if both bounds are constant.
bool hasConstantBounds() {
return hasConstantLowerBound() && hasConstantUpperBound();
}
/// Returns the value of the constant lower bound.
/// Fails assertion if the bound is non-constant.
int64_t getConstantLowerBound();
/// Returns the value of the constant upper bound. The upper bound is
/// exclusive. Fails assertion if the bound is non-constant.
int64_t getConstantUpperBound();
/// Sets the lower bound to the given constant value.
void setConstantLowerBound(int64_t value);
/// Sets the upper bound to the given constant value.
void setConstantUpperBound(int64_t value);
/// Returns true if both the lower and upper bound have the same operand lists
/// (same operands in the same order).
bool matchingBoundOperandList();
};
/// Returns if the provided value is the induction variable of a AffineForOp.
bool isForInductionVar(Value *val);
/// Returns the loop parent of an induction variable. If the provided value is
/// not an induction variable, then return nullptr.
AffineForOp getForInductionVarOwner(Value *val);
/// Extracts the induction variables from a list of AffineForOps and places them
/// in the output argument `ivs`.
void extractForInductionVars(ArrayRef<AffineForOp> forInsts,
SmallVectorImpl<Value *> *ivs);
/// AffineBound represents a lower or upper bound in the for operation.
/// This class does not own the underlying operands. Instead, it refers
/// to the operands stored in the AffineForOp. Its life span should not exceed
/// that of the for operation it refers to.
class AffineBound {
public:
AffineForOp getAffineForOp() { return op; }
AffineMap getMap() { return map; }
/// Returns an AffineValueMap representing this bound.
AffineValueMap getAsAffineValueMap();
unsigned getNumOperands() { return opEnd - opStart; }
Value *getOperand(unsigned idx) {
return op.getOperation()->getOperand(opStart + idx);
}
using operand_iterator = AffineForOp::operand_iterator;
using operand_range = AffineForOp::operand_range;
operand_iterator operand_begin() { return op.operand_begin() + opStart; }
operand_iterator operand_end() { return op.operand_begin() + opEnd; }
operand_range getOperands() { return {operand_begin(), operand_end()}; }
private:
// 'affine.for' operation that contains this bound.
AffineForOp op;
// Start and end positions of this affine bound operands in the list of
// the containing 'affine.for' operation operands.
unsigned opStart, opEnd;
// Affine map for this bound.
AffineMap map;
AffineBound(AffineForOp op, unsigned opStart, unsigned opEnd, AffineMap map)
: op(op), opStart(opStart), opEnd(opEnd), map(map) {}
friend class AffineForOp;
};
/// The "if" operation represents an if-then-else construct for conditionally
/// executing two regions of code. The operands to an if operation are an
/// IntegerSet condition and a set of symbol/dimension operands to the
/// condition set. The operation produces no results. For example:
///
/// affine.if #set(%i) {
/// ...
/// } else {
/// ...
/// }
///
/// The 'else' blocks to the if operation are optional, and may be omitted. For
/// example:
///
/// affine.if #set(%i) {
/// ...
/// }
///
class AffineIfOp
: public Op<AffineIfOp, OpTrait::VariadicOperands, OpTrait::ZeroResult> {
public:
using Op::Op;
// Hooks to customize behavior of this op.
static StringRef getOperationName() { return "affine.if"; }
static StringRef getConditionAttrName() { return "condition"; }
IntegerSet getIntegerSet();
void setIntegerSet(IntegerSet newSet);
/// Returns the 'then' region.
Region &getThenBlocks();
/// Returns the 'else' blocks.
Region &getElseBlocks();
LogicalResult verify();
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
};
/// The "affine.load" op reads an element from a memref, where the index
/// for each memref dimension is an affine expression of loop induction
/// variables and symbols. The output of 'affine.load' is a new value with the
/// same type as the elements of the memref. An affine expression of loop IVs
/// and symbols must be specified for each dimension of the memref. The keyword
/// 'symbol' can be used to indicate SSA identifiers which are symbolic.
//
// Example 1:
//
// %1 = affine.load %0[%i0 + 3, %i1 + 7] : memref<100x100xf32>
//
// Example 2: Uses 'symbol' keyword for symbols '%n' and '%m'.
//
// %1 = affine.load %0[%i0 + symbol(%n), %i1 + symbol(%m)]
// : memref<100x100xf32>
//
class AffineLoadOp : public Op<AffineLoadOp, OpTrait::OneResult,
OpTrait::AtLeastNOperands<1>::Impl> {
public:
using Op::Op;
/// Builds an affine load op with the specified map and operands.
static void build(Builder *builder, OperationState *result, AffineMap map,
ArrayRef<Value *> operands);
/// Builds an affine load op an identify map and operands.
static void build(Builder *builder, OperationState *result, Value *memref,
ArrayRef<Value *> indices = {});
/// Returns the operand index of the memref.
unsigned getMemRefOperandIndex() { return 0; }
/// Get memref operand.
Value *getMemRef() { return getOperand(getMemRefOperandIndex()); }
void setMemRef(Value *value) { setOperand(getMemRefOperandIndex(), value); }
MemRefType getMemRefType() {
return getMemRef()->getType().cast<MemRefType>();
}
/// Get affine map operands.
operand_range getIndices() { return llvm::drop_begin(getOperands(), 1); }
/// Returns the affine map used to index the memref for this operation.
AffineMap getAffineMap() { return getAffineMapAttr().getValue(); }
AffineMapAttr getAffineMapAttr() {
return getAttr(getMapAttrName()).cast<AffineMapAttr>();
}
/// Returns the AffineMapAttr associated with 'memref'.
NamedAttribute getAffineMapAttrForMemRef(Value *memref) {
assert(memref == getMemRef());
return {Identifier::get(getMapAttrName(), getContext()),
getAffineMapAttr()};
}
static StringRef getMapAttrName() { return "map"; }
static StringRef getOperationName() { return "affine.load"; }
// Hooks to customize behavior of this op.
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
LogicalResult verify();
static void getCanonicalizationPatterns(OwningRewritePatternList &results,
MLIRContext *context);
};
/// The "affine.store" op writes an element to a memref, where the index
/// for each memref dimension is an affine expression of loop induction
/// variables and symbols. The 'affine.store' op stores a new value which is the
/// same type as the elements of the memref. An affine expression of loop IVs
/// and symbols must be specified for each dimension of the memref. The keyword
/// 'symbol' can be used to indicate SSA identifiers which are symbolic.
//
// Example 1:
//
// affine.store %v0, %0[%i0 + 3, %i1 + 7] : memref<100x100xf32>
//
// Example 2: Uses 'symbol' keyword for symbols '%n' and '%m'.
//
// affine.store %v0, %0[%i0 + symbol(%n), %i1 + symbol(%m)]
// : memref<100x100xf32>
//
class AffineStoreOp : public Op<AffineStoreOp, OpTrait::ZeroResult,
OpTrait::AtLeastNOperands<1>::Impl> {
public:
using Op::Op;
/// Builds an affine store operation with the specified map and operands.
static void build(Builder *builder, OperationState *result,
Value *valueToStore, AffineMap map,
ArrayRef<Value *> operands);
/// Builds an affine store operation with an identity map and operands.
static void build(Builder *builder, OperationState *result,
Value *valueToStore, Value *memref,
ArrayRef<Value *> operands);
/// Get value to be stored by store operation.
Value *getValueToStore() { return getOperand(0); }
/// Returns the operand index of the memref.
unsigned getMemRefOperandIndex() { return 1; }
/// Get memref operand.
Value *getMemRef() { return getOperand(getMemRefOperandIndex()); }
void setMemRef(Value *value) { setOperand(getMemRefOperandIndex(), value); }
MemRefType getMemRefType() {
return getMemRef()->getType().cast<MemRefType>();
}
/// Get affine map operands.
operand_range getIndices() { return llvm::drop_begin(getOperands(), 2); }
/// Returns the affine map used to index the memref for this operation.
AffineMap getAffineMap() { return getAffineMapAttr().getValue(); }
AffineMapAttr getAffineMapAttr() {
return getAttr(getMapAttrName()).cast<AffineMapAttr>();
}
/// Returns the AffineMapAttr associated with 'memref'.
NamedAttribute getAffineMapAttrForMemRef(Value *memref) {
assert(memref == getMemRef());
return {Identifier::get(getMapAttrName(), getContext()),
getAffineMapAttr()};
}
static StringRef getMapAttrName() { return "map"; }
static StringRef getOperationName() { return "affine.store"; }
// Hooks to customize behavior of this op.
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
LogicalResult verify();
static void getCanonicalizationPatterns(OwningRewritePatternList &results,
MLIRContext *context);
};
/// Returns true if the given Value can be used as a dimension id.
bool isValidDim(Value *value);
/// Returns true if the given Value can be used as a symbol.
bool isValidSymbol(Value *value);
/// Modifies both `map` and `operands` in-place so as to:
/// 1. drop duplicate operands
/// 2. drop unused dims and symbols from map
void canonicalizeMapAndOperands(AffineMap *map,
llvm::SmallVectorImpl<Value *> *operands);
/// Returns a composed AffineApplyOp by composing `map` and `operands` with
/// other AffineApplyOps supplying those operands. The operands of the resulting
/// AffineApplyOp do not change the length of AffineApplyOp chains.
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map,
llvm::ArrayRef<Value *> operands);
/// Given an affine map `map` and its input `operands`, this method composes
/// into `map`, maps of AffineApplyOps whose results are the values in
/// `operands`, iteratively until no more of `operands` are the result of an
/// AffineApplyOp. When this function returns, `map` becomes the composed affine
/// map, and each Value in `operands` is guaranteed to be either a loop IV or a
/// terminal symbol, i.e., a symbol defined at the top level or a block/function
/// argument.
void fullyComposeAffineMapAndOperands(AffineMap *map,
llvm::SmallVectorImpl<Value *> *operands);
#define GET_OP_CLASSES
#include "mlir/AffineOps/AffineOps.h.inc"
} // end namespace mlir
#endif