21#define DEBUG_TYPE "x86-vector-contract-multi-level-unroll"
26using llvm::drop_begin;
36 bool isInVnniLayout =
false;
46 VectorType accRegTileType;
47 VectorType lhsRegTileType;
48 VectorType rhsRegTileType;
52 VectorType accNativeType;
53 VectorType lhsNativeType;
54 VectorType rhsNativeType;
61 bool accIsZeroInit =
false;
62 vector::TransferReadOp lhsRead;
63 vector::TransferReadOp rhsRead;
70 scf::ForOp slicedAccLoop;
71 scf::ForOp slicedInnerAccLoop;
82using MatchTypesAndShapeFn = std::function<LogicalResult(
83 Type inputElemType,
Type accumulatorElemType,
bool isInVnniLayout,
109 auto denseAttr = dyn_cast<DenseElementsAttr>(constOp.getValue());
110 if (!denseAttr || !denseAttr.isSplat())
119 return op.getPermutationMap().isMinorIdentity() && !op.getMask() &&
120 !op.hasOutOfBoundsDim();
129 for (
Operation *op = start->getNextNode(); op && op != end;
130 op = op->getNextNode()) {
145 VectorType targetType) {
146 auto *ctx =
b.getContext();
148 auto baseType = dyn_cast<MemRefType>(base.
getType());
149 assert(baseType &&
"base must be a MemRefType");
151 int64_t baseRank = baseType.getRank();
152 int64_t targetRank = targetType.getRank();
153 assert(targetRank <= baseRank &&
"target shape's rank > memref's rank");
158 copy(targetShape, sizesInt.begin() + baseRank - targetRank);
165 MemRefType rankReducedType = memref::SubViewOp::inferRankReducedResultType(
166 targetShape, baseType, offsets, sizes, strides);
167 return memref::SubViewOp::create(
b, loc, rankReducedType, base, offsets,
173 VectorType nativeType) {
174 VectorType regTileType = dyn_cast<VectorType>(value.
getType());
175 assert(regTileType &&
"can only slice vector values");
179 assert(regTileShape.size() == 2 && nativeShape.size() == 2 &&
180 "Only 2D shapes are supported");
182 int64_t regTileM = regTileShape[0];
183 int64_t regTileN = regTileShape[1];
184 int64_t nativeM = nativeShape[0];
185 int64_t nativeN = nativeShape[1];
188 result.reserve((regTileM / nativeM) * (regTileN / nativeN));
190 for (
int64_t m = 0; m < regTileM; m += nativeM)
191 for (
int64_t n = 0; n < regTileN; n += nativeN)
192 result.push_back(vector::ExtractStridedSliceOp::create(
201 VectorType regTileType) {
203 assert(!types.empty() && all_equal(types) &&
204 "All values must have the same type");
206 VectorType nativeType = dyn_cast<VectorType>(types.front());
207 assert(nativeType &&
"can only splice vector values");
212 assert(nativeShape.size() == 2 && regTileShape.size() == 2 &&
213 "Only 2D shapes are supported");
215 int64_t nativeM = nativeShape[0];
216 int64_t nativeN = nativeShape[1];
217 int64_t regTileM = regTileShape[0];
218 int64_t regTileN = regTileShape[1];
220 assert((regTileM / nativeM) * (regTileN / nativeN) ==
221 static_cast<int64_t>(values.size()) &&
222 "The number of values must match the number of slices");
224 Value result = ub::PoisonOp::create(
b, loc, regTileType);
226 auto valueIt = values.begin();
227 for (
int64_t m = 0; m < regTileM; m += nativeM)
228 for (
int64_t n = 0; n < regTileN; n += nativeN) {
229 result = vector::InsertStridedSliceOp::create(
b, loc, *valueIt,
result,
238static std::optional<SmallVector<int64_t>>
247 VectorType nativeType) {
248 if (op.getVectorType() != nativeType)
282 vector::ContractionOp
contract = candidate.contract;
283 if (
contract.getKind() != vector::CombiningKind::ADD)
285 "not using ADD combining kind");
287 VectorType accType = dyn_cast<VectorType>(
contract.getAccType());
288 if (!accType || accType.getRank() != 2)
290 "accumulator is not a 2D vector");
292 VectorType lhsType =
contract.getLhsType();
293 VectorType rhsType =
contract.getRhsType();
294 if (lhsType.getElementType() != rhsType.getElementType())
296 contract,
"LHS and RHS element types do not match");
298 auto iteratorTypes =
contract.getIteratorTypesArray();
299 auto indexingMaps =
contract.getIndexingMapsArray();
300 assert(indexingMaps.size() == 3 &&
"expected 3 indexing maps");
303 auto par = vector::IteratorType::parallel;
304 auto red = vector::IteratorType::reduction;
310 if (!isInVnniLayout) {
311 if (!equal(indexingMaps[0].getResults(),
ArrayRef{m, k}) ||
312 !equal(indexingMaps[1].getResults(),
ArrayRef{k, n}) ||
313 !equal(indexingMaps[2].getResults(),
ArrayRef{m, n}))
315 "indexing maps are not canonical");
317 if (!equal(iteratorTypes,
ArrayRef{par, par, red}))
319 "iterator types are not canonical");
321 if (!equal(indexingMaps[0].getResults(),
ArrayRef{m, k, vnni}) ||
322 !equal(indexingMaps[1].getResults(),
ArrayRef{k, n, vnni}) ||
323 !equal(indexingMaps[2].getResults(),
ArrayRef{m, n}))
325 contract,
"indexing maps are not canonical (VNNI)");
327 if (!equal(iteratorTypes,
ArrayRef{par, par, red, red}))
329 contract,
"iterator types are not canonical (VNNI)");
332 candidate.isInVnniLayout = isInVnniLayout;
333 candidate.accType = accType;
334 candidate.lhsType = lhsType;
335 candidate.rhsType = rhsType;
336 candidate.contractK = lhsType.getShape()[1];
348 const MatchTypesAndShapeFn &matchTypesAndShapeForTarget,
350 vector::ContractionOp
contract = candidate.contract;
352 Type inpElemType = candidate.lhsType.getElementType();
353 Type accElemType = candidate.accType.getElementType();
360 if (candidate.isInVnniLayout)
361 contractShape.push_back(lhsShape[2]);
364 if (failed(matchTypesAndShapeForTarget(
365 inpElemType, accElemType, candidate.isInVnniLayout, contractShape,
366 contractRegTileShape, contractNativeShape)) ||
367 contractRegTileShape.size() < contractShape.size() ||
368 contractNativeShape.size() < contractShape.size())
370 contract,
"no tiling and unrolling strategy found");
372 auto maybeTileShapeRatio =
374 auto maybeUnrollShapeRatio =
377 if (!maybeTileShapeRatio || !maybeUnrollShapeRatio)
379 contract,
"incompatible or inconsistent tiling and unrolling strategy");
381 auto isOne = [](
int64_t v) {
return v == 1; };
382 if (all_of(*maybeTileShapeRatio, isOne) &&
383 all_of(*maybeUnrollShapeRatio, isOne))
385 contract,
"contraction is already in native shape");
387 if (!all_of(drop_begin(*maybeUnrollShapeRatio, 2), isOne))
389 contract,
"cannot unroll in the reduction dimensions");
391 if (candidate.isInVnniLayout && (maybeTileShapeRatio->back() != 1))
393 "cannot tile in the VNNI dimension");
395 int64_t regTileM = contractRegTileShape[0];
396 int64_t regTileN = contractRegTileShape[1];
397 int64_t regTileK = contractRegTileShape[2];
399 int64_t nativeM = contractNativeShape[0];
400 int64_t nativeN = contractNativeShape[1];
401 int64_t nativeK = contractNativeShape[2];
403 candidate.accRegTileType = VectorType::get({regTileM, regTileN}, accElemType);
404 candidate.accNativeType = VectorType::get({nativeM, nativeN}, accElemType);
407 if (candidate.isInVnniLayout) {
409 newShape.push_back(contractNativeShape[3]);
410 return VectorType::get(newShape, inpElemType);
412 return VectorType::get(
shape, inpElemType);
415 candidate.lhsRegTileType = getOperandType({regTileM, regTileK});
416 candidate.rhsRegTileType = getOperandType({regTileK, regTileN});
418 candidate.lhsNativeType = getOperandType({nativeM, nativeK});
419 candidate.rhsNativeType = getOperandType({nativeK, nativeN});
421 candidate.contractNativeShape.append(contractNativeShape.begin(),
422 contractNativeShape.end());
458 auto &
contract = candidate.contract;
460 auto accLoop = dyn_cast_if_present<scf::ForOp>(
contract->getParentOp());
461 if (!accLoop || !accLoop.getInductionVar().getType().isIndex())
463 "is not in an index-based for-loop");
466 !step || (*step != 1 && *step != candidate.contractK))
468 "accumulation loop step is not 1 or K");
471 if (!accIterArg || accIterArg.
getOwner() != accLoop.getBody())
473 "accumulator is not a block argument");
475 if (accLoop.getNumRegionIterArgs() != 1 ||
476 accLoop.getBody()->getOperations().size() != 4)
478 contract,
"accumulation loop has additional iter_args or operations");
480 if (!
contract->getResult(0).hasOneUse())
482 "result does not have exactly one use");
485 if (!isa<scf::YieldOp>(user))
487 "accumulator is not loop-carried");
489 Value accInitVal = accLoop.getTiedLoopInit(accIterArg)->get();
492 auto lhsRead =
contract.getLhs().getDefiningOp<vector::TransferReadOp>();
493 auto rhsRead =
contract.getRhs().getDefiningOp<vector::TransferReadOp>();
494 if (!lhsRead || !rhsRead)
496 contract,
"LHS/RHS are not vector.transfer_read ops");
498 auto checkOperandRead = [&](vector::TransferReadOp read,
unsigned kVecDim) {
501 contract,
"operand read has non-identity permutation, mask, or "
502 "out-of-bounds check");
504 auto indices = read.getIndices();
507 contract,
"operand read VNNI dimension offset must be zero");
510 std::find(
indices.begin(),
indices.end(), accLoop.getInductionVar());
513 contract,
"operand read does not use loop index");
514 if (std::find(ivIt + 1,
indices.end(), accLoop.getInductionVar()) !=
517 contract,
"operand read uses loop index more than once");
537 unsigned firstVecDimPos = read.getLeadingShapedRank();
538 unsigned ivPos = std::distance(
indices.begin(), ivIt);
539 if (ivPos < firstVecDimPos &&
540 candidate.contractK > candidate.contractNativeShape[2] &&
543 "operand read from blocked layout has "
544 "offset in vector's K dimension");
549 if (failed(checkOperandRead(lhsRead, 1)) ||
550 failed(checkOperandRead(rhsRead, 0)))
553 candidate.accLoop = accLoop;
554 candidate.accInitVal = accInitVal;
555 candidate.accIsZeroInit = accIsZeroInit;
556 candidate.lhsRead = lhsRead;
557 candidate.rhsRead = rhsRead;
576 if (candidate.accIsZeroInit)
581 auto loc = candidate.accLoop.getLoc();
584 candidate.accInitVal.
getDefiningOp<vector::TransferReadOp>();
589 accRead.getIndices(), accRead.getVectorType());
593 auto accBufferType = MemRefType::get(candidate.accType.getShape(),
594 candidate.accType.getElementType());
595 candidate.accMemIn = memref::AllocaOp::create(rewriter, loc, accBufferType);
599 vector::TransferWriteOp::create(rewriter, loc, candidate.accInitVal,
600 candidate.accMemIn, zeroIndices);
618 auto loc = candidate.accLoop.getLoc();
620 auto accRes = candidate.accLoop.getResult(0);
621 vector::TransferWriteOp accWrite;
622 if (accRes.hasOneUse())
623 accWrite = dyn_cast<vector::TransferWriteOp>(*accRes.getUsers().begin());
625 Block *accLoopBlock = candidate.accLoop->getBlock();
629 Value accWriteBase = accWrite.getBase();
630 ValueRange accWriteIndices = accWrite.getIndices();
632 auto isSafeToUseView = [&]() {
633 if (candidate.accIsZeroInit)
635 auto accMemInView = candidate.accMemIn.
getDefiningOp<memref::SubViewOp>();
639 return accMemInView.getSource() == accWriteBase &&
644 auto isDefinedBeforeAccLoop = [&](
Value v) {
648 return !defOp || defOp->
getBlock() != accLoopBlock ||
652 if (isSafeToUseView() && isDefinedBeforeAccLoop(accWriteBase) &&
653 all_of(accWriteIndices, isDefinedBeforeAccLoop)) {
654 candidate.accMemOut =
656 accWrite.getVectorType());
657 candidate.opsToDelete.push_back(accWrite);
658 candidate.opsToDelete.push_back(candidate.accLoop);
663 if (!candidate.accIsZeroInit &&
665 candidate.accMemOut = candidate.accMemIn;
667 auto accBufferType = MemRefType::get(candidate.accType.getShape(),
668 candidate.accType.getElementType());
669 candidate.accMemOut =
670 memref::AllocaOp::create(rewriter, loc, accBufferType);
676 auto accRemat = vector::TransferReadOp::create(
677 rewriter, loc, candidate.accType, candidate.accMemOut, zeroIndices,
681 candidate.opsToDelete.push_back(candidate.accLoop);
690 auto loc = candidate.accLoop.getLoc();
695 auto makeIndexConst = [&](
int64_t v) {
699 Value c0 = makeIndexConst(0);
700 Value ubM = makeIndexConst(accShape[0]);
701 Value stepM = makeIndexConst(accRegTileShape[0]);
702 Value ubN = makeIndexConst(accShape[1]);
703 Value stepN = makeIndexConst(accRegTileShape[1]);
706 rewriter, loc, c0, ubM, stepM, {},
709 b, loc, c0, ubN, stepN, {},
712 scf::YieldOp::create(
b, loc);
714 scf::YieldOp::create(
b, loc);
742 b.getI64IntegerAttr(accRegTileShape[0]),
743 b.getI64IntegerAttr(accRegTileShape[1])};
745 b.getI64IntegerAttr(1)};
748 if (candidate.accIsZeroInit) {
749 accInit = arith::ConstantOp::create(
750 b, loc,
b.getZeroAttr(candidate.accRegTileType));
753 memref::SubViewOp::create(
b, loc, candidate.accMemIn, accViewOffsets,
754 accViewSizes, accViewStrides);
755 auto accInitRead = vector::TransferReadOp::create(
756 b, loc, candidate.accRegTileType, accViewIn,
ValueRange{c0, c0},
759 accInit = accInitRead;
766 Value lbK = candidate.accLoop.getLowerBound();
767 Value ubK = candidate.accLoop.getUpperBound();
768 Value stepK = candidate.accLoop.getStep();
780 int64_t contractNativeK = candidate.contractNativeShape[2];
781 bool needToTileK = candidate.contractK > contractNativeK;
782 bool needLLoop =
false;
785 Value cContractNativeK =
790 stepL = cContractNativeK;
792 stepK = cContractNativeK;
796 candidate.slicedAccLoop = scf::ForOp::create(
797 b, loc, lbK, ubK, stepK, accInitSliced,
801 candidate,
b, loc, ivM, ivN, ivK,
Value{}, iterArgsK);
802 scf::YieldOp::create(
b, loc, newIterArgs);
806 candidate.slicedInnerAccLoop = scf::ForOp::create(
807 b, loc, lbL, ubL, stepL, iterArgsK,
810 candidate,
b, loc, ivM, ivN, ivK, ivL, iterArgsL);
811 scf::YieldOp::create(
b, loc, newIterArgs);
813 scf::YieldOp::create(
b, loc, candidate.slicedInnerAccLoop.getResults());
816 Value accSpliced =
splice(
b, loc, candidate.slicedAccLoop.getResults(),
817 candidate.accRegTileType);
819 memref::SubViewOp::create(
b, loc, candidate.accMemOut, accViewOffsets,
820 accViewSizes, accViewStrides);
821 auto accWrite = vector::TransferWriteOp::create(
822 b, loc, accSpliced, accViewOut,
ValueRange{c0, c0});
838 Value accSpliced =
splice(
b, loc, iterArgs, candidate.accRegTileType);
843 Value accLoopIV = candidate.accLoop.getInductionVar();
844 replace(lhsIndices, accLoopIV, ivK);
845 replace(rhsIndices, accLoopIV, ivK);
847 unsigned mPos = lhsIndices.size() - 2 - candidate.isInVnniLayout;
848 unsigned nPos = rhsIndices.size() - 1 - candidate.isInVnniLayout;
849 lhsIndices[mPos] = arith::AddIOp::create(
b, loc, lhsIndices[mPos], ivM);
850 rhsIndices[nPos] = arith::AddIOp::create(
b, loc, rhsIndices[nPos], ivN);
853 unsigned lPosL = lhsIndices.size() - 1 - candidate.isInVnniLayout;
854 unsigned lPosR = rhsIndices.size() - 2 - candidate.isInVnniLayout;
858 "Expected zero offsets for L dimension");
859 lhsIndices[lPosL] = ivL;
860 rhsIndices[lPosR] = ivL;
864 lhsIndices, candidate.lhsRegTileType);
866 rhsIndices, candidate.rhsRegTileType);
870 auto lhsRead = vector::TransferReadOp::create(
871 b, candidate.lhsRead.getLoc(), candidate.lhsRegTileType, lhsView,
872 zeroIndices, std::nullopt);
873 auto rhsRead = vector::TransferReadOp::create(
874 b, candidate.rhsRead.getLoc(), candidate.rhsRegTileType, rhsView,
875 zeroIndices, std::nullopt);
876 auto contract = vector::ContractionOp::create(
877 b, candidate.contract.getLoc(), lhsRead, rhsRead, accSpliced,
878 candidate.contract.getIndexingMaps(),
879 candidate.contract.getIteratorTypes());
884 b.getDenseI64ArrayAttr(candidate.contractNativeShape));
912struct VectorContractMultiLevelUnroll
914 VectorContractMultiLevelUnroll(
915 MLIRContext *context,
916 const MatchTypesAndShapeFn &matchTypesAndShapeForTarget,
917 PatternBenefit benefit = 1)
918 : OpRewritePattern<vector::ContractionOp>(context, benefit),
919 matchTypesAndShapeForTarget(matchTypesAndShapeForTarget) {}
921 LogicalResult matchAndRewrite(vector::ContractionOp
contract,
922 PatternRewriter &rewriter)
const override {
928 MLUCandidate candidate;
939 if (candidate.slicedInnerAccLoop)
942 for (
auto *op : candidate.opsToDelete)
949 MatchTypesAndShapeFn matchTypesAndShapeForTarget;
967 if (!((inputElemType.
isBF16() && accumulatorElemType.
isF32()) ||
971 int64_t origM = contractShape[0];
972 int64_t origN = contractShape[1];
974 int64_t regTileM, regTileN, regTileK;
975 int64_t nativeM, nativeN, nativeK, vnni;
978 nativeM = nativeN = 16;
979 nativeK = isInVnniLayout ? 16 : 16 * vnni;
983 regTileM = origM % (2 * nativeM) == 0 ? 2 * nativeM : nativeM;
985 origN % (2 * nativeN) == 0 || !isInVnniLayout ? 2 * nativeN : nativeN;
988 contractRegTileShape.append({regTileM, regTileN, regTileK});
989 contractNativeShape.append({nativeM, nativeN, nativeK});
990 if (isInVnniLayout) {
991 contractRegTileShape.push_back(vnni);
992 contractNativeShape.push_back(vnni);
1004 patterns.
add<VectorContractMultiLevelUnroll>(patterns.
getContext(),
1008 vector::populateVectorUnrollPatterns(patterns, nativeUnrollOptions);
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
static void contract(RootOrderingGraph &graph, ArrayRef< Value > cycle, const DenseMap< Value, unsigned > &parentDepths, DenseMap< Value, Value > &actualSource, DenseMap< Value, Value > &actualTarget)
Contracts the specified cycle in the given graph in-place.
static void buildLoopNestForCandidate(MLUCandidate &candidate, PatternRewriter &rewriter)
static std::optional< SmallVector< int64_t > > getFromNativeShapeAttr(Operation *op)
static Value makeRankReducingSubview(OpBuilder &b, Location loc, Value base, ValueRange indices, VectorType targetType)
static void makeAccInitializationBackedByMemory(MLUCandidate &candidate, PatternRewriter &rewriter)
static Value splice(OpBuilder &b, Location loc, ValueRange values, VectorType regTileType)
static void buildMNLoopBody(MLUCandidate &candidate, OpBuilder &b, Location loc, Value ivM, Value ivN)
static void makeAccResultBackedByMemory(MLUCandidate &candidate, PatternRewriter &rewriter)
static SmallVector< Value > buildKLLoopBody(MLUCandidate &candidate, OpBuilder &b, Location loc, Value ivM, Value ivN, Value ivK, Value ivL, ValueRange iterArgs)
static void setNativeShapeAttr(VectorTransferOpInterface op, VectorType nativeType)
static bool isZeroVector(Value v)
static constexpr auto nativeShapeAttrName
static LogicalResult matchTypesAndShape(MLUCandidate &candidate, const MatchTypesAndShapeFn &matchTypesAndShapeForTarget, PatternRewriter &rewriter)
static LogicalResult matchAccumulationLoop(MLUCandidate &candidate, PatternRewriter &rewriter)
static bool noMemoryEffectsBetween(Operation *start, Operation *end)
static LogicalResult matchTypesAndShapeForAMX(Type inputElemType, Type accumulatorElemType, bool isInVnniLayout, ArrayRef< int64_t > contractShape, SmallVectorImpl< int64_t > &contractRegTileShape, SmallVectorImpl< int64_t > &contractNativeShape)
static LogicalResult matchCanonicalMatmul(MLUCandidate &candidate, PatternRewriter &rewriter)
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
static bool isSimpleTransferOp(VectorTransferOpInterface op)
Attributes are known-constant values of operations.
This class represents an argument of a Block.
Block * getOwner() const
Returns the block that owns this argument.
Block represents an ordered list of Operations.
AffineExpr getAffineDimExpr(unsigned position)
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Operation is the basic unit of execution within MLIR.
AttrClass getAttrOfType(StringAttr name)
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.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
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).
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class provides an abstraction over the different types of ranges over Values.
type_range getTypes() const
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.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< int64_t > content)
void populateVectorContractMultiLevelUnrollForAMXPatterns(RewritePatternSet &patterns)
bool isInVnniLayout(Operation *op, llvm::ArrayRef< AffineMap > indexingMaps, std::optional< unsigned > blockingFactor=std::nullopt)
Include the generated interface declarations.
OpFoldResult getAsIndexOpFoldResult(MLIRContext *ctx, int64_t val)
Convert int64_t to integer attributes of index type and return them as OpFoldResult.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
bool isZeroIntegerOrFloat(OpFoldResult v)
Return "true" if v is an integer/float value/attribute with constant value zero.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
bool isEqualConstantIntOrValueArray(ArrayRef< OpFoldResult > ofrs1, ArrayRef< OpFoldResult > ofrs2)
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
std::optional< SmallVector< int64_t > > computeShapeRatio(ArrayRef< int64_t > shape, ArrayRef< int64_t > subShape)
Return the multi-dimensional integral ratio of subShape to the trailing dimensions of shape.
size_t moveLoopInvariantCode(ArrayRef< Region * > regions, function_ref< bool(Value, Region *)> isDefinedOutsideRegion, function_ref< bool(Operation *, Region *)> shouldMoveOutOfRegion, function_ref< void(Operation *, Region *)> moveOutOfRegion)
Given a list of regions, perform loop-invariant code motion.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
Options that control the vector unrolling.
UnrollVectorOptions & setNativeShapeFn(NativeShapeFnType fn)