26#include "llvm/ADT/STLExtras.h"
27#include "llvm/Support/Casting.h"
29#define DEBUG_TYPE "pad-tiling-interface"
35#define DBGS() (llvm::dbgs() << "[" DEBUG_TYPE << "]: ")
36#define DBGSNL() (llvm::dbgs() << "\n")
44 for (
size_t idx = 0, e = indexingSizes.size(); idx != e; ++idx) {
46 paddingSizes.push_back(
options.paddingSizes.size() > idx
54 options.padToMultipleOf ?
b.getIndexAttr(1) : indexingSizes[idx];
56 LLVM_DEBUG(
DBGS() <<
"----idx: " << idx <<
" : " << paddingSizes[idx]
67 if (
auto binOp = dyn_cast<AffineBinaryOpExpr>(expr)) {
69 auto lhsD = dyn_cast<AffineDimExpr>(binOp.getLHS());
70 auto rhsC = dyn_cast<AffineConstantExpr>(binOp.getRHS());
72 return rhsC.getValue();
74 auto lhsC = dyn_cast<AffineConstantExpr>(binOp.getLHS());
75 auto rhsD = dyn_cast<AffineDimExpr>(binOp.getRHS());
77 return lhsC.getValue();
108 auto tensorType = cast<RankedTensorType>(v.getType());
109 paddedShape.resize_for_overwrite(tensorType.getRank());
110 assert(tensorType.getRank() == indexingMap.
getNumResults() &&
111 "expect the number of results of the affine map to match the tensor "
120 for (
const auto &enResults : enumerate(indexingMap.
getResults())) {
121 int64_t resultIndex = enResults.index();
125 LLVM_DEBUG(
DBGS() <<
"----resultIndex: " << resultIndex
126 <<
" with partialIndexingMap: " << partialIndexingMap
132 for (
size_t paddingDim = 0, e = paddingSizes.size(); paddingDim != e;
135 LLVM_DEBUG(
DBGS() <<
"------try apply padding of dim: " << paddingDim
136 <<
" to: " << paddingSize <<
"\n");
137 if (!enResults.value().isFunctionOfDim(paddingDim))
140 LLVM_DEBUG(
DBGS() <<
"------apply padding of dim: " << paddingDim
141 <<
" to: " << paddingSize <<
"\n");
144 llvm::SmallBitVector projectedDims(partialIndexingMap.
getNumDims(),
true);
145 projectedDims.flip(paddingDim);
159 builder, loc, composedMap, {indexingSizes[paddingDim], paddingSize},
164 builder, loc, projectedMap, paddingSize);
174 builder, loc, subtractMap, {paddingDimOfr});
175 terms.push_back(maxAccessIdx);
177 LLVM_DEBUG(
DBGS() <<
"------new term: " << terms.back() <<
"\n");
182 paddedShape[resultIndex] =
191 for (
unsigned i = 1; i < dims.size(); ++i)
192 sumExpr = sumExpr + dims[i];
196 paddedShape[resultIndex] = paddedDimOfr;
202FailureOr<SmallVector<OpFoldResult>>
207 llvm::dyn_cast<IndexingMapOpInterface>(operandToPad.
getOwner());
212 assert(llvm::all_of(iterationDomain, [&builder](
Range r) {
215 }) &&
"expected 0-offset 1-stride loop ranges");
218 loopUpperBounds.reserve(iterationDomain.size());
219 for (
const Range &range : iterationDomain)
220 loopUpperBounds.push_back(range.size);
222 AffineMap indexingMap = transferOp.getMatchingIndexingMap(&operandToPad);
225 indexingMap, loopUpperBounds,
options);
237 if (
auto complexAttr = dyn_cast<ArrayAttr>(paddingValueAttr)) {
238 paddingValue = complex::ConstantOp::create(builder, opToPad.getLoc(),
239 complexTy, complexAttr);
242 paddingValue = ub::PoisonOp::create(builder, opToPad.getLoc(),
244 }
else if (
auto typedAttr = dyn_cast<TypedAttr>(paddingValueAttr)) {
246 arith::ConstantOp::create(builder, opToPad.getLoc(), typedAttr);
248 assert(paddingValue &&
"failed to create value from padding attribute");
255 tensorShape.push_back(cst.has_value() ? *cst : ShapedType::kDynamic);
256 if (!cst.has_value())
257 dynDims.push_back(ofr.dyn_cast<
Value>());
261 auto paddedTensorType =
263 LLVM_DEBUG(
DBGS() <<
"--SUCCESS, makeComposedPadHighOp with type: "
264 << paddedTensorType);
266 paddingValue,
false, dynDims);
273 assert(operand->
getOwner() == linalgOp.getOperation() &&
274 "expected operand to belong to the LinalgOp");
276 AffineMap map = linalgOp.getMatchingIndexingMap(operand);
277 return llvm::any_of(llvm::enumerate(iterTypes), [&](
auto it) {
278 return it.value() == utils::IteratorType::reduction &&
286static FailureOr<SmallVector<Attribute>>
312 if ((isa<arith::MulFOp>(elemwise) && isa<arith::AddFOp>(
reduce)) ||
313 (isa<arith::MulIOp>(elemwise) && isa<arith::AddIOp>(
reduce)) ||
314 (isa<complex::MulOp>(elemwise) && isa<complex::AddOp>(
reduce)))
317 if (isa<arith::AndIOp>(elemwise) && isa<arith::OrIOp>(
reduce) &&
324 defaultPadVals.end());
325 for (
OpOperand *input : linalgOp.getDpsInputOperands()) {
332 paddingValues[input->getOperandNumber()] = padValue;
334 return paddingValues;
347static FailureOr<SmallVector<Attribute>>
358 if (!llvm::is_contained(iterTypes, utils::IteratorType::reduction))
359 return paddingValues;
363 auto linalgOp = dyn_cast<linalg::LinalgOp>(op);
369 FailureOr<SmallVector<Attribute>> contractionValues =
372 if (succeeded(contractionValues))
373 return *contractionValues;
376 if (linalgOp.getNumDpsInits() != 1)
382 if (!
matchReduction(linalgOp.getRegionOutputArgs(), 0, combiners))
384 if (combiners.size() != 1)
395 if (
auto floatNeutral = dyn_cast<FloatAttr>(*neutral);
396 floatNeutral && floatNeutral.getValue().isNaN()) {
397 auto fastMath = dyn_cast<arith::ArithFastMathInterface>(combiner);
399 arith::bitEnumContainsAny(fastMath.getFastMathFlagsAttr().getValue(),
400 arith::FastMathFlags::nnan))
404 for (
OpOperand *input : linalgOp.getDpsInputOperands()) {
409 if (!llvm::is_contained(linalgOp.getMatchingBlockArgument(input).getUsers(),
412 paddingValues[input->getOperandNumber()] = *neutral;
414 return paddingValues;
418 OpBuilder &builder, TilingInterface toPad,
421 LLVM_DEBUG(
DBGS() <<
"Start rewriteAsPaddedOp : " << toPad <<
"\n");
426 if (
options.paddingValues.empty()) {
427 FailureOr<SmallVector<Attribute>> inferred =
429 if (failed(inferred)) {
430 LLVM_DEBUG(
DBGS() <<
"Could not infer pad values: FAIL\n");
433 options.paddingValues = std::move(*inferred);
436 if (llvm::any_of(toPad->getOperands(),
437 [](
Value v) { return isa<MemRefType>(v.getType()); })) {
438 LLVM_DEBUG(
DBGS() <<
"Not an operation on tensors: FAIL\n");
447 newOperands.reserve(toPad->getNumOperands());
448 for (
OpOperand &opOperand : toPad->getOpOperands()) {
449 Value operand = opOperand.get();
450 LLVM_DEBUG(
DBGS() <<
"--start padding operand: " << operand <<
"\n");
454 if (!isa<RankedTensorType>(operandType)) {
455 assert((!isa<ShapedType>(operandType) || isa<VectorType>(operandType)) &&
456 "Unexpected non-vector ShapedType");
457 newOperands.push_back(operand);
462 FailureOr<SmallVector<OpFoldResult>> maybePaddedShape =
463 computePaddingSizeFun(builder, opOperand, iterationDomain,
options);
464 if (failed(maybePaddedShape)) {
465 LLVM_DEBUG(
DBGS() <<
"Could not get padded shape of operand: FAIL\n");
472 if (opOperand.getOperandNumber() >=
options.paddingValues.size()) {
473 LLVM_DEBUG(
DBGS() <<
"Too few padding values specified: FAIL\n");
477 options.paddingValues[opOperand.getOperandNumber()];
480 Value paddedOperand =
482 *maybePaddedShape, paddingValueAttr);
483 LLVM_DEBUG(
DBGS() <<
"--done padding operand: " << paddedOperand <<
"\n");
485 newOperands.push_back(paddedOperand);
486 if (
auto padOp = paddedOperand.
getDefiningOp<tensor::PadOp>())
487 padOps.push_back(padOp);
493 LLVM_DEBUG(
DBGS() <<
"Failed to reify result shapes: FAIL\n");
496 assert(reifiedResultShapes.size() == toPad->getNumResults() &&
497 "expected same number of results");
500 auto resultTensorTypes =
501 ValueRange(newOperands).take_back(toPad->getNumResults()).getTypes();
503 TilingInterface paddedOp =
504 clone(builder, toPad, resultTensorTypes, newOperands);
505 LLVM_DEBUG(
DBGS() <<
"--cloned padded op: " << paddedOp <<
"\n");
509 paddedSubtensorResults.reserve(toPad->getNumResults());
510 for (
const auto &en : llvm::enumerate(paddedOp->getResults())) {
511 Value paddedResult = en.value();
512 int64_t resultNumber = en.index();
513 int64_t rank = cast<RankedTensorType>(paddedResult.
getType()).getRank();
516 paddedSubtensorResults.push_back(tensor::ExtractSliceOp::create(
517 builder, loc, paddedResult, offsets, reifiedResultShapes[resultNumber],
static Value reduce(OpBuilder &builder, Location loc, Value input, Value output, int64_t dim)
static bool isReducedOperand(linalg::LinalgOp linalgOp, OpOperand *operand, ArrayRef< utils::IteratorType > iterTypes)
Returns true if operand is indexed along at least one reduction dimension of linalgOp
static FailureOr< SmallVector< Attribute > > inferContractionPaddingValues(OpBuilder &builder, linalg::LinalgOp linalgOp, ArrayRef< utils::IteratorType > iterTypes, ArrayRef< Attribute > defaultPadVals)
On success, returns a vector with pad values for every operand of linalgOp.
static SmallVector< OpFoldResult > getFullRankPaddingSizes(Builder &b, ArrayRef< OpFoldResult > indexingSizes, const PadTilingInterfaceOptions &options)
Form a "full-rank" padding specification so that the application is easy.
static FailureOr< SmallVector< Attribute > > inferPaddingValues(OpBuilder &builder, TilingInterface toPad)
Infers a semantics-preserving padding value for every operand of toPad (indexed by operand number).
static int64_t extractConstantMultiplier(AffineExpr expr)
Extracts the constant multiplier from an affine expression of the form d * c or c * d,...
static Value padOperand(OpBuilder &builder, TilingInterface opToPad, TypedValue< RankedTensorType > v, ArrayRef< OpFoldResult > paddedShape, Attribute paddingValueAttr)
Pad a single operand to paddedShape using paddingValueAttr as padding Value.
static llvm::ManagedStatic< PassManagerOptions > options
Base type for affine expression.
AffineExpr ceilDiv(uint64_t v) const
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
bool isFunctionOfDim(unsigned position) const
Return true if any affine expression involves AffineDimExpr position.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
AffineExpr getResult(unsigned idx) const
AffineMap getSubMap(ArrayRef< unsigned > resultPos) const
Returns the map consisting of the resultPos subset.
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
Attributes are known-constant values of operations.
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
TypedAttr getZeroAttr(Type type)
MLIRContext * getContext() const
IRValueT get() const
Return the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
This class represents a single result from folding an operation.
This class represents an operand of an operation.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
operand_type_range getOperandTypes()
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isInteger() const
Return true if this is an integer type (with the specified width).
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Operation * getOwner() const
Return the owner of this operand.
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
std::optional< TypedAttr > getNeutralElement(Operation *op)
Return the identity numeric value associated to the give op.
bool isContractionBody(Block &block, function_ref< bool(Operation *, Operation *)> isaPair, llvm::raw_ostream &errs=mlir::thread_safe_nulls())
Returns true if the block contains a contraction of the following form:
LogicalResult rewriteAsPaddedOp(RewriterBase &rewriter, LinalgOp opToPad, const LinalgPaddingOptions &options, LinalgOp &paddedOp, SmallVector< Value > &replacements, SmallVector< tensor::PadOp > &padOps)
Pad the iterator dimensions options.paddingDimensions of all opToPad operands to a static bounding bo...
std::function< FailureOr< SmallVector< OpFoldResult > >( OpBuilder &, OpOperand &, ArrayRef< Range >, const PadTilingInterfaceOptions &)> PadSizeComputationFunction
SmallVector< OpFoldResult > computePaddedShape(OpBuilder &, TypedValue< RankedTensorType > v, AffineMap indexingMap, ArrayRef< OpFoldResult > indexingSizes, const PadTilingInterfaceOptions &options)
Helper function to compute the padded shape of the given value v of RankedTensorType given:
OpFoldResult createFoldedDimOp(OpBuilder &b, Location loc, Value val, int64_t dim)
Create one memref::DimOp or tensor::DimOp depending on the type of val.
FailureOr< SmallVector< OpFoldResult > > computeIndexingMapOpInterfacePaddedShape(OpBuilder &, OpOperand &operandToPad, ArrayRef< Range > iterationDomain, const PadTilingInterfaceOptions &)
Specific helper for Linalg ops.
Value makeComposedPadHighOp(OpBuilder &b, Location loc, RankedTensorType type, Value source, Value padding, bool nofold, ValueRange typeDynDims={})
Create a tensor::PadOp that pads source to the shape of type whose sizes are assumed to be greater th...
detail::poison_attr_matcher m_Poison()
Matches a poison constant (any attribute implementing PoisonAttrInterface).
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
LogicalResult reifyResultShapes(OpBuilder &b, Operation *op, ReifiedRankedShapedTypeDims &reifiedReturnShapes)
Reify the shape of the result of an operation (typically in terms of the shape of its operands).
void bindDimsList(MLIRContext *ctx, MutableArrayRef< AffineExprTy > exprs)
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
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...
@ Mul
RHS of mul is always a constant or a symbolic expression.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
AffineMap projectDims(AffineMap map, const llvm::SmallBitVector &projectedDimensions, bool compressDimsFlag=false)
Returns the map that results from projecting out the dimensions specified in projectedDimensions.
Represents a range (offset, size, and stride) where each element of the triple may be dynamic or stat...
Operations and values created in the process of padding a TilingInterface operation.