summaryrefslogtreecommitdiff
path: root/mlir/lib/Dialect/Vector/Transforms/VectorTransforms.cpp
diff options
context:
space:
mode:
authorQuinn Dawkins <quinn@nod-labs.com>2023-04-06 16:45:59 -0400
committerQuinn Dawkins <quinn@nod-labs.com>2023-04-25 20:18:02 -0400
commit650f04feda9039e170de513dab261c672fa847cd (patch)
tree7b71fa380ec9b1519d338c65f1c41b9df13dbebf /mlir/lib/Dialect/Vector/Transforms/VectorTransforms.cpp
parentfc28560fd675a93f06d73628b469fbe3c6850282 (diff)
[mlir][vector] Add pattern to break down vector.bitcast
The pattern added here is intended as a last resort for targets like SPIR-V where there are vector size restrictions and we need to be able to break down large vector types. Vectorizing loads/stores for small bitwidths (e.g. i8) relies on bitcasting to a larger element type and patterns to bubble bitcast ops to where they can cancel. This fails for cases such as ``` %1 = arith.trunci %0 : vector<2x32xi32> to vector<2x32xi8> vector.transfer_write %1, %destination[%c0, %c0] {in_bounds = [true, true]} : vector<2x32xi8>, memref<2x32xi8> ``` where the `arith.trunci` op essentially does the job of one of the bitcasts, leading to a bitcast that need to be further broken down ``` vector.bitcast %0 : vector<16xi8> to vector<4xi32> ``` Differential Revision: https://reviews.llvm.org/D149065
Diffstat (limited to 'mlir/lib/Dialect/Vector/Transforms/VectorTransforms.cpp')
-rw-r--r--mlir/lib/Dialect/Vector/Transforms/VectorTransforms.cpp92
1 files changed, 92 insertions, 0 deletions
diff --git a/mlir/lib/Dialect/Vector/Transforms/VectorTransforms.cpp b/mlir/lib/Dialect/Vector/Transforms/VectorTransforms.cpp
index d412b157e284..44f3a10c4da5 100644
--- a/mlir/lib/Dialect/Vector/Transforms/VectorTransforms.cpp
+++ b/mlir/lib/Dialect/Vector/Transforms/VectorTransforms.cpp
@@ -800,6 +800,91 @@ struct BubbleUpBitCastForStridedSliceInsert
}
};
+// Breaks down vector.bitcast op
+//
+// This transforms IR like:
+// %1 = vector.bitcast %0: vector<8xf16> to vector<4xf32>
+// Into:
+// %cst = vector.splat %c0_f32 : vector<4xf32>
+// %1 = vector.extract_strided_slice %0 {
+// offsets = [0], sizes = [4], strides = [1]
+// } : vector<8xf16> to vector<4xf16>
+// %2 = vector.bitcast %1 : vector<4xf16> to vector<2xf32>
+// %4 = vector.insert_strided_slice %2, %cst {
+// offsets = [0], strides = [1]} : vector<2xf32> into vector<4xf32>
+// %5 = vector.extract_strided_slice %0 {
+// offsets = [4], sizes = [4], strides = [1]
+// } : vector<8xf16> to vector<4xf16>
+// %6 = vector.bitcast %5 : vector<4xf16> to vector<2xf32>
+// %7 = vector.insert_strided_slice %6, %cst {
+// offsets = [2], strides = [1]} : vector<2xf32> into vector<4xf32>
+struct BreakDownVectorBitCast : public OpRewritePattern<vector::BitCastOp> {
+ using OpRewritePattern::OpRewritePattern;
+
+public:
+ BreakDownVectorBitCast(MLIRContext *context,
+ std::function<bool(vector::BitCastOp)> controlFn,
+ PatternBenefit benefit)
+ : OpRewritePattern(context, benefit), controlFn(std::move(controlFn)) {}
+
+ LogicalResult matchAndRewrite(vector::BitCastOp bitcastOp,
+ PatternRewriter &rewriter) const override {
+
+ if (controlFn && !controlFn(bitcastOp))
+ return failure();
+
+ VectorType castSrcType = bitcastOp.getSourceVectorType();
+ VectorType castDstType = bitcastOp.getResultVectorType();
+ assert(castSrcType.getRank() == castDstType.getRank());
+
+ // Only support rank 1 case for now.
+ if (castSrcType.getRank() != 1)
+ return failure();
+
+ int64_t castSrcLastDim = castSrcType.getShape().back();
+ int64_t castDstLastDim = castDstType.getShape().back();
+ // Require casting to less elements for now; other cases to be implemented.
+ if (castSrcLastDim < castDstLastDim)
+ return failure();
+
+ assert(castSrcLastDim % castDstLastDim == 0);
+ int64_t shrinkRatio = castSrcLastDim / castDstLastDim;
+ // Nothing to do if it is already bitcasting to a single element.
+ if (castSrcLastDim == shrinkRatio)
+ return failure();
+
+ Location loc = bitcastOp.getLoc();
+ Type elemType = castDstType.getElementType();
+ assert(elemType.isSignlessIntOrIndexOrFloat());
+
+ Value zero = rewriter.create<arith::ConstantOp>(
+ loc, elemType, rewriter.getZeroAttr(elemType));
+ Value res = rewriter.create<SplatOp>(loc, castDstType, zero);
+
+ SmallVector<int64_t> sliceShape{castDstLastDim};
+ SmallVector<int64_t> strides{1};
+ VectorType newCastDstType =
+ VectorType::get(SmallVector<int64_t>{castDstLastDim / shrinkRatio},
+ castDstType.getElementType());
+
+ for (int i = 0, e = shrinkRatio; i < e; ++i) {
+ Value extracted = rewriter.create<ExtractStridedSliceOp>(
+ loc, bitcastOp.getSource(), ArrayRef<int64_t>{i * castDstLastDim},
+ sliceShape, strides);
+ Value bitcast =
+ rewriter.create<BitCastOp>(loc, newCastDstType, extracted);
+ res = rewriter.create<InsertStridedSliceOp>(
+ loc, bitcast, res,
+ ArrayRef<int64_t>{i * castDstLastDim / shrinkRatio}, strides);
+ }
+ rewriter.replaceOp(bitcastOp, res);
+ return success();
+ }
+
+private:
+ std::function<bool(BitCastOp)> controlFn;
+};
+
// Helper that returns a vector comparison that constructs a mask:
// mask = [0,1,..,n-1] + [o,o,..,o] < [b,b,..,b]
//
@@ -1151,6 +1236,13 @@ void mlir::vector::populateBubbleVectorBitCastOpPatterns(
benefit);
}
+void mlir::vector::populateBreakDownVectorBitCastOpPatterns(
+ RewritePatternSet &patterns,
+ std::function<bool(vector::BitCastOp)> controlFn, PatternBenefit benefit) {
+ patterns.add<BreakDownVectorBitCast>(patterns.getContext(),
+ std::move(controlFn), benefit);
+}
+
void mlir::vector::populateVectorContractCanonicalizeMatmulToMMT(
RewritePatternSet &patterns,
std::function<LogicalResult(vector::ContractionOp)> constraint,