MLIR 24.0.0git
LowerContractToSVEPatterns.cpp
Go to the documentation of this file.
1//===- LowerContractToSVEPatterns.cpp - Contract to I8MM/BF16 ---*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements lowering patterns from vector.contract to operations
10// that map to instructions from the SVE FEAT_I8MM and FEAT_BF16 extensions.
11//
12// TODO: There may be opportunities to unify this with a similar pattern
13// for Neon. See:
14// https://github.com/llvm/llvm-project/issues/145559
15// LowerContractToNeonPatterns.cpp
16//
17//===----------------------------------------------------------------------===//
18
25#include "mlir/IR/AffineMap.h"
27
28#include <cassert>
29#include <numeric>
30
31#define DEBUG_TYPE "lower-contract-to-arm-sve-i8mm"
32
33using namespace mlir;
34using namespace mlir::arm_sve;
35
36namespace {
37// Get the operand of a `vector.contract`. This function is intended to abstract
38// away from the particular way a value is extended before feeding it into the
39// `vector.contract` - via zero-extend or an explicit or implicit sign-extend
40// (for implicit sign-extension see `vector.contract` documentation).
41//
42// The template parameter `Op` indicates the extension operation (explicit or
43// implicit) for which we are checking.
44//
45// Return success only for extensions from `i8` to `i32`.
46template <typename Op>
47std::optional<Value> getExtOperand(Value v) {
48
49 static_assert(llvm::is_one_of<Op, arith::ExtSIOp, arith::ExtUIOp>::value,
50 "Must be instantiated with either sign- or zero- extension op");
51
52 // If the operand is not defined by an explicit extend operation of the
53 // accepted operation type allow for an implicit sign-extension.
54 auto extOp = v.getDefiningOp<Op>();
55 if (!extOp) {
56 if constexpr (std::is_same<Op, arith::ExtSIOp>::value) {
57 auto vTy = cast<VectorType>(v.getType());
58 if (!vTy.getElementType().isSignlessInteger(8))
59 return {};
60 return v;
61 }
62 return {};
63 }
64
65 // If the operand is defined by an explicit extend operation of the accepted
66 // operation type, check it's extended from `i8` to `i32`.
67 auto inOp = extOp.getIn();
68 auto inTy = dyn_cast<VectorType>(inOp.getType());
69 if (!inTy || !inTy.getElementType().isSignlessInteger(8))
70 return {};
71
72 auto outTy = dyn_cast<VectorType>(extOp.getType());
73 if (!outTy || !outTy.getElementType().isSignlessInteger(32))
74 return {};
75
76 return inOp;
77}
78
79/// This class encapsulates the algorithm and parametrisation (in terms of types
80/// and dimensions) of lowering a `vector.contract` to "primitive" matrix
81/// multiplication operations of the SVE dialect (here "primitive" would mean
82/// corresponding to a single target instruction).
83///
84/// Supported are lowering to FEAT_I8MM `smmla`, `ummla`, and `usmmla`, and to
85/// FEAT_BF16 `bfmmla`. All the transformations are very similar to each other
86/// for concreteness the description below is given for `smmla`.
87///
88/// The lowering triggers for a contraction operation that performs a matrix
89/// multiply of two 8-bit integer matrix tiles with logical dimensions
90/// <Mx8> and <8x[N]> for the left-hand side (LHS) and the right-hand side
91/// (RHS), respectively, added to a 32-bit integer accumulator operand (ACC)
92/// with dimensions <Mx[N]>, yielding a <Mx[N]> 32-bit integer result (OUT).
93///
94/// The operands' shapes are such that the operands can be evenly split into
95/// sub-tiles with dimensions as expected by the targeted FEAT_I8MM
96/// instructions. The intent is that M and N are chosen (by higher level
97/// transforms) in such a way as to maximise register usage. The main use case
98/// we envision as of now is MMT4D, thus the RHS operand is expected
99/// pre-transposed.
100///
101/// The matrix multiplication is performed by unrolling the usual tiled matrix
102/// multiplication algorithm using sub-tiles with dimensions <2x8> for the
103/// LHS, <8x[2]> for the RHS, and <2x[2]> for the result and the input
104/// accumulator.
105///
106/// One way to illustrate the operation is as follows:
107///
108/// RHS<8x[N]>: <8x[2]> <8x[2]> ... <8x[2]>
109/// +-----------------------------
110/// LHS<Mx8>: <2x8> | <2x[2]> <2x[2]> ... <2x[2]>
111/// <2x8> | <2x[2]> <2x[2]> ... <2x[2]>
112/// ... | ... ... ... ...
113/// <2x8> | <2x[2]> <2x[2]> ... <2x[2]>
114///
115/// The RHS operand is unpacked into N/2 values, each representing a sequence
116/// of VSCALE number of sub-tiles with dimensions <8x2>.
117/// The LHS operand is initially unpacked into M/2 values, each representing a
118/// sub-tile with dimensions <2x8>, and then each such sub-tile is replicated
119/// VSCALE times. Multiplying thus replicated LHS sub-tile by the corresponding
120/// RHS sub-tile correctly computes an entire result sub-tile.
121/// The 2x2 sub-tiles of the ACC and OUT have rows that are not adjacent
122/// (in memory or when imposing a row-major layout on the 2D vector value).
123/// Reading the ACC is implemented as reading two consecutive rows and
124/// interleaving the by pairs to obtain a vector having length twice the length
125/// of an ACC row. This vector now is a sequence of one-dimensional tiles with
126/// the exact layout needed by the `smmla`/`bfmmla`/etc instructions, which
127/// tiles are extracted one by one. For illustration, if we have an 2x4 ACC tile
128/// a0 a1 b0 b1
129/// a2 a3 b2 b3
130/// we read the two rows as separate values and then interleave by pairs
131/// to obtain
132/// a0 a1 a2 a3 b0 b1 b2 b3
133/// from which we extract `a0 a1 a2 a3` and `b0 b1 b2 b3`.
134///
135/// Writing the OUT tile is done by the reverse of the above procedure,
136/// concatenate two "flattened" sub-tiles into
137/// c0 c1 c2 c3 d0 d1 d2 d3
138/// deinterleave by pairs to obtain as separate values
139/// c0 c1 d0 d1
140/// c2 c3 d2 d3
141/// which are then inserted into the final result.
142///
143/// Multiplication of a signed LHS by an unsigned LHS is performed by
144/// swapping the order of the operands and emitting an `usmmla` (since there
145/// isn't an `summla` instruction). Therefore each ACC sub-tile needs
146/// to be transposed before the addition and the sum, an OUT sub-tile,
147/// needs to be transposed before insertion into the final result.
148/// This is done very elegantly by a modification of the above to
149/// interleave/deinterleave not by pairs, but by individual elements, e.g.
150/// after ordinary interleave we obtain
151/// a0 a2 a1 a3 b0 b2 b1 b3
152/// which is exactly the desired layout of having each individual 2x2 tile
153/// transposed.
154///
155/// All of the above readily applies to FEAT_BF16 `bfmmla` with the
156/// difference that the shapes of the LHS, RHS are <Mx4>, <4x[M]>, and
157/// respectively, that is the "K" dimension is fixed to 4, instead of 8 (like
158/// for the integer case).
159class VectorContractRewriter {
160protected:
161 // Designate the operation (resp. instruction) used to do sub-tile matrix
162 // multiplications.
163 enum class MMLA {
164 Nop,
165 SignedInt, // smmla
166 UnsignedInt, // ummla
167 MixedInt, // usmmla
168 Bfloat // bfmmla
169 };
170
171 // Lower-level operation to be emitted.
172 MMLA mmlaOp = MMLA::Nop;
173
174 // Indicate if the operands for the ArmSVE dialect operation need to be
175 // swapped. Currently this is needed in order to emulate an "summla"
176 // operation.
177 bool swapOperands = false;
178
179 // The operand tiles. These are not necessarily the operends of
180 // `vector.contract`, for example they could be operands to `arith.extsi`
181 // that is in turn fed into `vector.contract`.
182 Value lhs;
183 Value rhs;
184 Value acc;
185
186 // Conventional names for matrix dimensions.
187 int64_t m = 0;
188 int64_t n = 0;
189 int64_t k = 0;
190
191 // Create the matrix mulitply and accumulate operation according to
192 // `mmlaOp`.
193 Value createMMLA(PatternRewriter &rewriter, Location loc, Value acc,
194 Value lhs, Value rhs);
195
196 // Check general preconditions for applying the transformation, common to the
197 // integer and the bfloat16 case.
198 LogicalResult match(vector::ContractionOp op, PatternRewriter &rewriter);
199
200public:
201 VectorContractRewriter() = default;
202
203 // Do the actuall rewrite. This member function is shared by both integer and
204 // bfloat16 rewrites.
205 Value lower(vector::ContractionOp op, PatternRewriter &rewriter);
206};
207
208Value VectorContractRewriter::createMMLA(PatternRewriter &rewriter,
209 Location loc, Value acc, Value lhs,
210 Value rhs) {
211
212 Type resTy = acc.getType();
213 if (swapOperands)
214 std::swap(lhs, rhs);
215
216 switch (mmlaOp) {
217 case MMLA::SignedInt:
218 return arm_sve::SmmlaOp::create(rewriter, loc, resTy, acc, lhs, rhs);
219 case MMLA::UnsignedInt:
220 return arm_sve::UmmlaOp::create(rewriter, loc, resTy, acc, lhs, rhs);
221 case MMLA::MixedInt:
222 return arm_sve::UsmmlaOp::create(rewriter, loc, resTy, acc, lhs, rhs);
223 case MMLA::Bfloat:
224 return arm_sve::BfmmlaOp::create(rewriter, loc, resTy, acc, lhs, rhs);
225 default:
226 llvm_unreachable("Uninitialized operation kind");
227 }
228}
229
230LogicalResult VectorContractRewriter::match(vector::ContractionOp op,
231 PatternRewriter &rewriter) {
232 // Check iterator types for matrix multiplication.
233 auto itTypes = op.getIteratorTypesArray();
234 if (itTypes.size() != 3 || itTypes[0] != vector::IteratorType::parallel ||
235 itTypes[1] != vector::IteratorType::parallel ||
236 itTypes[2] != vector::IteratorType::reduction)
237 return rewriter.notifyMatchFailure(
238 op, "iterator types do not correspond to matrix multiplication");
239
240 // Check permutation maps. For now only accept
241 // lhs: (d0, d1, d2) -> (d0, d2)
242 // rhs: (d0, d1, d2) -> (d1, d2)
243 // acc: (d0, d1, d2) -> (d0, d1)
244 // This corresponds to matrix multiplication with transposed RHS.
245 if (op.getIndexingMapsArray()[0] !=
247 op.getContext()) ||
248 op.getIndexingMapsArray()[1] !=
250 op.getContext()) ||
251 op.getIndexingMapsArray()[2] != AffineMap::getMultiDimMapWithTargets(
252 3, ArrayRef{0u, 1u}, op.getContext()))
253 return rewriter.notifyMatchFailure(op, "non-matching permutation maps");
254
255 // Check the combining kind is addition.
256 if (op.getKind() != vector::CombiningKind::ADD)
257 return rewriter.notifyMatchFailure(op, "combining kind is not an addition");
258
259 return success();
260}
261
262Value VectorContractRewriter::lower(vector::ContractionOp op,
263 PatternRewriter &rewriter) {
264
265 // Initialize some helper types.
266 Type operandEltType = cast<VectorType>(lhs.getType()).getElementType();
267 Type resultEltType = cast<VectorType>(op.getResultType()).getElementType();
268
269 const int64_t numOperandSubTileElts =
270 128 / operandEltType.getIntOrFloatBitWidth();
271
272 assert(resultEltType.getIntOrFloatBitWidth() == 32 &&
273 "Only implemented for i32 or f32 output");
274 const int64_t numResultSubTileElts = 4;
275
276 // Single-dimensional vector types for the operands of the ArmSVE dialect
277 // op.
278 auto flatLhsType =
279 VectorType::get(/*shape=*/numOperandSubTileElts, operandEltType,
280 /*scalableDims=*/{true});
281 auto flatRhsType =
282 VectorType::get(/*shape=*/numOperandSubTileElts, operandEltType,
283 /*scalableDims=*/{true});
284 auto flatAccType =
285 VectorType::get(/*shape=*/numResultSubTileElts, resultEltType,
286 /*scalableDims=*/{true});
287
288 // Single-dimension vector type for the entire RHS tile.
289
290 auto flatRhsTileType = VectorType::get(/*shape=*/k * n, operandEltType,
291 /*scalableDims=*/{true});
292
293 // Vector type having the same number of elements as a row in the
294 // accumulator/output tile and the same element type.
295 auto accRowTy = VectorType::get(/*shape=*/n, resultEltType,
296 /*scalableDims=*/{true});
297
298 // Vector type having twice the number of elements as a row in the
299 // accumulator/output tile the same element type.
300 auto accRowX2Ty = VectorType::get(/*shape=*/2 * n, resultEltType,
301 /*scalableDims=*/{true});
302 // Vector type having half the number of elements as a row in the
303 // accumulator/output tile and an integer element type with twice the bit
304 // width.
305 auto accRow64Ty = VectorType::get(/*shape=*/n / 2, rewriter.getI64Type(),
306 /*scalableDims=*/{true});
307 // Vector type having the same the number of elements as a row in the
308 // accumulator/output tile and an integer element type with twice the bit
309 // width.
310 auto accRowX264Ty = VectorType::get(/*shape=*/n, rewriter.getI64Type(),
311 /*scalableDims=*/{true});
312
313 Location loc = op.getLoc();
314
315 // Extract LHS sub-tiles with logical shape <2xK>.
316 SmallVector<Value> lhsTile;
317 for (int64_t i = 0; i < m; i += 2) {
318 // Extract two consecutive rows of the LHS tile.
319 auto r0 =
320 vector::ExtractOp::create(rewriter, loc, lhs, ArrayRef<int64_t>{i});
321 auto r1 =
322 vector::ExtractOp::create(rewriter, loc, lhs, ArrayRef<int64_t>{i + 1});
323 // Concatenate to obtain a 2 x K x <input-type> flattened sub-tile.
324 SmallVector<int64_t> shuffleIdx(2 * k);
325 std::iota(shuffleIdx.begin(), shuffleIdx.end(), 0);
326 auto t = vector::ShuffleOp::create(rewriter, loc, r0, r1, shuffleIdx);
327 // Turn it into a scalable vector.
328 auto s = vector::ScalableInsertOp::create(
329 rewriter, loc, t, ub::PoisonOp::create(rewriter, loc, flatLhsType), 0);
330 // Replicate the sub-tile VSCALE times to fill the entire vector.
331 auto r = arm_sve::DupQLaneOp::create(rewriter, loc, s, 0);
332 lhsTile.push_back(r);
333 }
334
335 // "Flatten" the RHS tile from <[N]xK> to <[N*K]>.
336 auto rhs = vector::ShapeCastOp::create(rewriter, this->rhs.getLoc(),
337 flatRhsTileType, this->rhs);
338
339 // Extract the RHS sub-tiles with logical shape <Kx[2]>.
340 SmallVector<Value> rhsTile;
341 for (int64_t j = 0; j < n; j += 2)
342 rhsTile.push_back(vector::ScalableExtractOp::create(
343 rewriter, loc, flatRhsType, rhs, j * k));
344
345 // Extract and pack the ACC sub-tiles.
346 SmallVector<Value> accTile;
347 for (int64_t i = 0; i < m; i += 2) {
348 // Extract two consecutive rows of the accumulator tile.
349 auto r0 = vector::ExtractOp::create(rewriter, loc, op.getAcc(),
351 auto r1 = vector::ExtractOp::create(rewriter, loc, op.getAcc(),
352 ArrayRef<int64_t>{i + 1});
353 Value accTileVec;
354 if (swapOperands) {
355 // We are performing the operation with swapped LHS and RHS we need to
356 // transpose each individual 2x2 tile of the accumulator and (later) the
357 // final result.
358 accTileVec = vector::InterleaveOp::create(rewriter, loc, r0, r1);
359 } else {
360 // Bitcast accumulator rows to double-width integer elements, so
361 // subsequent interleave/deinterleave work on pairs of elements.
362 auto r0I64 = vector::BitCastOp::create(rewriter, loc, accRow64Ty, r0);
363 auto r1I64 = vector::BitCastOp::create(rewriter, loc, accRow64Ty, r1);
364
365 // Interleave the rows, effectively flattening each 2x2 tile into 4
366 // consecutive elements.
367 auto intrI64 = vector::InterleaveOp::create(rewriter, loc, r0I64, r1I64);
368
369 // Bitcast back to original element type.
370 accTileVec =
371 vector::BitCastOp::create(rewriter, loc, accRowX2Ty, intrI64);
372 }
373 // Extract ACC sub-tiles.
374 for (int64_t j = 0; j < n; j += 2)
375 accTile.push_back(vector::ScalableExtractOp::create(
376 rewriter, loc, flatAccType, accTileVec, j * 2));
377 }
378
379 // Emit sub-tile matrix multiplications.
380 SmallVector<Value> outTile;
381 for (int64_t i = 0; i < m / 2; ++i)
382 for (int64_t j = 0; j < n / 2; ++j) {
383 Value mmla = createMMLA(rewriter, loc, accTile[i * n / 2 + j], lhsTile[i],
384 rhsTile[j]);
385 outTile.push_back(mmla);
386 }
387
388 // Unpack the OUT sub-tiles and insert into the result.
389 Value result = ub::PoisonOp::create(rewriter, loc, op.getResultType());
390 for (int64_t i = 0; i < m / 2; ++i) {
391 // Collect a number of sub-tiles in a row.
392 Value row = ub::PoisonOp::create(rewriter, loc, accRowX2Ty);
393 for (int64_t j = 0; j < n / 2; ++j)
394 row = vector::ScalableInsertOp::create(
395 rewriter, loc, outTile[i * n / 2 + j], row, j * 4);
396
397 // Unpack the row to obtain two rows of the output. If we have the out
398 // sub-tiles transposed we obtain two consecutive output rows by
399 // separating even and odd elements, i.e. a simple deinterleave.
400 // Otherwise, the interleave is by pairs.
401 Value out0, out1;
402 if (swapOperands) {
403 auto tmp = vector::DeinterleaveOp::create(rewriter, loc, row);
404 out0 = tmp.getRes1();
405 out1 = tmp.getRes2();
406 } else {
407 // Deinterleave by pairs.
408 auto row64 = vector::BitCastOp::create(rewriter, loc, accRowX264Ty, row);
409 auto deintr64 = vector::DeinterleaveOp::create(rewriter, loc, row64);
410
411 // Bitcast back into original element type and insert into the result.
412 out0 = vector::BitCastOp::create(rewriter, loc, accRowTy,
413 deintr64.getRes1());
414 out1 = vector::BitCastOp::create(rewriter, loc, accRowTy,
415 deintr64.getRes2());
416 }
417 result = vector::InsertOp::create(rewriter, loc, out0, result, i * 2);
418 result = vector::InsertOp::create(rewriter, loc, out1, result, i * 2 + 1);
419 }
420
421 return result;
422}
423
424class VectorContractRewriterI8MM : public VectorContractRewriter {
425public:
426 // Check the specific preconditions for the integer case. Initialise
427 // parametrisation types and dimensions.
428 LogicalResult matchAndInit(vector::ContractionOp op,
429 PatternRewriter &rewriter) {
430 if (failed(match(op, rewriter)))
431 return failure();
432
433 VectorType lhsType = op.getLhsType();
434 VectorType rhsType = op.getRhsType();
435
436 m = lhsType.getDimSize(0);
437 n = rhsType.getDimSize(0);
438 k = rhsType.getDimSize(1);
439
440 // Check the operands have the expected shape:
441 // * for LHS: fixed vector MxK
442 // * for RHS: scalable vector [N]xK
443 // * K == 8
444 // * M and N even and at least 2
445 if (lhsType.isScalable() || !rhsType.getScalableDims()[0] ||
446 rhsType.getScalableDims()[1] || lhsType.getDimSize(1) != k || k != 8 ||
447 m < 2 || m % 2 != 0 || n < 2 || n % 2 != 0 ||
448 !rhsType.getScalableDims()[0])
449 return rewriter.notifyMatchFailure(op, "non-matching operand shape");
450
451 // Check the output is a vector of i32 elements.
452 auto outTy = dyn_cast<VectorType>(op.getResultType());
453 if (!outTy || outTy.getElementType() != rewriter.getI32Type())
454 return rewriter.notifyMatchFailure(op,
455 "output type is not a vector of i32");
456
457 // Check inputs are sign-/zero- extensions from i8 to i32. Get the values
458 // before the extension. All four signed/unsigned combinations for input
459 // operands are supported, but they are lowered to different operations.
460 // Determine which is the appropriate operation to lower to.
461 mmlaOp = MMLA::SignedInt;
462 swapOperands = false;
463 auto maybeLhs = getExtOperand<arith::ExtSIOp>(op.getLhs());
464 if (!maybeLhs) {
465 mmlaOp = MMLA::UnsignedInt;
466 maybeLhs = getExtOperand<arith::ExtUIOp>(op.getLhs());
467 }
468 if (!maybeLhs)
469 return rewriter.notifyMatchFailure(
470 op, "LHS is not a sign- or zero- extended i8");
471
472 auto maybeRhs = getExtOperand<arith::ExtSIOp>(op.getRhs());
473 if (maybeRhs) {
474 if (mmlaOp == MMLA::UnsignedInt)
475 mmlaOp = MMLA::MixedInt;
476 } else {
477 if (mmlaOp == MMLA::SignedInt) {
478 mmlaOp = MMLA::MixedInt;
479 swapOperands = true;
480 }
481 maybeRhs = getExtOperand<arith::ExtUIOp>(op.getRhs());
482 }
483 if (!maybeRhs)
484 return rewriter.notifyMatchFailure(
485 op, "RHS is not a sign- or zero- extended i8");
486
487 // Initialise algorithm parameters.
488 lhs = *maybeLhs;
489 rhs = *maybeRhs;
490 acc = op.getAcc();
491
492 return success();
493 }
494};
495
496class VectorContractRewriterBfloat : public VectorContractRewriter {
497public:
498 // Check the specific preconditions for the bfloat16 case. Initialise
499 // parametrisation types and dimensions.
500 LogicalResult matchAndInit(vector::ContractionOp op,
501 PatternRewriter &rewriter) {
502 if (failed(match(op, rewriter)))
503 return failure();
504
505 VectorType lhsType = op.getLhsType();
506 VectorType rhsType = op.getRhsType();
507
508 m = lhsType.getDimSize(0);
509 n = rhsType.getDimSize(0);
510 k = rhsType.getDimSize(1);
511
512 // Check the operands have the expected shape:
513 // * for LHS: fixed vector MxK
514 // * for RHS: scalable vector [N]xK
515 // * K == 4
516 // * M and N even and at least 2
517 if (lhsType.isScalable() || !rhsType.getScalableDims()[0] ||
518 rhsType.getScalableDims()[1] || lhsType.getDimSize(1) != k || k != 4 ||
519 m < 2 || m % 2 != 0 || n < 2 || n % 2 != 0 ||
520 !rhsType.getScalableDims()[0])
521 return rewriter.notifyMatchFailure(op, "non-matching operand shape");
522
523 // Check the output is a vector of Float32 elements.
524 auto outTy = dyn_cast<VectorType>(op.getResultType());
525 if (!outTy || outTy.getElementType() != rewriter.getF32Type())
526 return rewriter.notifyMatchFailure(op,
527 "output type is not a vector of f32");
528
529 // Check the inputs are vectors of BFloat16 elements.
530 if (lhsType.getElementType() != rewriter.getBF16Type())
531 return rewriter.notifyMatchFailure(op,
532 "input type is not a vector of bf16");
533
534 // Initialise algorithm parameters.
535 mmlaOp = MMLA::Bfloat;
536 swapOperands = false;
537 lhs = op.getLhs();
538 rhs = op.getRhs();
539 acc = op.getAcc();
540
541 return success();
542 }
543};
544
545class LowerContractionToSVEI8MMPattern
546 : public OpRewritePattern<vector::ContractionOp> {
547public:
549 LogicalResult matchAndRewrite(vector::ContractionOp op,
550 PatternRewriter &rewriter) const override {
551
552 // Match i8xi8 -> i32 matrix multiply and accumulate.
553 VectorContractRewriterI8MM vcr;
554 if (failed(vcr.matchAndInit(op, rewriter)))
555 return failure();
556
557 Value result = vcr.lower(op, rewriter);
558 rewriter.replaceOp(op, result);
559
560 return success();
561 }
562};
563
564class LowerContractionToSVEBFMMLAPattern
565 : public OpRewritePattern<vector::ContractionOp> {
566public:
568 LogicalResult matchAndRewrite(vector::ContractionOp op,
569 PatternRewriter &rewriter) const override {
570
571 // Match bf16xbf16 -> f32 matrix multiply and accumulate.
572 VectorContractRewriterBfloat vcr;
573 if (failed(vcr.matchAndInit(op, rewriter)))
574 return failure();
575
576 Value result = vcr.lower(op, rewriter);
577 rewriter.replaceOp(op, result);
578
579 return success();
580 }
581};
582
583} // namespace
584
586 RewritePatternSet &patterns) {
587 MLIRContext *context = patterns.getContext();
588 patterns.add<LowerContractionToSVEI8MMPattern>(context, /*benefit=*/2);
589}
590
592 RewritePatternSet &patterns) {
593 MLIRContext *context = patterns.getContext();
594 patterns.add<LowerContractionToSVEBFMMLAPattern>(context, /*benefit=*/2);
595}
return success()
lhs
static AffineMap getMultiDimMapWithTargets(unsigned numDims, ArrayRef< unsigned > targets, MLIRContext *context)
Returns an affine map with numDims input dimensions and results specified by targets.
FloatType getF32Type()
Definition Builders.cpp:51
IntegerType getI64Type()
Definition Builders.cpp:73
IntegerType getI32Type()
Definition Builders.cpp:71
FloatType getBF16Type()
Definition Builders.cpp:45
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This provides public APIs that all operations should have.
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,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
void populateLowerContractionToSVEBFMMLAPatterns(RewritePatternSet &patterns)
void populateLowerContractionToSVEI8MMPatterns(RewritePatternSet &patterns)
Include the generated interface declarations.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.