18#define GEN_PASS_DEF_LEGALIZEVECTORSTORAGE
19#include "mlir/Dialect/ArmSVE/Transforms/Passes.h.inc"
45bool isSVEMaskType(VectorType type) {
46 return type.getRank() > 0 && type.getElementType().isInteger(1) &&
47 type.getScalableDims().back() && type.getShape().back() < 16 &&
48 llvm::isPowerOf2_32(type.getShape().back()) &&
49 !llvm::is_contained(type.getScalableDims().drop_back(),
true);
52VectorType widenScalableMaskTypeToSvbool(VectorType type) {
53 assert(isSVEMaskType(type));
59template <
typename TOp,
typename TLegalizerCallback>
61 TLegalizerCallback callback) {
63 auto newOp = op.clone();
70template <
typename TOp,
typename TLegalizerCallback>
71void replaceOpWithUnrealizedConversion(
PatternRewriter &rewriter, TOp op,
72 TLegalizerCallback callback) {
73 replaceOpWithLegalizedOp(rewriter, op, [&](TOp newOp) {
75 return UnrealizedConversionCastOp::create(
76 rewriter, op.getLoc(),
TypeRange{op.getResult().getType()},
85static FailureOr<Value> getSVELegalizedMemref(
Value illegalMemref) {
89 auto unrealizedConversion =
90 llvm::cast<UnrealizedConversionCastOp>(definingOp);
91 return unrealizedConversion.getOperand(0);
97struct RelaxScalableVectorAllocaAlignment
101 LogicalResult matchAndRewrite(memref::AllocaOp allocaOp,
103 auto memrefElementType = allocaOp.getType().getElementType();
104 auto vectorType = llvm::dyn_cast<VectorType>(memrefElementType);
105 if (!vectorType || !vectorType.isScalable() || allocaOp.getAlignment())
109 unsigned aligment = vectorType.getElementType().isInteger(1) ? 2 : 16;
111 [&] { allocaOp.setAlignment(aligment); });
132template <
typename AllocLikeOp>
134 using OpRewritePattern<AllocLikeOp>::OpRewritePattern;
136 LogicalResult matchAndRewrite(AllocLikeOp allocLikeOp,
137 PatternRewriter &rewriter)
const override {
139 llvm::dyn_cast<VectorType>(allocLikeOp.getType().getElementType());
141 if (!vectorType || !isSVEMaskType(vectorType))
147 replaceOpWithUnrealizedConversion(
148 rewriter, allocLikeOp, [&](AllocLikeOp newAllocLikeOp) {
149 newAllocLikeOp.getResult().setType(
150 llvm::cast<MemRefType>(newAllocLikeOp.getType().cloneWith(
151 {}, widenScalableMaskTypeToSvbool(vectorType))));
152 return newAllocLikeOp;
180struct LegalizeSVEMaskTypeCastConversion
184 LogicalResult matchAndRewrite(vector::TypeCastOp typeCastOp,
185 PatternRewriter &rewriter)
const override {
186 auto resultType = typeCastOp.getResultMemRefType();
187 auto vectorType = llvm::dyn_cast<VectorType>(resultType.getElementType());
189 if (!vectorType || !isSVEMaskType(vectorType))
192 auto legalMemref = getSVELegalizedMemref(typeCastOp.getMemref());
197 replaceOpWithUnrealizedConversion(
198 rewriter, typeCastOp, [&](vector::TypeCastOp newTypeCast) {
199 newTypeCast.setOperand(*legalMemref);
200 newTypeCast.getResult().setType(
201 llvm::cast<MemRefType>(newTypeCast.getType().cloneWith(
202 {}, widenScalableMaskTypeToSvbool(vectorType))));
223struct LegalizeSVEMaskStoreConversion
227 LogicalResult matchAndRewrite(memref::StoreOp storeOp,
228 PatternRewriter &rewriter)
const override {
229 auto loc = storeOp.getLoc();
231 Value valueToStore = storeOp.getValueToStore();
232 auto vectorType = llvm::dyn_cast<VectorType>(valueToStore.
getType());
234 if (!vectorType || !isSVEMaskType(vectorType))
237 auto legalMemref = getSVELegalizedMemref(storeOp.getMemref());
241 auto legalMaskType = widenScalableMaskTypeToSvbool(
242 llvm::cast<VectorType>(valueToStore.
getType()));
243 auto convertToSvbool = arm_sve::ConvertToSvboolOp::create(
244 rewriter, loc, legalMaskType, valueToStore);
247 replaceOpWithLegalizedOp(rewriter, storeOp,
248 [&](memref::StoreOp newStoreOp) {
249 newStoreOp.setOperand(0, convertToSvbool);
250 newStoreOp.setOperand(1, *legalMemref);
271struct LegalizeSVEMaskLoadConversion :
public OpRewritePattern<memref::LoadOp> {
274 LogicalResult matchAndRewrite(memref::LoadOp loadOp,
275 PatternRewriter &rewriter)
const override {
276 auto loc = loadOp.getLoc();
278 Value loadedMask = loadOp.getResult();
279 auto vectorType = llvm::dyn_cast<VectorType>(loadedMask.
getType());
281 if (!vectorType || !isSVEMaskType(vectorType))
284 auto legalMemref = getSVELegalizedMemref(loadOp.getMemref());
288 auto legalMaskType = widenScalableMaskTypeToSvbool(vectorType);
291 replaceOpWithLegalizedOp(rewriter, loadOp, [&](memref::LoadOp newLoadOp) {
292 newLoadOp.setMemRef(*legalMemref);
293 newLoadOp.getResult().setType(legalMaskType);
294 return arm_sve::ConvertFromSvboolOp::create(
295 rewriter, loc, loadedMask.
getType(), newLoadOp);
324struct LegalizeTransferRead :
public OpRewritePattern<vector::TransferReadOp> {
327 LogicalResult matchAndRewrite(vector::TransferReadOp readOp,
328 PatternRewriter &rewriter)
const override {
341 if (readOp.isMasked() || readOp.getMask())
343 "masked transfers not-supported");
354 if (!readOp.getPermutationMap().isMinorIdentity())
365 VectorType origVT = readOp.getVectorType();
366 ArrayRef<bool> origScalableDims = origVT.getScalableDims();
367 const int64_t origVRank = origVT.getRank();
368 if (origVRank < 2 || origVT.getNumScalableDims() != 1)
374 const int64_t numCollapseDims = std::distance(
375 llvm::find(origScalableDims,
true), origScalableDims.end());
376 if (numCollapseDims < 2)
378 "scalable dimension is trailing");
382 auto memTy = dyn_cast<MemRefType>(readOp.getBase().getType());
383 if (!(memTy && memTy.areTrailingDimsContiguous(numCollapseDims)))
385 readOp,
"non-contiguous memref dimensions to collapse");
393 if (!llvm::equal(memTy.getShape().take_back(numCollapseDims - 1),
394 origVT.getShape().take_back(numCollapseDims - 1)))
396 readOp,
"memref and vector dimensions do not match");
398 SmallVector<bool> origInBounds = readOp.getInBoundsValues();
400 ArrayRef<bool>(origInBounds).take_back(numCollapseDims - 1),
401 [](
bool v) {
return v; }))
403 readOp,
"out-of-bounds transfer from a dimension to collapse");
406 SmallVector<ReassociationIndices> reassoc;
407 for (int64_t i = 0; i < memTy.getRank() - numCollapseDims + 1; ++i)
408 reassoc.push_back({i});
409 for (int64_t i = memTy.getRank() - numCollapseDims + 1; i < memTy.getRank();
411 reassoc.back().push_back(i);
412 if (!memref::CollapseShapeOp::isGuaranteedCollapsible(memTy, reassoc))
414 Value collapsedMem = memref::CollapseShapeOp::create(
415 rewriter, readOp.getLoc(), readOp.getBase(), reassoc);
418 SmallVector<int64_t> shape(origVT.getShape());
419 for (int64_t i = origVRank - numCollapseDims + 1; i < origVRank; ++i)
420 shape[origVRank - numCollapseDims] *= shape[i];
421 shape.pop_back_n(numCollapseDims - 1);
423 VectorType::get(shape, origVT.getElementType(),
424 origScalableDims.drop_back(numCollapseDims - 1));
430 readOp.getIndices().drop_back(numCollapseDims - 1));
432 rewriter, readOp.getLoc(),
433 ShapedType::getNumElements(
434 origVT.getShape().take_back(numCollapseDims - 1)));
436 readOp.getLoc(),
indices.back(), scale);
439 auto newReadOp = vector::TransferReadOp::create(
440 rewriter, readOp.getLoc(), collapsedVT, collapsedMem,
indices,
442 ArrayRef<bool>(origInBounds).drop_back(numCollapseDims - 1));
445 auto toOrigShape = vector::ShapeCastOp::create(rewriter, readOp.getLoc(),
458 .
add<RelaxScalableVectorAllocaAlignment,
459 LegalizeSVEMaskAllocation<memref::AllocaOp>,
460 LegalizeSVEMaskAllocation<memref::AllocOp>,
461 LegalizeSVEMaskTypeCastConversion, LegalizeSVEMaskStoreConversion,
462 LegalizeSVEMaskLoadConversion, LegalizeTransferRead>(
467struct LegalizeVectorStorage
470 void runOnOperation()
override {
477 target.addDynamicallyLegalOp<UnrealizedConversionCastOp>(
478 [](UnrealizedConversionCastOp unrealizedConversion) {
482 if (
failed(applyPartialConversion(getOperation(),
target, {})))
490 return std::make_unique<LegalizeVectorStorage>();
constexpr StringLiteral kSVELegalizerTag("__arm_sve_legalize_vector_storage__")
StringAttr getStringAttr(const Twine &bytes)
NamedAttribute represents a combination of a name and an Attribute value.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
Operation * insert(Operation *op)
Insert the given operation at the current insertion point and return it.
Operation is the basic unit of execution within MLIR.
bool hasDiscardableAttr(StringRef name)
Return true if this operation has a discardable attribute with the provided name.
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 replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
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,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
This class provides an abstraction over the various different ranges of value types.
This class provides an abstraction over the different types of ranges over Values.
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.
This is a builder type that keeps local references to arguments.
Builder & setDim(unsigned pos, int64_t val)
Set a dim in shape @pos to val.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
void populateLegalizeVectorStoragePatterns(RewritePatternSet &patterns)
Collect a set of patterns to legalize Arm SVE vector storage.
std::unique_ptr< Pass > createLegalizeVectorStoragePass()
Pass to legalize Arm SVE vector storage.
Include the generated interface declarations.
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...