MLIR 24.0.0git
ConvertVectorToLLVM.cpp
Go to the documentation of this file.
1//===- VectorToLLVM.cpp - Conversion from Vector to the LLVM dialect ------===//
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
10
32#include "llvm/ADT/APFloat.h"
33#include "llvm/IR/LLVMContext.h"
34#include "llvm/Support/Casting.h"
35
36#include <optional>
37
38using namespace mlir;
39using namespace mlir::vector;
40
41// Helper that picks the proper sequence for inserting.
42static Value insertOne(ConversionPatternRewriter &rewriter,
43 const LLVMTypeConverter &typeConverter, Location loc,
44 Value val1, Value val2, Type llvmType, int64_t rank,
45 int64_t pos) {
46 assert(rank > 0 && "0-D vector corner case should have been handled already");
47 if (rank == 1) {
48 Type idxType = typeConverter.convertType(rewriter.getIndexType());
49 auto constant = LLVM::ConstantOp::create(
50 rewriter, loc, idxType, rewriter.getIntegerAttr(idxType, pos));
51 return LLVM::InsertElementOp::create(rewriter, loc, llvmType, val1, val2,
52 constant);
53 }
54 return LLVM::InsertValueOp::create(rewriter, loc, val1, val2, pos);
55}
56
57// Helper that picks the proper sequence for extracting.
58static Value extractOne(ConversionPatternRewriter &rewriter,
59 const LLVMTypeConverter &typeConverter, Location loc,
60 Value val, Type llvmType, int64_t rank, int64_t pos) {
61 if (rank <= 1) {
62 Type idxType = typeConverter.convertType(rewriter.getIndexType());
63 auto constant = LLVM::ConstantOp::create(
64 rewriter, loc, idxType, rewriter.getIntegerAttr(idxType, pos));
65 return LLVM::ExtractElementOp::create(rewriter, loc, llvmType, val,
66 constant);
67 }
68 return LLVM::ExtractValueOp::create(rewriter, loc, val, pos);
69}
70
71// Helper that returns data layout alignment of a vector.
72LogicalResult getVectorAlignment(const LLVMTypeConverter &typeConverter,
73 VectorType vectorType, unsigned &align) {
74 Type convertedVectorTy = typeConverter.convertType(vectorType);
75 if (!convertedVectorTy)
76 return failure();
77
78 llvm::LLVMContext llvmContext;
79 align = LLVM::TypeToLLVMIRTranslator(llvmContext)
80 .getPreferredAlignment(convertedVectorTy,
81 typeConverter.getDataLayout());
82
83 return success();
84}
85
86// Helper that returns data layout alignment of a memref.
87LogicalResult getMemRefAlignment(const LLVMTypeConverter &typeConverter,
88 MemRefType memrefType, unsigned &align) {
89 Type elementTy = typeConverter.convertType(memrefType.getElementType());
90 if (!elementTy)
91 return failure();
92
93 // TODO: this should use the MLIR data layout when it becomes available and
94 // stop depending on translation.
95 llvm::LLVMContext llvmContext;
96 align = LLVM::TypeToLLVMIRTranslator(llvmContext)
97 .getPreferredAlignment(elementTy, typeConverter.getDataLayout());
98 return success();
99}
100
101// Helper to resolve the alignment for vector load/store, gather and scatter
102// ops. If useVectorAlignment is true, get the preferred alignment for the
103// vector type in the operation. This option is used for hardware backends with
104// vectorization. Otherwise, use the preferred alignment of the element type of
105// the memref. Note that if you choose to use vector alignment, the shape of the
106// vector type must be resolved before the ConvertVectorToLLVM pass is run.
107LogicalResult getVectorToLLVMAlignment(const LLVMTypeConverter &typeConverter,
108 VectorType vectorType,
109 MemRefType memrefType, unsigned &align,
110 bool useVectorAlignment) {
111 if (useVectorAlignment) {
112 if (failed(getVectorAlignment(typeConverter, vectorType, align))) {
113 return failure();
114 }
115 } else {
116 if (failed(getMemRefAlignment(typeConverter, memrefType, align))) {
117 return failure();
118 }
119 }
120 return success();
121}
122
123// Check if the last stride is non-unit and has a valid memory space.
124static LogicalResult isMemRefTypeSupported(MemRefType memRefType,
125 const LLVMTypeConverter &converter) {
126 if (!memRefType.isLastDimUnitStride())
127 return failure();
128 if (failed(converter.getMemRefAddressSpace(memRefType)))
129 return failure();
130 return success();
131}
132
133// Add an index vector component to a base pointer.
134static Value getIndexedPtrs(ConversionPatternRewriter &rewriter, Location loc,
135 const LLVMTypeConverter &typeConverter,
136 MemRefType memRefType, Value llvmMemref, Value base,
137 Value index, VectorType vectorType) {
138 assert(succeeded(isMemRefTypeSupported(memRefType, typeConverter)) &&
139 "unsupported memref type");
140 assert(vectorType.getRank() == 1 && "expected a 1-d vector type");
141 auto pType = MemRefDescriptor(llvmMemref).getElementPtrType();
142 auto ptrsType =
143 LLVM::getVectorType(pType, vectorType.getDimSize(0),
144 /*isScalable=*/vectorType.getScalableDims()[0]);
145 return LLVM::GEPOp::create(
146 rewriter, loc, ptrsType,
147 typeConverter.convertType(memRefType.getElementType()), base, index);
148}
149
150/// Convert `foldResult` into a Value. Integer attribute is converted to
151/// an LLVM constant op.
153 OpFoldResult foldResult) {
154 if (auto attr = dyn_cast<Attribute>(foldResult)) {
155 auto intAttr = cast<IntegerAttr>(attr);
156 return LLVM::ConstantOp::create(builder, loc, intAttr).getResult();
157 }
158
159 return cast<Value>(foldResult);
160}
161
162namespace {
163
164/// Trivial Vector to LLVM conversions
165using VectorScaleOpConversion =
167
168/// Conversion pattern for a vector.bitcast.
169class VectorBitCastOpConversion
170 : public ConvertOpToLLVMPattern<vector::BitCastOp> {
171public:
172 using ConvertOpToLLVMPattern<vector::BitCastOp>::ConvertOpToLLVMPattern;
173
174 LogicalResult
175 matchAndRewrite(vector::BitCastOp bitCastOp, OpAdaptor adaptor,
176 ConversionPatternRewriter &rewriter) const override {
177 // Only 0-D and 1-D vectors can be lowered to LLVM.
178 VectorType resultTy = bitCastOp.getResultVectorType();
179 if (resultTy.getRank() > 1)
180 return failure();
181 Type newResultTy = typeConverter->convertType(resultTy);
182 rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(bitCastOp, newResultTy,
183 adaptor.getOperands()[0]);
184 return success();
185 }
186};
187
188/// Overloaded utility that replaces a vector.load, vector.store,
189/// vector.maskedload and vector.maskedstore with their respective LLVM
190/// couterparts.
191static void replaceLoadOrStoreOp(vector::LoadOp loadOp,
192 vector::LoadOpAdaptor adaptor,
193 VectorType vectorTy, Value ptr, unsigned align,
194 ConversionPatternRewriter &rewriter) {
195 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(loadOp, vectorTy, ptr, align,
196 /*volatile_=*/false,
197 loadOp.getNontemporal());
198}
199
200static void replaceLoadOrStoreOp(vector::MaskedLoadOp loadOp,
201 vector::MaskedLoadOpAdaptor adaptor,
202 VectorType vectorTy, Value ptr, unsigned align,
203 ConversionPatternRewriter &rewriter) {
204 rewriter.replaceOpWithNewOp<LLVM::MaskedLoadOp>(
205 loadOp, vectorTy, ptr, adaptor.getMask(), adaptor.getPassThru(), align);
206}
207
208static void replaceLoadOrStoreOp(vector::StoreOp storeOp,
209 vector::StoreOpAdaptor adaptor,
210 VectorType vectorTy, Value ptr, unsigned align,
211 ConversionPatternRewriter &rewriter) {
212 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(storeOp, adaptor.getValueToStore(),
213 ptr, align, /*volatile_=*/false,
214 storeOp.getNontemporal());
215}
216
217static void replaceLoadOrStoreOp(vector::MaskedStoreOp storeOp,
218 vector::MaskedStoreOpAdaptor adaptor,
219 VectorType vectorTy, Value ptr, unsigned align,
220 ConversionPatternRewriter &rewriter) {
221 rewriter.replaceOpWithNewOp<LLVM::MaskedStoreOp>(
222 storeOp, adaptor.getValueToStore(), ptr, adaptor.getMask(), align);
223}
224
225/// Conversion pattern for a vector.load, vector.store, vector.maskedload, and
226/// vector.maskedstore.
227template <class LoadOrStoreOp>
228class VectorLoadStoreConversion : public ConvertOpToLLVMPattern<LoadOrStoreOp> {
229public:
230 explicit VectorLoadStoreConversion(const LLVMTypeConverter &typeConv,
231 bool useVectorAlign,
232 bool enableGEPInboundsNuw)
233 : ConvertOpToLLVMPattern<LoadOrStoreOp>(typeConv),
234 useVectorAlignment(useVectorAlign),
235 enableGEPInboundsNuw(enableGEPInboundsNuw) {}
236
237 LogicalResult
238 matchAndRewrite(LoadOrStoreOp loadOrStoreOp,
239 typename LoadOrStoreOp::Adaptor adaptor,
240 ConversionPatternRewriter &rewriter) const override {
241 // Only 1-D vectors can be lowered to LLVM.
242 VectorType vectorTy = loadOrStoreOp.getVectorType();
243 if (vectorTy.getRank() > 1)
244 return failure();
245
246 auto loc = loadOrStoreOp->getLoc();
247 MemRefType memRefTy = loadOrStoreOp.getMemRefType();
248
249 // Resolve alignment.
250 // Explicit alignment takes priority over use-vector-alignment.
251 unsigned align = loadOrStoreOp.getAlignment().value_or(0);
252 if (!align &&
253 failed(getVectorToLLVMAlignment(*this->getTypeConverter(), vectorTy,
254 memRefTy, align, useVectorAlignment)))
255 return rewriter.notifyMatchFailure(loadOrStoreOp,
256 "could not resolve alignment");
257
258 // Resolve address.
259 // When --enable-gep-inbounds-nuw is set, emit inbounds|nuw on the GEP so
260 // LLVM can apply no-wrap optimizations on the index arithmetic. This
261 // assumes 0 <= idx < dim_size and non-negative strides; the caller is
262 // responsible for ensuring those conditions hold. Masked variants are
263 // designed for near-boundary access and never receive these flags.
264 LLVM::GEPNoWrapFlags noWrapFlags = LLVM::GEPNoWrapFlags::none;
265 if constexpr (std::is_same_v<LoadOrStoreOp, vector::LoadOp> ||
266 std::is_same_v<LoadOrStoreOp, vector::StoreOp>) {
267 // The verifier (verifyLoadStoreMemRefLayout) guarantees that the
268 // trailing (most minor) stride of the memref is 1. Assert to make
269 // the invariant explicit in the lowering code.
270 auto [strides, offset] = memRefTy.getStridesAndOffset();
271 assert((strides.empty() || strides.back() == 1) &&
272 "vector.load/store requires unit trailing memref stride");
273 if (enableGEPInboundsNuw) {
274 noWrapFlags = noWrapFlags | LLVM::GEPNoWrapFlags::inbounds;
275
276 // `nuw` additionally requires non-negative strides.
277 assert(
278 !(memref::hasNegativeStaticStride(memRefTy)) &&
279 "Invalid MemRef type - should have been rejected by Op verifier.");
280 noWrapFlags = noWrapFlags | LLVM::GEPNoWrapFlags::nuw;
281 }
282 }
283 auto vtype = cast<VectorType>(
284 this->typeConverter->convertType(loadOrStoreOp.getVectorType()));
285 Value dataPtr =
286 this->getStridedElementPtr(rewriter, loc, memRefTy, adaptor.getBase(),
287 adaptor.getIndices(), noWrapFlags);
288 replaceLoadOrStoreOp(loadOrStoreOp, adaptor, vtype, dataPtr, align,
289 rewriter);
290 return success();
291 }
292
293private:
294 // If true, use the preferred alignment of the vector type.
295 // If false, use the preferred alignment of the element type
296 // of the memref. This flag is intended for use with hardware
297 // backends that require alignment of vector operations.
298 const bool useVectorAlignment;
299 const bool enableGEPInboundsNuw;
300};
301
302/// Conversion pattern for a vector.gather.
303class VectorGatherOpConversion
304 : public ConvertOpToLLVMPattern<vector::GatherOp> {
305public:
306 explicit VectorGatherOpConversion(const LLVMTypeConverter &typeConv,
307 bool useVectorAlign)
308 : ConvertOpToLLVMPattern<vector::GatherOp>(typeConv),
309 useVectorAlignment(useVectorAlign) {}
310 using ConvertOpToLLVMPattern<vector::GatherOp>::ConvertOpToLLVMPattern;
311
312 LogicalResult
313 matchAndRewrite(vector::GatherOp gather, OpAdaptor adaptor,
314 ConversionPatternRewriter &rewriter) const override {
315 Location loc = gather->getLoc();
316 MemRefType memRefType = dyn_cast<MemRefType>(gather.getBaseType());
317 assert(memRefType && "The base should be bufferized");
318
319 // TODO: Add support for strided MemRef.
320 if (failed(isMemRefTypeSupported(memRefType, *this->getTypeConverter())))
321 return rewriter.notifyMatchFailure(gather, "memref type not supported");
322
323 VectorType vType = gather.getVectorType();
324 if (vType.getRank() > 1) {
325 return rewriter.notifyMatchFailure(
326 gather, "only 1-D vectors can be lowered to LLVM");
327 }
328
329 // Resolve alignment.
330 // Explicit alignment takes priority over use-vector-alignment.
331 unsigned align = gather.getAlignment().value_or(0);
332 if (!align &&
333 failed(getVectorToLLVMAlignment(*this->getTypeConverter(), vType,
334 memRefType, align, useVectorAlignment)))
335 return rewriter.notifyMatchFailure(gather, "could not resolve alignment");
336
337 // Resolve address.
338 Value ptr = getStridedElementPtr(rewriter, loc, memRefType,
339 adaptor.getBase(), adaptor.getOffsets());
340 Value base = adaptor.getBase();
341 Value ptrs =
342 getIndexedPtrs(rewriter, loc, *this->getTypeConverter(), memRefType,
343 base, ptr, adaptor.getIndices(), vType);
344
345 // Replace with the gather intrinsic.
346 rewriter.replaceOpWithNewOp<LLVM::masked_gather>(
347 gather, typeConverter->convertType(vType), ptrs, adaptor.getMask(),
348 adaptor.getPassThru(), align);
349 return success();
350 }
351
352private:
353 // If true, use the preferred alignment of the vector type.
354 // If false, use the preferred alignment of the element type
355 // of the memref. This flag is intended for use with hardware
356 // backends that require alignment of vector operations.
357 const bool useVectorAlignment;
358};
359
360/// Conversion pattern for a vector.scatter.
361class VectorScatterOpConversion
362 : public ConvertOpToLLVMPattern<vector::ScatterOp> {
363public:
364 explicit VectorScatterOpConversion(const LLVMTypeConverter &typeConv,
365 bool useVectorAlign)
366 : ConvertOpToLLVMPattern<vector::ScatterOp>(typeConv),
367 useVectorAlignment(useVectorAlign) {}
368
369 using ConvertOpToLLVMPattern<vector::ScatterOp>::ConvertOpToLLVMPattern;
370
371 LogicalResult
372 matchAndRewrite(vector::ScatterOp scatter, OpAdaptor adaptor,
373 ConversionPatternRewriter &rewriter) const override {
374 auto loc = scatter->getLoc();
375 auto memRefType = dyn_cast<MemRefType>(scatter.getBaseType());
376 assert(memRefType && "The base should be bufferized");
377
378 // TODO: Add support for strided MemRef.
379 if (failed(isMemRefTypeSupported(memRefType, *this->getTypeConverter())))
380 return rewriter.notifyMatchFailure(scatter, "memref type not supported");
381
382 VectorType vType = scatter.getVectorType();
383 if (vType.getRank() > 1) {
384 return rewriter.notifyMatchFailure(
385 scatter, "only 1-D vectors can be lowered to LLVM");
386 }
387
388 // Resolve alignment.
389 // Explicit alignment takes priority over use-vector-alignment.
390 unsigned align = scatter.getAlignment().value_or(0);
391 if (!align &&
392 failed(getVectorToLLVMAlignment(*this->getTypeConverter(), vType,
393 memRefType, align, useVectorAlignment)))
394 return rewriter.notifyMatchFailure(scatter,
395 "could not resolve alignment");
396
397 // Resolve address.
398 Value ptr = getStridedElementPtr(rewriter, loc, memRefType,
399 adaptor.getBase(), adaptor.getOffsets());
400 Value ptrs =
401 getIndexedPtrs(rewriter, loc, *this->getTypeConverter(), memRefType,
402 adaptor.getBase(), ptr, adaptor.getIndices(), vType);
403
404 // Replace with the scatter intrinsic.
405 rewriter.replaceOpWithNewOp<LLVM::masked_scatter>(
406 scatter, adaptor.getValueToStore(), ptrs, adaptor.getMask(), align);
407 return success();
408 }
409
410private:
411 // If true, use the preferred alignment of the vector type.
412 // If false, use the preferred alignment of the element type
413 // of the memref. This flag is intended for use with hardware
414 // backends that require alignment of vector operations.
415 const bool useVectorAlignment;
416};
417
418/// Conversion pattern for a vector.expandload.
419class VectorExpandLoadOpConversion
420 : public ConvertOpToLLVMPattern<vector::ExpandLoadOp> {
421public:
422 using ConvertOpToLLVMPattern<vector::ExpandLoadOp>::ConvertOpToLLVMPattern;
423
424 LogicalResult
425 matchAndRewrite(vector::ExpandLoadOp expand, OpAdaptor adaptor,
426 ConversionPatternRewriter &rewriter) const override {
427 auto loc = expand->getLoc();
428 MemRefType memRefType = expand.getMemRefType();
429
430 // Resolve address.
431 auto vtype = typeConverter->convertType(expand.getVectorType());
432 Value ptr = getStridedElementPtr(rewriter, loc, memRefType,
433 adaptor.getBase(), adaptor.getIndices());
434
435 // From:
436 // https://llvm.org/docs/LangRef.html#llvm-masked-expandload-intrinsics
437 // The pointer alignment defaults to 1.
438 uint64_t alignment = expand.getAlignment().value_or(1);
439
440 rewriter.replaceOpWithNewOp<LLVM::masked_expandload>(
441 expand, vtype, ptr, adaptor.getMask(), adaptor.getPassThru(),
442 alignment);
443 return success();
444 }
445};
446
447/// Conversion pattern for a vector.compressstore.
448class VectorCompressStoreOpConversion
449 : public ConvertOpToLLVMPattern<vector::CompressStoreOp> {
450public:
451 using ConvertOpToLLVMPattern<vector::CompressStoreOp>::ConvertOpToLLVMPattern;
452
453 LogicalResult
454 matchAndRewrite(vector::CompressStoreOp compress, OpAdaptor adaptor,
455 ConversionPatternRewriter &rewriter) const override {
456 auto loc = compress->getLoc();
457 MemRefType memRefType = compress.getMemRefType();
458
459 // Resolve address.
460 Value ptr = getStridedElementPtr(rewriter, loc, memRefType,
461 adaptor.getBase(), adaptor.getIndices());
462
463 // From:
464 // https://llvm.org/docs/LangRef.html#llvm-masked-compressstore-intrinsics
465 // The pointer alignment defaults to 1.
466 uint64_t alignment = compress.getAlignment().value_or(1);
467
468 rewriter.replaceOpWithNewOp<LLVM::masked_compressstore>(
469 compress, adaptor.getValueToStore(), ptr, adaptor.getMask(), alignment);
470 return success();
471 }
472};
473
474/// Reduction neutral classes for overloading.
475class ReductionNeutralZero {};
476class ReductionNeutralIntOne {};
477class ReductionNeutralFPOne {};
478class ReductionNeutralAllOnes {};
479class ReductionNeutralSIntMin {};
480class ReductionNeutralUIntMin {};
481class ReductionNeutralSIntMax {};
482class ReductionNeutralUIntMax {};
483class ReductionNeutralFPQNaN {};
484class ReductionNeutralFPNegQNaN {};
485class ReductionNeutralFPNegInf {};
486class ReductionNeutralFPPosInf {};
487class ReductionNeutralFPLowestFinite {};
488class ReductionNeutralFPLargestFinite {};
489
490/// Create the reduction neutral zero value.
491static Value createReductionNeutralValue(ReductionNeutralZero neutral,
492 ConversionPatternRewriter &rewriter,
493 Location loc, Type llvmType) {
494 return LLVM::ConstantOp::create(rewriter, loc, llvmType,
495 rewriter.getZeroAttr(llvmType));
496}
497
498/// Create the reduction neutral integer one value.
499static Value createReductionNeutralValue(ReductionNeutralIntOne neutral,
500 ConversionPatternRewriter &rewriter,
501 Location loc, Type llvmType) {
502 return LLVM::ConstantOp::create(rewriter, loc, llvmType,
503 rewriter.getIntegerAttr(llvmType, 1));
504}
505
506/// Create the reduction neutral fp one value.
507static Value createReductionNeutralValue(ReductionNeutralFPOne neutral,
508 ConversionPatternRewriter &rewriter,
509 Location loc, Type llvmType) {
510 return LLVM::ConstantOp::create(rewriter, loc, llvmType,
511 rewriter.getFloatAttr(llvmType, 1.0));
512}
513
514/// Create the reduction neutral all-ones value.
515static Value createReductionNeutralValue(ReductionNeutralAllOnes neutral,
516 ConversionPatternRewriter &rewriter,
517 Location loc, Type llvmType) {
518 return LLVM::ConstantOp::create(
519 rewriter, loc, llvmType,
520 rewriter.getIntegerAttr(
521 llvmType, llvm::APInt::getAllOnes(llvmType.getIntOrFloatBitWidth())));
522}
523
524/// Create the reduction neutral signed int minimum value.
525static Value createReductionNeutralValue(ReductionNeutralSIntMin neutral,
526 ConversionPatternRewriter &rewriter,
527 Location loc, Type llvmType) {
528 return LLVM::ConstantOp::create(
529 rewriter, loc, llvmType,
530 rewriter.getIntegerAttr(llvmType, llvm::APInt::getSignedMinValue(
531 llvmType.getIntOrFloatBitWidth())));
532}
533
534/// Create the reduction neutral unsigned int minimum value.
535static Value createReductionNeutralValue(ReductionNeutralUIntMin neutral,
536 ConversionPatternRewriter &rewriter,
537 Location loc, Type llvmType) {
538 return LLVM::ConstantOp::create(
539 rewriter, loc, llvmType,
540 rewriter.getIntegerAttr(llvmType, llvm::APInt::getMinValue(
541 llvmType.getIntOrFloatBitWidth())));
542}
543
544/// Create the reduction neutral signed int maximum value.
545static Value createReductionNeutralValue(ReductionNeutralSIntMax neutral,
546 ConversionPatternRewriter &rewriter,
547 Location loc, Type llvmType) {
548 return LLVM::ConstantOp::create(
549 rewriter, loc, llvmType,
550 rewriter.getIntegerAttr(llvmType, llvm::APInt::getSignedMaxValue(
551 llvmType.getIntOrFloatBitWidth())));
552}
553
554/// Create the reduction neutral unsigned int maximum value.
555static Value createReductionNeutralValue(ReductionNeutralUIntMax neutral,
556 ConversionPatternRewriter &rewriter,
557 Location loc, Type llvmType) {
558 return LLVM::ConstantOp::create(
559 rewriter, loc, llvmType,
560 rewriter.getIntegerAttr(llvmType, llvm::APInt::getMaxValue(
561 llvmType.getIntOrFloatBitWidth())));
562}
563
564/// Create the reduction neutral quiet NaN value.
565static Value createReductionNeutralValue(ReductionNeutralFPQNaN neutral,
566 ConversionPatternRewriter &rewriter,
567 Location loc, Type llvmType) {
568 auto floatType = cast<FloatType>(llvmType);
569 return LLVM::ConstantOp::create(
570 rewriter, loc, llvmType,
571 rewriter.getFloatAttr(
572 llvmType, llvm::APFloat::getQNaN(floatType.getFloatSemantics(),
573 /*Negative=*/false)));
574}
575
576/// Create the reduction neutral negative quiet NaN value.
577static Value createReductionNeutralValue(ReductionNeutralFPNegQNaN neutral,
578 ConversionPatternRewriter &rewriter,
579 Location loc, Type llvmType) {
580 auto floatType = cast<FloatType>(llvmType);
581 return LLVM::ConstantOp::create(
582 rewriter, loc, llvmType,
583 rewriter.getFloatAttr(
584 llvmType, llvm::APFloat::getQNaN(floatType.getFloatSemantics(),
585 /*Negative=*/true)));
586}
587
588/// Create the reduction neutral negative infinity value.
589static Value createReductionNeutralValue(ReductionNeutralFPNegInf neutral,
590 ConversionPatternRewriter &rewriter,
591 Location loc, Type llvmType) {
592 auto floatType = cast<FloatType>(llvmType);
593 return LLVM::ConstantOp::create(
594 rewriter, loc, llvmType,
595 rewriter.getFloatAttr(llvmType,
596 llvm::APFloat::getInf(floatType.getFloatSemantics(),
597 /*Negative=*/true)));
598}
599
600/// Create the reduction neutral positive infinity value.
601static Value createReductionNeutralValue(ReductionNeutralFPPosInf neutral,
602 ConversionPatternRewriter &rewriter,
603 Location loc, Type llvmType) {
604 auto floatType = cast<FloatType>(llvmType);
605 return LLVM::ConstantOp::create(
606 rewriter, loc, llvmType,
607 rewriter.getFloatAttr(llvmType,
608 llvm::APFloat::getInf(floatType.getFloatSemantics(),
609 /*Negative=*/false)));
610}
611
612/// Create the reduction neutral lowest finite value.
613static Value createReductionNeutralValue(ReductionNeutralFPLowestFinite neutral,
614 ConversionPatternRewriter &rewriter,
615 Location loc, Type llvmType) {
616 auto floatType = cast<FloatType>(llvmType);
617 return LLVM::ConstantOp::create(
618 rewriter, loc, llvmType,
619 rewriter.getFloatAttr(
620 llvmType, llvm::APFloat::getLargest(floatType.getFloatSemantics(),
621 /*Negative=*/true)));
622}
623
624/// Create the reduction neutral largest finite value.
625static Value
626createReductionNeutralValue(ReductionNeutralFPLargestFinite neutral,
627 ConversionPatternRewriter &rewriter, Location loc,
628 Type llvmType) {
629 auto floatType = cast<FloatType>(llvmType);
630 return LLVM::ConstantOp::create(
631 rewriter, loc, llvmType,
632 rewriter.getFloatAttr(
633 llvmType, llvm::APFloat::getLargest(floatType.getFloatSemantics(),
634 /*Negative=*/false)));
635}
636
637/// Returns `accumulator` if it has a valid value. Otherwise, creates and
638/// returns a new accumulator value using `ReductionNeutral`.
639template <class ReductionNeutral>
640static Value getOrCreateAccumulator(ConversionPatternRewriter &rewriter,
641 Location loc, Type llvmType,
642 Value accumulator) {
643 if (accumulator)
644 return accumulator;
645
646 return createReductionNeutralValue(ReductionNeutral(), rewriter, loc,
647 llvmType);
648}
649
650/// Creates a value with the 1-D vector shape provided in `llvmType`.
651/// This is used as effective vector length by some intrinsics supporting
652/// dynamic vector lengths at runtime.
653static Value createVectorLengthValue(ConversionPatternRewriter &rewriter,
654 Location loc, Type llvmType) {
655 VectorType vType = cast<VectorType>(llvmType);
656 auto vShape = vType.getShape();
657 assert(vShape.size() == 1 && "Unexpected multi-dim vector type");
658
659 Value baseVecLength = LLVM::ConstantOp::create(
660 rewriter, loc, rewriter.getI32Type(),
661 rewriter.getIntegerAttr(rewriter.getI32Type(), vShape[0]));
662
663 if (!vType.getScalableDims()[0])
664 return baseVecLength;
665
666 // For a scalable vector type, create and return `vScale * baseVecLength`.
667 Value vScale = vector::VectorScaleOp::create(rewriter, loc);
668 vScale =
669 arith::IndexCastOp::create(rewriter, loc, rewriter.getI32Type(), vScale);
670 Value scalableVecLength =
671 arith::MulIOp::create(rewriter, loc, baseVecLength, vScale);
672 return scalableVecLength;
673}
674
675/// Helper method to lower a `vector.reduction` op that performs an arithmetic
676/// operation like add,mul, etc.. `VectorOp` is the LLVM vector intrinsic to use
677/// and `ScalarOp` is the scalar operation used to add the accumulation value if
678/// non-null.
679template <class LLVMRedIntrinOp, class ScalarOp>
680static Value createIntegerReductionArithmeticOpLowering(
681 ConversionPatternRewriter &rewriter, Location loc, Type llvmType,
682 Value vectorOperand, Value accumulator) {
683
684 Value result =
685 LLVMRedIntrinOp::create(rewriter, loc, llvmType, vectorOperand);
686
687 if (accumulator)
688 result = ScalarOp::create(rewriter, loc, accumulator, result);
689 return result;
690}
691
692/// Helper method to lower a `vector.reduction` operation that performs
693/// a comparison operation like `min`/`max`. `VectorOp` is the LLVM vector
694/// intrinsic to use and `predicate` is the predicate to use to compare+combine
695/// the accumulator value if non-null.
696template <class LLVMRedIntrinOp>
697static Value createIntegerReductionComparisonOpLowering(
698 ConversionPatternRewriter &rewriter, Location loc, Type llvmType,
699 Value vectorOperand, Value accumulator, LLVM::ICmpPredicate predicate) {
700 Value result =
701 LLVMRedIntrinOp::create(rewriter, loc, llvmType, vectorOperand);
702 if (accumulator) {
703 Value cmp =
704 LLVM::ICmpOp::create(rewriter, loc, predicate, accumulator, result);
705 result = LLVM::SelectOp::create(rewriter, loc, cmp, accumulator, result);
706 }
707 return result;
708}
709
710namespace {
711template <typename Source>
712struct VectorToScalarMapper;
713template <>
714struct VectorToScalarMapper<LLVM::vector_reduce_fmaximum> {
715 using Type = LLVM::MaximumOp;
716};
717template <>
718struct VectorToScalarMapper<LLVM::vector_reduce_fminimum> {
719 using Type = LLVM::MinimumOp;
720};
721template <>
722struct VectorToScalarMapper<LLVM::vector_reduce_fmax> {
723 using Type = LLVM::MaxNumOp;
724};
725template <>
726struct VectorToScalarMapper<LLVM::vector_reduce_fmin> {
727 using Type = LLVM::MinNumOp;
728};
729} // namespace
730
731template <class LLVMRedIntrinOp>
732static Value createFPReductionComparisonOpLowering(
733 ConversionPatternRewriter &rewriter, Location loc, Type llvmType,
734 Value vectorOperand, Value accumulator, LLVM::FastmathFlagsAttr fmf) {
735 Value result =
736 LLVMRedIntrinOp::create(rewriter, loc, llvmType, vectorOperand, fmf);
737
738 if (accumulator) {
739 result = VectorToScalarMapper<LLVMRedIntrinOp>::Type::create(
740 rewriter, loc, result, accumulator);
741 }
742
743 return result;
744}
745
746template <class LLVMRedIntrinOp, class ReductionNeutral>
747static Value
748lowerReductionWithStartValue(ConversionPatternRewriter &rewriter, Location loc,
749 Type llvmType, Value vectorOperand,
750 Value accumulator, LLVM::FastmathFlagsAttr fmf) {
751 accumulator = getOrCreateAccumulator<ReductionNeutral>(rewriter, loc,
752 llvmType, accumulator);
753 return LLVMRedIntrinOp::create(rewriter, loc, llvmType,
754 /*start_value=*/accumulator, vectorOperand,
755 fmf);
756}
757
758/// Overloaded methods to lower a *predicated* reduction to an llvm intrinsic
759/// that requires a start value. This start value format spans across fp
760/// reductions without mask and all the masked reduction intrinsics.
761template <class LLVMVPRedIntrinOp, class ReductionNeutral>
762static Value
763lowerPredicatedReductionWithStartValue(ConversionPatternRewriter &rewriter,
764 Location loc, Type llvmType,
765 Value vectorOperand, Value accumulator) {
766 accumulator = getOrCreateAccumulator<ReductionNeutral>(rewriter, loc,
767 llvmType, accumulator);
768 return LLVMVPRedIntrinOp::create(rewriter, loc, llvmType,
769 /*startValue=*/accumulator, vectorOperand);
770}
771
772template <class LLVMVPRedIntrinOp, class ReductionNeutral>
773static Value lowerPredicatedReductionWithStartValue(
774 ConversionPatternRewriter &rewriter, Location loc, Type llvmType,
775 Value vectorOperand, Value accumulator, Value mask) {
776 accumulator = getOrCreateAccumulator<ReductionNeutral>(rewriter, loc,
777 llvmType, accumulator);
778 Value vectorLength =
779 createVectorLengthValue(rewriter, loc, vectorOperand.getType());
780 return LLVMVPRedIntrinOp::create(rewriter, loc, llvmType,
781 /*satrt_value=*/accumulator, vectorOperand,
782 mask, vectorLength);
783}
784
785template <class LLVMIntVPRedIntrinOp, class IntReductionNeutral,
786 class LLVMFPVPRedIntrinOp, class FPReductionNeutral>
787static Value lowerPredicatedReductionWithStartValue(
788 ConversionPatternRewriter &rewriter, Location loc, Type llvmType,
789 Value vectorOperand, Value accumulator, Value mask) {
790 if (llvmType.isIntOrIndex())
791 return lowerPredicatedReductionWithStartValue<LLVMIntVPRedIntrinOp,
792 IntReductionNeutral>(
793 rewriter, loc, llvmType, vectorOperand, accumulator, mask);
794
795 // FP dispatch.
796 return lowerPredicatedReductionWithStartValue<LLVMFPVPRedIntrinOp,
797 FPReductionNeutral>(
798 rewriter, loc, llvmType, vectorOperand, accumulator, mask);
799}
800
801/// Conversion pattern for all vector reductions.
802class VectorReductionOpConversion
803 : public ConvertOpToLLVMPattern<vector::ReductionOp> {
804public:
805 explicit VectorReductionOpConversion(const LLVMTypeConverter &typeConv,
806 bool reassociateFPRed)
807 : ConvertOpToLLVMPattern<vector::ReductionOp>(typeConv),
808 reassociateFPReductions(reassociateFPRed) {}
809
810 LogicalResult
811 matchAndRewrite(vector::ReductionOp reductionOp, OpAdaptor adaptor,
812 ConversionPatternRewriter &rewriter) const override {
813 auto kind = reductionOp.getKind();
814 Type eltType = reductionOp.getDest().getType();
815 Type llvmType = typeConverter->convertType(eltType);
816 Value operand = adaptor.getVector();
817 Value acc = adaptor.getAcc();
818 Location loc = reductionOp.getLoc();
819
820 if (eltType.isIntOrIndex()) {
821 // Integer reductions: add/mul/min/max/and/or/xor.
822 Value result;
823 switch (kind) {
824 case vector::CombiningKind::ADD:
825 result =
826 createIntegerReductionArithmeticOpLowering<LLVM::vector_reduce_add,
827 LLVM::AddOp>(
828 rewriter, loc, llvmType, operand, acc);
829 break;
830 case vector::CombiningKind::MUL:
831 result =
832 createIntegerReductionArithmeticOpLowering<LLVM::vector_reduce_mul,
833 LLVM::MulOp>(
834 rewriter, loc, llvmType, operand, acc);
835 break;
836 case vector::CombiningKind::MINUI:
837 result = createIntegerReductionComparisonOpLowering<
838 LLVM::vector_reduce_umin>(rewriter, loc, llvmType, operand, acc,
839 LLVM::ICmpPredicate::ule);
840 break;
841 case vector::CombiningKind::MINSI:
842 result = createIntegerReductionComparisonOpLowering<
843 LLVM::vector_reduce_smin>(rewriter, loc, llvmType, operand, acc,
844 LLVM::ICmpPredicate::sle);
845 break;
846 case vector::CombiningKind::MAXUI:
847 result = createIntegerReductionComparisonOpLowering<
848 LLVM::vector_reduce_umax>(rewriter, loc, llvmType, operand, acc,
849 LLVM::ICmpPredicate::uge);
850 break;
851 case vector::CombiningKind::MAXSI:
852 result = createIntegerReductionComparisonOpLowering<
853 LLVM::vector_reduce_smax>(rewriter, loc, llvmType, operand, acc,
854 LLVM::ICmpPredicate::sge);
855 break;
856 case vector::CombiningKind::AND:
857 result =
858 createIntegerReductionArithmeticOpLowering<LLVM::vector_reduce_and,
859 LLVM::AndOp>(
860 rewriter, loc, llvmType, operand, acc);
861 break;
862 case vector::CombiningKind::OR:
863 result =
864 createIntegerReductionArithmeticOpLowering<LLVM::vector_reduce_or,
865 LLVM::OrOp>(
866 rewriter, loc, llvmType, operand, acc);
867 break;
868 case vector::CombiningKind::XOR:
869 result =
870 createIntegerReductionArithmeticOpLowering<LLVM::vector_reduce_xor,
871 LLVM::XOrOp>(
872 rewriter, loc, llvmType, operand, acc);
873 break;
874 default:
875 return failure();
876 }
877 rewriter.replaceOp(reductionOp, result);
878
879 return success();
880 }
881
882 if (!isa<FloatType>(eltType))
883 return failure();
884
885 arith::FastMathFlagsAttr fMFAttr = reductionOp.getFastMathFlagsAttr();
886 LLVM::FastmathFlagsAttr fmf = LLVM::FastmathFlagsAttr::get(
887 reductionOp.getContext(),
888 convertArithFastMathFlagsToLLVM(fMFAttr.getValue()));
889 fmf = LLVM::FastmathFlagsAttr::get(
890 reductionOp.getContext(),
891 fmf.getValue() | (reassociateFPReductions ? LLVM::FastmathFlags::reassoc
892 : LLVM::FastmathFlags::none));
893
894 // Floating-point reductions: add/mul/min/max
895 Value result;
896 if (kind == vector::CombiningKind::ADD) {
897 result = lowerReductionWithStartValue<LLVM::vector_reduce_fadd,
898 ReductionNeutralZero>(
899 rewriter, loc, llvmType, operand, acc, fmf);
900 } else if (kind == vector::CombiningKind::MUL) {
901 result = lowerReductionWithStartValue<LLVM::vector_reduce_fmul,
902 ReductionNeutralFPOne>(
903 rewriter, loc, llvmType, operand, acc, fmf);
904 } else if (kind == vector::CombiningKind::MINIMUMF) {
905 result =
906 createFPReductionComparisonOpLowering<LLVM::vector_reduce_fminimum>(
907 rewriter, loc, llvmType, operand, acc, fmf);
908 } else if (kind == vector::CombiningKind::MAXIMUMF) {
909 result =
910 createFPReductionComparisonOpLowering<LLVM::vector_reduce_fmaximum>(
911 rewriter, loc, llvmType, operand, acc, fmf);
912 } else if (kind == vector::CombiningKind::MINNUMF) {
913 result = createFPReductionComparisonOpLowering<LLVM::vector_reduce_fmin>(
914 rewriter, loc, llvmType, operand, acc, fmf);
915 } else if (kind == vector::CombiningKind::MAXNUMF) {
916 result = createFPReductionComparisonOpLowering<LLVM::vector_reduce_fmax>(
917 rewriter, loc, llvmType, operand, acc, fmf);
918 } else {
919 return failure();
920 }
921
922 rewriter.replaceOp(reductionOp, result);
923 return success();
924 }
925
926private:
927 const bool reassociateFPReductions;
928};
929
930/// Base class to convert a `vector.mask` operation while matching traits
931/// of the maskable operation nested inside. A `VectorMaskOpConversionBase`
932/// instance matches against a `vector.mask` operation. The `matchAndRewrite`
933/// method performs a second match against the maskable operation `MaskedOp`.
934/// Finally, it invokes the virtual method `matchAndRewriteMaskableOp` to be
935/// implemented by the concrete conversion classes. This method can match
936/// against specific traits of the `vector.mask` and the maskable operation. It
937/// must replace the `vector.mask` operation.
938template <class MaskedOp>
939class VectorMaskOpConversionBase
940 : public ConvertOpToLLVMPattern<vector::MaskOp> {
941public:
942 using ConvertOpToLLVMPattern<vector::MaskOp>::ConvertOpToLLVMPattern;
943
944 LogicalResult
945 matchAndRewrite(vector::MaskOp maskOp, OpAdaptor adaptor,
946 ConversionPatternRewriter &rewriter) const final {
947 // Match against the maskable operation kind.
948 auto maskedOp = llvm::dyn_cast_or_null<MaskedOp>(maskOp.getMaskableOp());
949 if (!maskedOp)
950 return failure();
951 return matchAndRewriteMaskableOp(maskOp, maskedOp, rewriter);
952 }
953
954protected:
955 virtual LogicalResult
956 matchAndRewriteMaskableOp(vector::MaskOp maskOp,
957 vector::MaskableOpInterface maskableOp,
958 ConversionPatternRewriter &rewriter) const = 0;
959};
960
961class MaskedReductionOpConversion
962 : public VectorMaskOpConversionBase<vector::ReductionOp> {
963
964public:
965 using VectorMaskOpConversionBase<
966 vector::ReductionOp>::VectorMaskOpConversionBase;
967
968 LogicalResult matchAndRewriteMaskableOp(
969 vector::MaskOp maskOp, MaskableOpInterface maskableOp,
970 ConversionPatternRewriter &rewriter) const override {
971 auto reductionOp = cast<ReductionOp>(maskableOp.getOperation());
972 auto kind = reductionOp.getKind();
973 Type eltType = reductionOp.getDest().getType();
974 Type llvmType = typeConverter->convertType(eltType);
975 Value operand = reductionOp.getVector();
976 Value acc = reductionOp.getAcc();
977 Location loc = reductionOp.getLoc();
978
979 arith::FastMathFlagsAttr fMFAttr = reductionOp.getFastMathFlagsAttr();
980 LLVM::FastmathFlagsAttr fmf = LLVM::FastmathFlagsAttr::get(
981 reductionOp.getContext(),
982 convertArithFastMathFlagsToLLVM(fMFAttr.getValue()));
983 const bool noInfs =
984 LLVM::bitEnumContainsAny(fmf.getValue(), LLVM::FastmathFlags::ninf);
985
986 Value result;
987 switch (kind) {
988 case vector::CombiningKind::ADD:
989 result = lowerPredicatedReductionWithStartValue<
990 LLVM::VPReduceAddOp, ReductionNeutralZero, LLVM::VPReduceFAddOp,
991 ReductionNeutralZero>(rewriter, loc, llvmType, operand, acc,
992 maskOp.getMask());
993 break;
994 case vector::CombiningKind::MUL:
995 result = lowerPredicatedReductionWithStartValue<
996 LLVM::VPReduceMulOp, ReductionNeutralIntOne, LLVM::VPReduceFMulOp,
997 ReductionNeutralFPOne>(rewriter, loc, llvmType, operand, acc,
998 maskOp.getMask());
999 break;
1000 case vector::CombiningKind::MINUI:
1001 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceUMinOp,
1002 ReductionNeutralUIntMax>(
1003 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1004 break;
1005 case vector::CombiningKind::MINSI:
1006 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceSMinOp,
1007 ReductionNeutralSIntMax>(
1008 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1009 break;
1010 case vector::CombiningKind::MAXUI:
1011 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceUMaxOp,
1012 ReductionNeutralUIntMin>(
1013 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1014 break;
1015 case vector::CombiningKind::MAXSI:
1016 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceSMaxOp,
1017 ReductionNeutralSIntMin>(
1018 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1019 break;
1020 case vector::CombiningKind::AND:
1021 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceAndOp,
1022 ReductionNeutralAllOnes>(
1023 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1024 break;
1025 case vector::CombiningKind::OR:
1026 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceOrOp,
1027 ReductionNeutralZero>(
1028 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1029 break;
1030 case vector::CombiningKind::XOR:
1031 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceXorOp,
1032 ReductionNeutralZero>(
1033 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1034 break;
1035 case vector::CombiningKind::MINNUMF:
1036 result =
1037 lowerPredicatedReductionWithStartValue<LLVM::VPReduceFMinOp,
1038 ReductionNeutralFPNegQNaN>(
1039 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1040 break;
1041 case vector::CombiningKind::MAXNUMF:
1042 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceFMaxOp,
1043 ReductionNeutralFPQNaN>(
1044 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1045 break;
1046 case CombiningKind::MAXIMUMF:
1047 // `ninf` promises no infinity reaches the reduction, so the neutral start
1048 // value must stay finite.
1049 result =
1050 noInfs
1051 ? lowerPredicatedReductionWithStartValue<
1052 LLVM::VPReduceFMaximumOp, ReductionNeutralFPLowestFinite>(
1053 rewriter, loc, llvmType, operand, acc, maskOp.getMask())
1054 : lowerPredicatedReductionWithStartValue<
1055 LLVM::VPReduceFMaximumOp, ReductionNeutralFPNegInf>(
1056 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1057 break;
1058 case CombiningKind::MINIMUMF:
1059 result =
1060 noInfs
1061 ? lowerPredicatedReductionWithStartValue<
1062 LLVM::VPReduceFMinimumOp, ReductionNeutralFPLargestFinite>(
1063 rewriter, loc, llvmType, operand, acc, maskOp.getMask())
1064 : lowerPredicatedReductionWithStartValue<
1065 LLVM::VPReduceFMinimumOp, ReductionNeutralFPPosInf>(
1066 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1067 break;
1068 }
1069
1070 // Replace `vector.mask` operation altogether.
1071 rewriter.replaceOp(maskOp, result);
1072 return success();
1073 }
1074};
1075
1076class VectorShuffleOpConversion
1077 : public ConvertOpToLLVMPattern<vector::ShuffleOp> {
1078public:
1079 using ConvertOpToLLVMPattern<vector::ShuffleOp>::ConvertOpToLLVMPattern;
1080
1081 LogicalResult
1082 matchAndRewrite(vector::ShuffleOp shuffleOp, OpAdaptor adaptor,
1083 ConversionPatternRewriter &rewriter) const override {
1084 auto loc = shuffleOp->getLoc();
1085 auto v1Type = shuffleOp.getV1VectorType();
1086 auto v2Type = shuffleOp.getV2VectorType();
1087 auto vectorType = shuffleOp.getResultVectorType();
1088 Type llvmType = typeConverter->convertType(vectorType);
1089 ArrayRef<int64_t> mask = shuffleOp.getMask();
1090
1091 // Bail if result type cannot be lowered.
1092 if (!llvmType)
1093 return failure();
1094
1095 // Get rank and dimension sizes.
1096 int64_t rank = vectorType.getRank();
1097#ifndef NDEBUG
1098 bool wellFormed0DCase =
1099 v1Type.getRank() == 0 && v2Type.getRank() == 0 && rank == 1;
1100 bool wellFormedNDCase =
1101 v1Type.getRank() == rank && v2Type.getRank() == rank;
1102 assert((wellFormed0DCase || wellFormedNDCase) && "op is not well-formed");
1103#endif
1104
1105 // For rank 0 and 1, where both operands have *exactly* the same vector
1106 // type, there is direct shuffle support in LLVM. Use it!
1107 if (rank <= 1 && v1Type == v2Type) {
1108 Value llvmShuffleOp = LLVM::ShuffleVectorOp::create(
1109 rewriter, loc, adaptor.getV1(), adaptor.getV2(),
1110 llvm::to_vector_of<int32_t>(mask));
1111 rewriter.replaceOp(shuffleOp, llvmShuffleOp);
1112 return success();
1113 }
1114
1115 // For all other cases, insert the individual values individually.
1116 int64_t v1Dim = v1Type.getDimSize(0);
1117 Type eltType;
1118 if (auto arrayType = dyn_cast<LLVM::LLVMArrayType>(llvmType))
1119 eltType = arrayType.getElementType();
1120 else
1121 eltType = cast<VectorType>(llvmType).getElementType();
1122 Value insert = LLVM::PoisonOp::create(rewriter, loc, llvmType);
1123 int64_t insPos = 0;
1124 for (int64_t extPos : mask) {
1125 Value value = adaptor.getV1();
1126 if (extPos >= v1Dim) {
1127 extPos -= v1Dim;
1128 value = adaptor.getV2();
1129 }
1130 Value extract = extractOne(rewriter, *getTypeConverter(), loc, value,
1131 eltType, rank, extPos);
1132 insert = insertOne(rewriter, *getTypeConverter(), loc, insert, extract,
1133 llvmType, rank, insPos++);
1134 }
1135 rewriter.replaceOp(shuffleOp, insert);
1136 return success();
1137 }
1138};
1139
1140class VectorExtractOpConversion
1141 : public ConvertOpToLLVMPattern<vector::ExtractOp> {
1142public:
1143 using ConvertOpToLLVMPattern<vector::ExtractOp>::ConvertOpToLLVMPattern;
1144
1145 LogicalResult
1146 matchAndRewrite(vector::ExtractOp extractOp, OpAdaptor adaptor,
1147 ConversionPatternRewriter &rewriter) const override {
1148 auto loc = extractOp->getLoc();
1149 auto resultType = extractOp.getResult().getType();
1150 auto llvmResultType = typeConverter->convertType(resultType);
1151 // Bail if result type cannot be lowered.
1152 if (!llvmResultType)
1153 return failure();
1154
1155 SmallVector<OpFoldResult> positionVec = getMixedValues(
1156 adaptor.getStaticPosition(), adaptor.getDynamicPosition(), rewriter);
1157
1158 // The Vector -> LLVM lowering models N-D vectors as nested aggregates of
1159 // 1-d vectors. This nesting is modeled using arrays. We do this conversion
1160 // from a N-d vector extract to a nested aggregate vector extract in two
1161 // steps:
1162 // - Extract a member from the nested aggregate. The result can be
1163 // a lower rank nested aggregate or a vector (1-D). This is done using
1164 // `llvm.extractvalue`.
1165 // - Extract a scalar out of the vector if needed. This is done using
1166 // `llvm.extractelement`.
1167
1168 // Determine if we need to extract a member out of the aggregate. We
1169 // always need to extract a member if the input rank >= 2.
1170 bool extractsAggregate = extractOp.getSourceVectorType().getRank() >= 2;
1171 // Determine if we need to extract a scalar as the result. We extract
1172 // a scalar if the extract is full rank, i.e., the number of indices is
1173 // equal to source vector rank.
1174 bool extractsScalar = static_cast<int64_t>(positionVec.size()) ==
1175 extractOp.getSourceVectorType().getRank();
1176
1177 // Since the LLVM type converter converts 0-d vectors to 1-d vectors, we
1178 // need to add a position for this change.
1179 if (extractOp.getSourceVectorType().getRank() == 0) {
1180 Type idxType = typeConverter->convertType(rewriter.getIndexType());
1181 positionVec.push_back(rewriter.getZeroAttr(idxType));
1182 }
1183
1184 Value extracted = adaptor.getSource();
1185 if (extractsAggregate) {
1186 ArrayRef<OpFoldResult> position(positionVec);
1187 if (extractsScalar) {
1188 // If we are extracting a scalar from the extracted member, we drop
1189 // the last index, which will be used to extract the scalar out of the
1190 // vector.
1191 position = position.drop_back();
1192 }
1193 // llvm.extractvalue does not support dynamic dimensions.
1194 if (!llvm::all_of(position, llvm::IsaPred<Attribute>)) {
1195 return failure();
1196 }
1197 extracted = LLVM::ExtractValueOp::create(rewriter, loc, extracted,
1198 getAsIntegers(position));
1199 }
1200
1201 if (extractsScalar) {
1202 extracted = LLVM::ExtractElementOp::create(
1203 rewriter, loc, extracted,
1204 getAsLLVMValue(rewriter, loc, positionVec.back()));
1205 }
1206
1207 rewriter.replaceOp(extractOp, extracted);
1208 return success();
1209 }
1210};
1211
1212/// Conversion pattern that turns a vector.fma on a 1-D vector
1213/// into an llvm.intr.fmuladd. This is a trivial 1-1 conversion.
1214/// This does not match vectors of n >= 2 rank.
1215///
1216/// Example:
1217/// ```
1218/// vector.fma %a, %a, %a : vector<8xf32>
1219/// ```
1220/// is converted to:
1221/// ```
1222/// llvm.intr.fmuladd %va, %va, %va:
1223/// (!llvm."<8 x f32>">, !llvm<"<8 x f32>">, !llvm<"<8 x f32>">)
1224/// -> !llvm."<8 x f32>">
1225/// ```
1226class VectorFMAOp1DConversion : public ConvertOpToLLVMPattern<vector::FMAOp> {
1227public:
1228 using ConvertOpToLLVMPattern<vector::FMAOp>::ConvertOpToLLVMPattern;
1229
1230 LogicalResult
1231 matchAndRewrite(vector::FMAOp fmaOp, OpAdaptor adaptor,
1232 ConversionPatternRewriter &rewriter) const override {
1233 VectorType vType = fmaOp.getVectorType();
1234 if (vType.getRank() > 1)
1235 return failure();
1236
1237 rewriter.replaceOpWithNewOp<LLVM::FMulAddOp>(
1238 fmaOp, adaptor.getLhs(), adaptor.getRhs(), adaptor.getAcc());
1239 return success();
1240 }
1241};
1242
1243class VectorInsertOpConversion
1244 : public ConvertOpToLLVMPattern<vector::InsertOp> {
1245public:
1246 using ConvertOpToLLVMPattern<vector::InsertOp>::ConvertOpToLLVMPattern;
1247
1248 LogicalResult
1249 matchAndRewrite(vector::InsertOp insertOp, OpAdaptor adaptor,
1250 ConversionPatternRewriter &rewriter) const override {
1251 auto loc = insertOp->getLoc();
1252 auto destVectorType = insertOp.getDestVectorType();
1253 auto llvmResultType = typeConverter->convertType(destVectorType);
1254 // Bail if result type cannot be lowered.
1255 if (!llvmResultType)
1256 return failure();
1257
1258 SmallVector<OpFoldResult> positionVec = getMixedValues(
1259 adaptor.getStaticPosition(), adaptor.getDynamicPosition(), rewriter);
1260
1261 // The logic in this pattern mirrors VectorExtractOpConversion. Refer to
1262 // its explanatory comment about how N-D vectors are converted as nested
1263 // aggregates (llvm.array's) of 1D vectors.
1264 //
1265 // The innermost dimension of the destination vector, when converted to a
1266 // nested aggregate form, will always be a 1D vector.
1267 //
1268 // * If the insertion is happening into the innermost dimension of the
1269 // destination vector:
1270 // - If the destination is a nested aggregate, extract a 1D vector out of
1271 // the aggregate. This can be done using llvm.extractvalue. The
1272 // destination is now guaranteed to be a 1D vector, to which we are
1273 // inserting.
1274 // - Do the insertion into the 1D destination vector, and make the result
1275 // the new source nested aggregate. This can be done using
1276 // llvm.insertelement.
1277 // * Insert the source nested aggregate into the destination nested
1278 // aggregate.
1279
1280 // Determine if we need to extract/insert a 1D vector out of the aggregate.
1281 bool isNestedAggregate = isa<LLVM::LLVMArrayType>(llvmResultType);
1282 // Determine if we need to insert a scalar into the 1D vector.
1283 bool insertIntoInnermostDim =
1284 static_cast<int64_t>(positionVec.size()) == destVectorType.getRank();
1285
1286 ArrayRef<OpFoldResult> positionOf1DVectorWithinAggregate(
1287 positionVec.begin(),
1288 insertIntoInnermostDim ? positionVec.size() - 1 : positionVec.size());
1289 OpFoldResult positionOfScalarWithin1DVector;
1290 if (destVectorType.getRank() == 0) {
1291 // Since the LLVM type converter converts 0D vectors to 1D vectors, we
1292 // need to create a 0 here as the position into the 1D vector.
1293 Type idxType = typeConverter->convertType(rewriter.getIndexType());
1294 positionOfScalarWithin1DVector = rewriter.getZeroAttr(idxType);
1295 } else if (insertIntoInnermostDim) {
1296 positionOfScalarWithin1DVector = positionVec.back();
1297 }
1298
1299 // We are going to mutate this 1D vector until it is either the final
1300 // result (in the non-aggregate case) or the value that needs to be
1301 // inserted into the aggregate result.
1302 Value sourceAggregate = adaptor.getValueToStore();
1303 if (insertIntoInnermostDim) {
1304 // Scalar-into-1D-vector case, so we know we will have to create a
1305 // InsertElementOp. The question is into what destination.
1306 if (isNestedAggregate) {
1307 // Aggregate case: the destination for the InsertElementOp needs to be
1308 // extracted from the aggregate.
1309 if (!llvm::all_of(positionOf1DVectorWithinAggregate,
1310 llvm::IsaPred<Attribute>)) {
1311 // llvm.extractvalue does not support dynamic dimensions.
1312 return failure();
1313 }
1314 sourceAggregate = LLVM::ExtractValueOp::create(
1315 rewriter, loc, adaptor.getDest(),
1316 getAsIntegers(positionOf1DVectorWithinAggregate));
1317 } else {
1318 // No-aggregate case. The destination for the InsertElementOp is just
1319 // the insertOp's destination.
1320 sourceAggregate = adaptor.getDest();
1321 }
1322 // Insert the scalar into the 1D vector.
1323 sourceAggregate = LLVM::InsertElementOp::create(
1324 rewriter, loc, sourceAggregate.getType(), sourceAggregate,
1325 adaptor.getValueToStore(),
1326 getAsLLVMValue(rewriter, loc, positionOfScalarWithin1DVector));
1327 }
1328
1329 Value result = sourceAggregate;
1330 if (isNestedAggregate) {
1331 if (!llvm::all_of(positionOf1DVectorWithinAggregate,
1332 llvm::IsaPred<Attribute>)) {
1333 // llvm.insertvalue does not support dynamic dimensions.
1334 return failure();
1335 }
1336 result = LLVM::InsertValueOp::create(
1337 rewriter, loc, adaptor.getDest(), sourceAggregate,
1338 getAsIntegers(positionOf1DVectorWithinAggregate));
1339 }
1340
1341 rewriter.replaceOp(insertOp, result);
1342 return success();
1343 }
1344};
1345
1346/// Lower vector.scalable.insert ops to LLVM vector.insert
1347struct VectorScalableInsertOpLowering
1348 : public ConvertOpToLLVMPattern<vector::ScalableInsertOp> {
1349 using ConvertOpToLLVMPattern<
1350 vector::ScalableInsertOp>::ConvertOpToLLVMPattern;
1351
1352 LogicalResult
1353 matchAndRewrite(vector::ScalableInsertOp insOp, OpAdaptor adaptor,
1354 ConversionPatternRewriter &rewriter) const override {
1355 rewriter.replaceOpWithNewOp<LLVM::vector_insert>(
1356 insOp, adaptor.getDest(), adaptor.getValueToStore(), adaptor.getPos());
1357 return success();
1358 }
1359};
1360
1361/// Lower vector.scalable.extract ops to LLVM vector.extract
1362struct VectorScalableExtractOpLowering
1363 : public ConvertOpToLLVMPattern<vector::ScalableExtractOp> {
1364 using ConvertOpToLLVMPattern<
1365 vector::ScalableExtractOp>::ConvertOpToLLVMPattern;
1366
1367 LogicalResult
1368 matchAndRewrite(vector::ScalableExtractOp extOp, OpAdaptor adaptor,
1369 ConversionPatternRewriter &rewriter) const override {
1370 rewriter.replaceOpWithNewOp<LLVM::vector_extract>(
1371 extOp, typeConverter->convertType(extOp.getResultVectorType()),
1372 adaptor.getSource(), adaptor.getPos());
1373 return success();
1374 }
1375};
1376
1377/// Rank reducing rewrite for n-D FMA into (n-1)-D FMA where n > 1.
1378///
1379/// Example:
1380/// ```
1381/// %d = vector.fma %a, %b, %c : vector<2x4xf32>
1382/// ```
1383/// is rewritten into:
1384/// ```
1385/// %r = vector.broadcast %f0 : f32 to vector<2x4xf32>
1386/// %va = vector.extractvalue %a[0] : vector<2x4xf32>
1387/// %vb = vector.extractvalue %b[0] : vector<2x4xf32>
1388/// %vc = vector.extractvalue %c[0] : vector<2x4xf32>
1389/// %vd = vector.fma %va, %vb, %vc : vector<4xf32>
1390/// %r2 = vector.insertvalue %vd, %r[0] : vector<4xf32> into vector<2x4xf32>
1391/// %va2 = vector.extractvalue %a2[1] : vector<2x4xf32>
1392/// %vb2 = vector.extractvalue %b2[1] : vector<2x4xf32>
1393/// %vc2 = vector.extractvalue %c2[1] : vector<2x4xf32>
1394/// %vd2 = vector.fma %va2, %vb2, %vc2 : vector<4xf32>
1395/// %r3 = vector.insertvalue %vd2, %r2[1] : vector<4xf32> into vector<2x4xf32>
1396/// // %r3 holds the final value.
1397/// ```
1398class VectorFMAOpNDRewritePattern : public OpRewritePattern<FMAOp> {
1399public:
1400 using Base::Base;
1401
1402 void initialize() {
1403 // This pattern recursively unpacks one dimension at a time. The recursion
1404 // bounded as the rank is strictly decreasing.
1405 setHasBoundedRewriteRecursion();
1406 }
1407
1408 LogicalResult matchAndRewrite(FMAOp op,
1409 PatternRewriter &rewriter) const override {
1410 auto vType = op.getVectorType();
1411 if (vType.getRank() < 2)
1412 return failure();
1413
1414 auto loc = op.getLoc();
1415 auto elemType = vType.getElementType();
1416 Value zero = arith::ConstantOp::create(rewriter, loc, elemType,
1417 rewriter.getZeroAttr(elemType));
1418 Value desc = vector::BroadcastOp::create(rewriter, loc, vType, zero);
1419 for (int64_t i = 0, e = vType.getShape().front(); i != e; ++i) {
1420 Value extrLHS = ExtractOp::create(rewriter, loc, op.getLhs(), i);
1421 Value extrRHS = ExtractOp::create(rewriter, loc, op.getRhs(), i);
1422 Value extrACC = ExtractOp::create(rewriter, loc, op.getAcc(), i);
1423 Value fma = FMAOp::create(rewriter, loc, extrLHS, extrRHS, extrACC);
1424 desc = InsertOp::create(rewriter, loc, fma, desc, i);
1425 }
1426 rewriter.replaceOp(op, desc);
1427 return success();
1428 }
1429};
1430
1431/// Returns the strides if the memory underlying `memRefType` has a contiguous
1432/// static layout.
1433static std::optional<SmallVector<int64_t, 4>>
1434computeContiguousStrides(MemRefType memRefType) {
1435 int64_t offset;
1437 if (failed(memRefType.getStridesAndOffset(strides, offset)))
1438 return std::nullopt;
1439 if (!strides.empty() && strides.back() != 1)
1440 return std::nullopt;
1441 // If no layout or identity layout, this is contiguous by definition.
1442 if (memRefType.getLayout().isIdentity())
1443 return strides;
1444
1445 // Otherwise, we must determine contiguity form shapes. This can only ever
1446 // work in static cases because MemRefType is underspecified to represent
1447 // contiguous dynamic shapes in other ways than with just empty/identity
1448 // layout.
1449 auto sizes = memRefType.getShape();
1450 for (int index = 0, e = strides.size() - 1; index < e; ++index) {
1451 if (ShapedType::isDynamic(sizes[index + 1]) ||
1452 ShapedType::isDynamic(strides[index]) ||
1453 ShapedType::isDynamic(strides[index + 1]))
1454 return std::nullopt;
1455 if (strides[index] != strides[index + 1] * sizes[index + 1])
1456 return std::nullopt;
1457 }
1458 return strides;
1459}
1460
1461class VectorTypeCastOpConversion
1462 : public ConvertOpToLLVMPattern<vector::TypeCastOp> {
1463public:
1464 using ConvertOpToLLVMPattern<vector::TypeCastOp>::ConvertOpToLLVMPattern;
1465
1466 LogicalResult
1467 matchAndRewrite(vector::TypeCastOp castOp, OpAdaptor adaptor,
1468 ConversionPatternRewriter &rewriter) const override {
1469 auto loc = castOp->getLoc();
1470 MemRefType sourceMemRefType =
1471 cast<MemRefType>(castOp.getOperand().getType());
1472 MemRefType targetMemRefType = castOp.getType();
1473
1474 // Only static shape casts supported atm.
1475 if (!sourceMemRefType.hasStaticShape() ||
1476 !targetMemRefType.hasStaticShape())
1477 return failure();
1478
1479 auto llvmSourceDescriptorTy =
1480 dyn_cast<LLVM::LLVMStructType>(adaptor.getOperands()[0].getType());
1481 if (!llvmSourceDescriptorTy)
1482 return failure();
1483 MemRefDescriptor sourceMemRef(adaptor.getOperands()[0]);
1484
1485 auto llvmTargetDescriptorTy = dyn_cast_or_null<LLVM::LLVMStructType>(
1486 typeConverter->convertType(targetMemRefType));
1487 if (!llvmTargetDescriptorTy)
1488 return failure();
1489
1490 // Only contiguous source buffers supported atm.
1491 auto sourceStrides = computeContiguousStrides(sourceMemRefType);
1492 if (!sourceStrides)
1493 return failure();
1494 auto targetStrides = computeContiguousStrides(targetMemRefType);
1495 if (!targetStrides)
1496 return failure();
1497 // Only support static strides for now, regardless of contiguity.
1498 if (llvm::any_of(*targetStrides, ShapedType::isDynamic))
1499 return failure();
1500
1501 // The offset, size and stride fields of a memref descriptor use the
1502 // converted index type.
1503 Type indexTy = getTypeConverter()->getIndexType();
1504
1505 // Create descriptor.
1506 auto desc = MemRefDescriptor::poison(rewriter, loc, llvmTargetDescriptorTy);
1507 // Set allocated ptr.
1508 Value allocated = sourceMemRef.allocatedPtr(rewriter, loc);
1509 desc.setAllocatedPtr(rewriter, loc, allocated);
1510
1511 // Set aligned ptr.
1512 Value ptr = sourceMemRef.alignedPtr(rewriter, loc);
1513 desc.setAlignedPtr(rewriter, loc, ptr);
1514 // Fill offset 0.
1515 desc.setOffset(rewriter, loc,
1516 LLVM::createIndexAttrConstant(rewriter, loc, indexTy, 0));
1517
1518 // Fill size and stride descriptors in memref.
1519 for (const auto &indexedSize :
1520 llvm::enumerate(targetMemRefType.getShape())) {
1521 int64_t index = indexedSize.index();
1522 desc.setSize(rewriter, loc, index,
1523 LLVM::createIndexAttrConstant(rewriter, loc, indexTy,
1524 indexedSize.value()));
1525 desc.setStride(rewriter, loc, index,
1526 LLVM::createIndexAttrConstant(rewriter, loc, indexTy,
1527 (*targetStrides)[index]));
1528 }
1529
1530 rewriter.replaceOp(castOp, {desc});
1531 return success();
1532 }
1533};
1534
1535/// Conversion pattern for a `vector.create_mask` (1-D scalable vectors only).
1536/// Non-scalable versions of this operation are handled in Vector Transforms.
1537class VectorCreateMaskOpConversion
1538 : public OpConversionPattern<vector::CreateMaskOp> {
1539public:
1540 explicit VectorCreateMaskOpConversion(MLIRContext *context,
1541 bool enableIndexOpt)
1542 : OpConversionPattern<vector::CreateMaskOp>(context),
1543 force32BitVectorIndices(enableIndexOpt) {}
1544
1545 LogicalResult
1546 matchAndRewrite(vector::CreateMaskOp op, OpAdaptor adaptor,
1547 ConversionPatternRewriter &rewriter) const override {
1548 auto dstType = op.getType();
1549 if (dstType.getRank() != 1 || !cast<VectorType>(dstType).isScalable())
1550 return failure();
1551 IntegerType idxType =
1552 force32BitVectorIndices ? rewriter.getI32Type() : rewriter.getI64Type();
1553 auto loc = op->getLoc();
1554 Value indices = LLVM::StepVectorOp::create(
1555 rewriter, loc,
1556 LLVM::getVectorType(idxType, dstType.getShape()[0],
1557 /*isScalable=*/true));
1558 Value maskBound = adaptor.getOperands()[0];
1559 // When using 32-bit indices, cap the bound at INT32_MAX in index type
1560 // before casting. For scalable vectors the runtime size (vscale * dim) is
1561 // unknown at compile time, so we can't clamp to `dim` as in the fixed-size
1562 // path. Clamping to INT32_MAX is safe because any realistic scalable vector
1563 // size fits well below this limit, so a bound >= vscale*dim still produces
1564 // an all-true mask after the comparison.
1565 if (force32BitVectorIndices) {
1566 Value maxBound =
1567 arith::ConstantIndexOp::create(rewriter, loc, (1LL << 31) - 1);
1568 maskBound = arith::MinSIOp::create(rewriter, loc, maskBound, maxBound);
1569 }
1570 auto bound =
1571 getValueOrCreateCastToIndexLike(rewriter, loc, idxType, maskBound);
1572 Value bounds = BroadcastOp::create(rewriter, loc, indices.getType(), bound);
1573 Value comp = arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::slt,
1574 indices, bounds);
1575 rewriter.replaceOp(op, comp);
1576 return success();
1577 }
1578
1579private:
1580 const bool force32BitVectorIndices;
1581};
1582
1583class VectorPrintOpConversion : public ConvertOpToLLVMPattern<vector::PrintOp> {
1584 SymbolTableCollection *symbolTables = nullptr;
1585
1586public:
1587 explicit VectorPrintOpConversion(
1588 const LLVMTypeConverter &typeConverter,
1589 SymbolTableCollection *symbolTables = nullptr)
1590 : ConvertOpToLLVMPattern<vector::PrintOp>(typeConverter),
1591 symbolTables(symbolTables) {}
1592
1593 // Lowering implementation that relies on a small runtime support library,
1594 // which only needs to provide a few printing methods (single value for all
1595 // data types, opening/closing bracket, comma, newline). The lowering splits
1596 // the vector into elementary printing operations. The advantage of this
1597 // approach is that the library can remain unaware of all low-level
1598 // implementation details of vectors while still supporting output of any
1599 // shaped and dimensioned vector.
1600 //
1601 // Note: This lowering only handles scalars, n-D vectors are broken into
1602 // printing scalars in loops in VectorToSCF.
1603 //
1604 // TODO: rely solely on libc in future? something else?
1605 //
1606 LogicalResult
1607 matchAndRewrite(vector::PrintOp printOp, OpAdaptor adaptor,
1608 ConversionPatternRewriter &rewriter) const override {
1609 auto parent = printOp->getParentOfType<ModuleOp>();
1610 if (!parent)
1611 return failure();
1612
1613 auto loc = printOp->getLoc();
1614
1615 if (auto value = adaptor.getSource()) {
1616 Type printType = printOp.getPrintType();
1617 if (isa<VectorType>(printType)) {
1618 // Vectors should be broken into elementary print ops in VectorToSCF.
1619 return failure();
1620 }
1621 if (failed(emitScalarPrint(rewriter, parent, loc, printType, value)))
1622 return failure();
1623 }
1624
1625 auto punct = printOp.getPunctuation();
1626 if (auto stringLiteral = printOp.getStringLiteral()) {
1627 auto createResult =
1628 LLVM::createPrintStrCall(rewriter, loc, parent, "vector_print_str",
1629 *stringLiteral, *getTypeConverter(),
1630 /*addNewline=*/false);
1631 if (createResult.failed())
1632 return failure();
1633
1634 } else if (punct != PrintPunctuation::NoPunctuation) {
1635 FailureOr<LLVM::LLVMFuncOp> op = [&]() {
1636 switch (punct) {
1637 case PrintPunctuation::Close:
1638 return LLVM::lookupOrCreatePrintCloseFn(rewriter, parent,
1639 symbolTables);
1640 case PrintPunctuation::Open:
1641 return LLVM::lookupOrCreatePrintOpenFn(rewriter, parent,
1642 symbolTables);
1643 case PrintPunctuation::Comma:
1644 return LLVM::lookupOrCreatePrintCommaFn(rewriter, parent,
1645 symbolTables);
1646 case PrintPunctuation::NewLine:
1647 return LLVM::lookupOrCreatePrintNewlineFn(rewriter, parent,
1648 symbolTables);
1649 default:
1650 llvm_unreachable("unexpected punctuation");
1651 }
1652 }();
1653 if (failed(op))
1654 return failure();
1655 emitCall(rewriter, printOp->getLoc(), op.value());
1656 }
1657
1658 rewriter.eraseOp(printOp);
1659 return success();
1660 }
1661
1662private:
1663 enum class PrintConversion {
1664 // clang-format off
1665 None,
1666 ZeroExt64,
1667 SignExt64,
1668 Bitcast16
1669 // clang-format on
1670 };
1671
1672 LogicalResult emitScalarPrint(ConversionPatternRewriter &rewriter,
1673 ModuleOp parent, Location loc, Type printType,
1674 Value value) const {
1675 if (typeConverter->convertType(printType) == nullptr)
1676 return failure();
1677
1678 // Make sure element type has runtime support.
1679 PrintConversion conversion = PrintConversion::None;
1680 FailureOr<Operation *> printer;
1681 if (printType.isF32()) {
1682 printer = LLVM::lookupOrCreatePrintF32Fn(rewriter, parent, symbolTables);
1683 } else if (printType.isF64()) {
1684 printer = LLVM::lookupOrCreatePrintF64Fn(rewriter, parent, symbolTables);
1685 } else if (printType.isF16()) {
1686 conversion = PrintConversion::Bitcast16; // bits!
1687 printer = LLVM::lookupOrCreatePrintF16Fn(rewriter, parent, symbolTables);
1688 } else if (printType.isBF16()) {
1689 conversion = PrintConversion::Bitcast16; // bits!
1690 printer = LLVM::lookupOrCreatePrintBF16Fn(rewriter, parent, symbolTables);
1691 } else if (printType.isIndex()) {
1692 printer = LLVM::lookupOrCreatePrintU64Fn(rewriter, parent, symbolTables);
1693 } else if (auto intTy = dyn_cast<IntegerType>(printType)) {
1694 // Integers need a zero or sign extension on the operand
1695 // (depending on the source type) as well as a signed or
1696 // unsigned print method. Up to 64-bit is supported.
1697 unsigned width = intTy.getWidth();
1698 if (intTy.isUnsigned()) {
1699 if (width <= 64) {
1700 if (width < 64)
1701 conversion = PrintConversion::ZeroExt64;
1702 printer =
1703 LLVM::lookupOrCreatePrintU64Fn(rewriter, parent, symbolTables);
1704 } else {
1705 return failure();
1706 }
1707 } else {
1708 assert(intTy.isSignless() || intTy.isSigned());
1709 if (width <= 64) {
1710 // Note that we *always* zero extend booleans (1-bit integers),
1711 // so that true/false is printed as 1/0 rather than -1/0.
1712 if (width == 1)
1713 conversion = PrintConversion::ZeroExt64;
1714 else if (width < 64)
1715 conversion = PrintConversion::SignExt64;
1716 printer =
1717 LLVM::lookupOrCreatePrintI64Fn(rewriter, parent, symbolTables);
1718 } else {
1719 return failure();
1720 }
1721 }
1722 } else if (auto floatTy = dyn_cast<FloatType>(printType)) {
1723 // Print other floating-point types using the APFloat runtime library.
1724 int32_t sem =
1725 llvm::APFloatBase::SemanticsToEnum(floatTy.getFloatSemantics());
1726 Value semValue = LLVM::ConstantOp::create(
1727 rewriter, loc, rewriter.getI32Type(),
1728 rewriter.getIntegerAttr(rewriter.getI32Type(), sem));
1729 Value floatBits =
1730 LLVM::ZExtOp::create(rewriter, loc, rewriter.getI64Type(), value);
1731 printer =
1732 LLVM::lookupOrCreateApFloatPrintFn(rewriter, parent, symbolTables);
1733 emitCall(rewriter, loc, printer.value(),
1734 ValueRange({semValue, floatBits}));
1735 return success();
1736 } else {
1737 return failure();
1738 }
1739 if (failed(printer))
1740 return failure();
1741
1742 switch (conversion) {
1743 case PrintConversion::ZeroExt64:
1744 value = arith::ExtUIOp::create(
1745 rewriter, loc, IntegerType::get(rewriter.getContext(), 64), value);
1746 break;
1747 case PrintConversion::SignExt64:
1748 value = arith::ExtSIOp::create(
1749 rewriter, loc, IntegerType::get(rewriter.getContext(), 64), value);
1750 break;
1751 case PrintConversion::Bitcast16:
1752 value = LLVM::BitcastOp::create(
1753 rewriter, loc, IntegerType::get(rewriter.getContext(), 16), value);
1754 break;
1755 case PrintConversion::None:
1756 break;
1757 }
1758 emitCall(rewriter, loc, printer.value(), value);
1759 return success();
1760 }
1761
1762 // Helper to emit a call.
1763 static void emitCall(ConversionPatternRewriter &rewriter, Location loc,
1764 Operation *ref, ValueRange params = ValueRange()) {
1765 LLVM::CallOp::create(rewriter, loc, TypeRange(), SymbolRefAttr::get(ref),
1766 params);
1767 }
1768};
1769
1770/// A broadcast of a scalar is lowered to an insertelement + a shufflevector
1771/// operation. Only broadcasts to 0-d and 1-d vectors are lowered by this
1772/// pattern, the higher rank cases are handled by another pattern.
1773struct VectorBroadcastScalarToLowRankLowering
1774 : public ConvertOpToLLVMPattern<vector::BroadcastOp> {
1775 using ConvertOpToLLVMPattern<vector::BroadcastOp>::ConvertOpToLLVMPattern;
1776
1777 LogicalResult
1778 matchAndRewrite(vector::BroadcastOp broadcast, OpAdaptor adaptor,
1779 ConversionPatternRewriter &rewriter) const override {
1780 if (isa<VectorType>(broadcast.getSourceType()))
1781 return rewriter.notifyMatchFailure(
1782 broadcast, "broadcast from vector type not handled");
1783
1784 VectorType resultType = broadcast.getType();
1785 if (resultType.getRank() > 1)
1786 return rewriter.notifyMatchFailure(broadcast,
1787 "broadcast to 2+-d handled elsewhere");
1788
1789 // First insert it into a poison vector so we can shuffle it.
1790 auto vectorType = typeConverter->convertType(broadcast.getType());
1791 Value poison =
1792 LLVM::PoisonOp::create(rewriter, broadcast.getLoc(), vectorType);
1793 auto zero = LLVM::ConstantOp::create(
1794 rewriter, broadcast.getLoc(),
1795 typeConverter->convertType(rewriter.getIntegerType(32)),
1796 rewriter.getZeroAttr(rewriter.getIntegerType(32)));
1797
1798 // For 0-d vector, we simply do `insertelement`.
1799 if (resultType.getRank() == 0) {
1800 rewriter.replaceOpWithNewOp<LLVM::InsertElementOp>(
1801 broadcast, vectorType, poison, adaptor.getSource(), zero);
1802 return success();
1803 }
1804
1805 auto v =
1806 LLVM::InsertElementOp::create(rewriter, broadcast.getLoc(), vectorType,
1807 poison, adaptor.getSource(), zero);
1808
1809 // For 1-d vector, we additionally do a `shufflevector`.
1810 int64_t width = cast<VectorType>(broadcast.getType()).getDimSize(0);
1811 SmallVector<int32_t> zeroValues(width, 0);
1812
1813 // Shuffle the value across the desired number of elements.
1814 auto shuffle = rewriter.createOrFold<LLVM::ShuffleVectorOp>(
1815 broadcast.getLoc(), v, poison, zeroValues);
1816 rewriter.replaceOp(broadcast, shuffle);
1817 return success();
1818 }
1819};
1820
1821/// The broadcast of a scalar is lowered to an insertelement + a shufflevector
1822/// operation. Only broadcasts to 2+-d vector result types are lowered by this
1823/// pattern, the 1-d case is handled by another pattern. Broadcasts from vectors
1824/// are not converted to LLVM, only broadcasts from scalars are.
1825struct VectorBroadcastScalarToNdLowering
1826 : public ConvertOpToLLVMPattern<BroadcastOp> {
1827 using ConvertOpToLLVMPattern<BroadcastOp>::ConvertOpToLLVMPattern;
1828
1829 LogicalResult
1830 matchAndRewrite(BroadcastOp broadcast, OpAdaptor adaptor,
1831 ConversionPatternRewriter &rewriter) const override {
1832 if (isa<VectorType>(broadcast.getSourceType()))
1833 return rewriter.notifyMatchFailure(
1834 broadcast, "broadcast from vector type not handled");
1835
1836 VectorType resultType = broadcast.getType();
1837 if (resultType.getRank() <= 1)
1838 return rewriter.notifyMatchFailure(
1839 broadcast, "broadcast to 1-d or 0-d handled elsewhere");
1840
1841 // First insert it into a poison vector so we can shuffle it.
1842 auto loc = broadcast.getLoc();
1843 auto vectorTypeInfo =
1844 LLVM::detail::extractNDVectorTypeInfo(resultType, *getTypeConverter());
1845 auto llvmNDVectorTy = vectorTypeInfo.llvmNDVectorTy;
1846 auto llvm1DVectorTy = vectorTypeInfo.llvm1DVectorTy;
1847 if (!llvmNDVectorTy || !llvm1DVectorTy)
1848 return failure();
1849
1850 // Construct returned value.
1851 Value desc = LLVM::PoisonOp::create(rewriter, loc, llvmNDVectorTy);
1852
1853 // Construct a 1-D vector with the broadcasted value that we insert in all
1854 // the places within the returned descriptor.
1855 Value vdesc = LLVM::PoisonOp::create(rewriter, loc, llvm1DVectorTy);
1856 auto zero = LLVM::ConstantOp::create(
1857 rewriter, loc, typeConverter->convertType(rewriter.getIntegerType(32)),
1858 rewriter.getZeroAttr(rewriter.getIntegerType(32)));
1859 Value v = LLVM::InsertElementOp::create(rewriter, loc, llvm1DVectorTy,
1860 vdesc, adaptor.getSource(), zero);
1861
1862 // Shuffle the value across the desired number of elements.
1863 int64_t width = resultType.getDimSize(resultType.getRank() - 1);
1864 SmallVector<int32_t> zeroValues(width, 0);
1865 v = LLVM::ShuffleVectorOp::create(rewriter, loc, v, v, zeroValues);
1866
1867 // Iterate of linear index, convert to coords space and insert broadcasted
1868 // 1-D vector in each position.
1869 nDVectorIterate(vectorTypeInfo, rewriter, [&](ArrayRef<int64_t> position) {
1870 desc = LLVM::InsertValueOp::create(rewriter, loc, desc, v, position);
1871 });
1872 rewriter.replaceOp(broadcast, desc);
1873 return success();
1874 }
1875};
1876
1877/// Conversion pattern for a `vector.interleave`.
1878/// This supports fixed-sized vectors and scalable vectors.
1879struct VectorInterleaveOpLowering
1880 : public ConvertOpToLLVMPattern<vector::InterleaveOp> {
1882
1883 LogicalResult
1884 matchAndRewrite(vector::InterleaveOp interleaveOp, OpAdaptor adaptor,
1885 ConversionPatternRewriter &rewriter) const override {
1886 VectorType resultType = interleaveOp.getResultVectorType();
1887 // n-D interleaves should have been lowered already.
1888 if (resultType.getRank() != 1)
1889 return rewriter.notifyMatchFailure(interleaveOp,
1890 "InterleaveOp not rank 1");
1891 // If the result is rank 1, then this directly maps to LLVM.
1892 if (resultType.isScalable()) {
1893 rewriter.replaceOpWithNewOp<LLVM::vector_interleave2>(
1894 interleaveOp, typeConverter->convertType(resultType),
1895 adaptor.getLhs(), adaptor.getRhs());
1896 return success();
1897 }
1898 // Lower fixed-size interleaves to a shufflevector. While the
1899 // vector.interleave2 intrinsic supports fixed and scalable vectors, the
1900 // langref still recommends fixed-vectors use shufflevector, see:
1901 // https://llvm.org/docs/LangRef.html#id876.
1902 int64_t resultVectorSize = resultType.getNumElements();
1903 SmallVector<int32_t> interleaveShuffleMask;
1904 interleaveShuffleMask.reserve(resultVectorSize);
1905 for (int i = 0, end = resultVectorSize / 2; i < end; ++i) {
1906 interleaveShuffleMask.push_back(i);
1907 interleaveShuffleMask.push_back((resultVectorSize / 2) + i);
1908 }
1909 rewriter.replaceOpWithNewOp<LLVM::ShuffleVectorOp>(
1910 interleaveOp, adaptor.getLhs(), adaptor.getRhs(),
1911 interleaveShuffleMask);
1912 return success();
1913 }
1914};
1915
1916/// Conversion pattern for a `vector.deinterleave`.
1917/// This supports fixed-sized vectors and scalable vectors.
1918struct VectorDeinterleaveOpLowering
1919 : public ConvertOpToLLVMPattern<vector::DeinterleaveOp> {
1921
1922 LogicalResult
1923 matchAndRewrite(vector::DeinterleaveOp deinterleaveOp, OpAdaptor adaptor,
1924 ConversionPatternRewriter &rewriter) const override {
1925 VectorType resultType = deinterleaveOp.getResultVectorType();
1926 VectorType sourceType = deinterleaveOp.getSourceVectorType();
1927 auto loc = deinterleaveOp.getLoc();
1928
1929 // Note: n-D deinterleave operations should be lowered to the 1-D before
1930 // converting to LLVM.
1931 if (resultType.getRank() != 1)
1932 return rewriter.notifyMatchFailure(deinterleaveOp,
1933 "DeinterleaveOp not rank 1");
1934
1935 if (resultType.isScalable()) {
1936 const auto *llvmTypeConverter = this->getTypeConverter();
1937 auto deinterleaveResults = deinterleaveOp.getResultTypes();
1938 auto packedOpResults =
1939 llvmTypeConverter->packOperationResults(deinterleaveResults);
1940 auto intrinsic = LLVM::vector_deinterleave2::create(
1941 rewriter, loc, packedOpResults, adaptor.getSource());
1942
1943 auto evenResult = LLVM::ExtractValueOp::create(
1944 rewriter, loc, intrinsic->getResult(0), 0);
1945 auto oddResult = LLVM::ExtractValueOp::create(rewriter, loc,
1946 intrinsic->getResult(0), 1);
1947
1948 rewriter.replaceOp(deinterleaveOp, ValueRange{evenResult, oddResult});
1949 return success();
1950 }
1951 // Lower fixed-size deinterleave to two shufflevectors. While the
1952 // vector.deinterleave2 intrinsic supports fixed and scalable vectors, the
1953 // langref still recommends fixed-vectors use shufflevector, see:
1954 // https://llvm.org/docs/LangRef.html#id889.
1955 int64_t resultVectorSize = resultType.getNumElements();
1956 SmallVector<int32_t> evenShuffleMask;
1957 SmallVector<int32_t> oddShuffleMask;
1958
1959 evenShuffleMask.reserve(resultVectorSize);
1960 oddShuffleMask.reserve(resultVectorSize);
1961
1962 for (int i = 0; i < sourceType.getNumElements(); ++i) {
1963 if (i % 2 == 0)
1964 evenShuffleMask.push_back(i);
1965 else
1966 oddShuffleMask.push_back(i);
1967 }
1968
1969 auto poison = LLVM::PoisonOp::create(rewriter, loc, sourceType);
1970 auto evenShuffle = LLVM::ShuffleVectorOp::create(
1971 rewriter, loc, adaptor.getSource(), poison, evenShuffleMask);
1972 auto oddShuffle = LLVM::ShuffleVectorOp::create(
1973 rewriter, loc, adaptor.getSource(), poison, oddShuffleMask);
1974
1975 rewriter.replaceOp(deinterleaveOp, ValueRange{evenShuffle, oddShuffle});
1976 return success();
1977 }
1978};
1979
1980/// Conversion pattern for a `vector.from_elements`.
1981struct VectorFromElementsLowering
1982 : public ConvertOpToLLVMPattern<vector::FromElementsOp> {
1984
1985 LogicalResult
1986 matchAndRewrite(vector::FromElementsOp fromElementsOp, OpAdaptor adaptor,
1987 ConversionPatternRewriter &rewriter) const override {
1988 Location loc = fromElementsOp.getLoc();
1989 VectorType vectorType = fromElementsOp.getType();
1990 // Only support 1-D vectors. Multi-dimensional vectors should have been
1991 // transformed to 1-D vectors by the vector-to-vector transformations before
1992 // this.
1993 if (vectorType.getRank() > 1)
1994 return rewriter.notifyMatchFailure(fromElementsOp,
1995 "rank > 1 vectors are not supported");
1996 Type llvmType = typeConverter->convertType(vectorType);
1997 Type llvmIndexType = typeConverter->convertType(rewriter.getIndexType());
1998 Value result = LLVM::PoisonOp::create(rewriter, loc, llvmType);
1999 for (auto [idx, val] : llvm::enumerate(adaptor.getElements())) {
2000 auto constIdx =
2001 LLVM::ConstantOp::create(rewriter, loc, llvmIndexType, idx);
2002 result = LLVM::InsertElementOp::create(rewriter, loc, llvmType, result,
2003 val, constIdx);
2004 }
2005 rewriter.replaceOp(fromElementsOp, result);
2006 return success();
2007 }
2008};
2009
2010/// Conversion pattern for a `vector.to_elements`.
2011struct VectorToElementsLowering
2012 : public ConvertOpToLLVMPattern<vector::ToElementsOp> {
2014
2015 LogicalResult
2016 matchAndRewrite(vector::ToElementsOp toElementsOp, OpAdaptor adaptor,
2017 ConversionPatternRewriter &rewriter) const override {
2018 Location loc = toElementsOp.getLoc();
2019 auto idxType = typeConverter->convertType(rewriter.getIndexType());
2020 Value source = adaptor.getSource();
2021
2022 SmallVector<Value> results(toElementsOp->getNumResults());
2023 for (auto [idx, element] : llvm::enumerate(toElementsOp.getElements())) {
2024 // Create an extractelement operation only for results that are not dead.
2025 if (element.use_empty())
2026 continue;
2027
2028 auto constIdx = LLVM::ConstantOp::create(
2029 rewriter, loc, idxType, rewriter.getIntegerAttr(idxType, idx));
2030 auto llvmType = typeConverter->convertType(element.getType());
2031
2032 Value result = LLVM::ExtractElementOp::create(rewriter, loc, llvmType,
2033 source, constIdx);
2034 results[idx] = result;
2035 }
2036
2037 rewriter.replaceOp(toElementsOp, results);
2038 return success();
2039 }
2040};
2041
2042/// Conversion pattern for vector.step.
2043struct VectorStepOpLowering : public ConvertOpToLLVMPattern<vector::StepOp> {
2045
2046 LogicalResult
2047 matchAndRewrite(vector::StepOp stepOp, OpAdaptor adaptor,
2048 ConversionPatternRewriter &rewriter) const override {
2049 Type llvmType = typeConverter->convertType(stepOp.getType());
2050 rewriter.replaceOpWithNewOp<LLVM::StepVectorOp>(stepOp, llvmType);
2051 return success();
2052 }
2053};
2054
2055/// Progressive lowering of a `vector.contract %a, %b, %c` with row-major matmul
2056/// semantics to:
2057/// ```
2058/// %flattened_a = vector.shape_cast %a
2059/// %flattened_b = vector.shape_cast %b
2060/// %flattened_d = vector.matrix_multiply %flattened_a, %flattened_b
2061/// %d = vector.shape_cast %%flattened_d
2062/// %e = add %c, %d
2063/// ```
2064/// `vector.matrix_multiply` later lowers to `llvm.matrix.multiply`.
2065class ContractionOpToMatmulOpLowering
2066 : public vector::MaskableOpRewritePattern<vector::ContractionOp> {
2067public:
2068 using MaskableOpRewritePattern::MaskableOpRewritePattern;
2069
2070 ContractionOpToMatmulOpLowering(MLIRContext *context,
2071 PatternBenefit benefit = 100)
2072 : MaskableOpRewritePattern<vector::ContractionOp>(context, benefit) {}
2073
2074 FailureOr<Value>
2075 matchAndRewriteMaskableOp(vector::ContractionOp op, MaskingOpInterface maskOp,
2076 PatternRewriter &rewriter) const override;
2077};
2078
2079/// Lower a qualifying `vector.contract %a, %b, %c` (with row-major matmul
2080/// semantics directly into `llvm.intr.matrix.multiply`:
2081/// BEFORE:
2082/// ```mlir
2083/// %res = vector.contract #matmat_trait %lhs, %rhs, %acc
2084/// : vector<2x4xf32>, vector<4x3xf32> into vector<2x3xf32>
2085/// ```
2086///
2087/// AFTER:
2088/// ```mlir
2089/// %lhs = vector.shape_cast %arg0 : vector<2x4xf32> to vector<8xf32>
2090/// %rhs = vector.shape_cast %arg1 : vector<4x3xf32> to vector<12xf32>
2091/// %matmul = llvm.intr.matrix.multiply %lhs, %rhs
2092/// %res = arith.addf %acc, %matmul : vector<2x3xf32>
2093/// ```
2094//
2095/// Scalable vectors are not supported.
2096FailureOr<Value> ContractionOpToMatmulOpLowering::matchAndRewriteMaskableOp(
2097 vector::ContractionOp op, MaskingOpInterface maskOp,
2098 PatternRewriter &rew) const {
2099 // TODO: Support vector.mask.
2100 if (maskOp)
2101 return failure();
2102
2103 auto iteratorTypes = op.getIteratorTypes().getValue();
2104 if (!isParallelIterator(iteratorTypes[0]) ||
2105 !isParallelIterator(iteratorTypes[1]) ||
2106 !isReductionIterator(iteratorTypes[2]))
2107 return failure();
2108
2109 Type opResType = op.getType();
2110 VectorType vecType = dyn_cast<VectorType>(opResType);
2111 if (vecType && vecType.isScalable()) {
2112 // Note - this is sufficient to reject all cases with scalable vectors.
2113 return failure();
2114 }
2115
2116 Type elementType = op.getLhsType().getElementType();
2117 if (!elementType.isIntOrFloat())
2118 return failure();
2119
2120 Type dstElementType = vecType ? vecType.getElementType() : opResType;
2121 if (elementType != dstElementType)
2122 return failure();
2123
2124 // Perform lhs + rhs transpositions to conform to matmul row-major semantics.
2125 // Bail out if the contraction cannot be put in this form.
2126 MLIRContext *ctx = op.getContext();
2127 Location loc = op.getLoc();
2128 AffineExpr m, n, k;
2129 bindDims(rew.getContext(), m, n, k);
2130 // LHS must be A(m, k) or A(k, m).
2131 Value lhs = op.getLhs();
2132 auto lhsMap = op.getIndexingMapsArray()[0];
2133 if (lhsMap == AffineMap::get(3, 0, {k, m}, ctx))
2134 lhs = vector::TransposeOp::create(rew, loc, lhs, ArrayRef<int64_t>{1, 0});
2135 else if (lhsMap != AffineMap::get(3, 0, {m, k}, ctx))
2136 return failure();
2137
2138 // RHS must be B(k, n) or B(n, k).
2139 Value rhs = op.getRhs();
2140 auto rhsMap = op.getIndexingMapsArray()[1];
2141 if (rhsMap == AffineMap::get(3, 0, {n, k}, ctx))
2142 rhs = vector::TransposeOp::create(rew, loc, rhs, ArrayRef<int64_t>{1, 0});
2143 else if (rhsMap != AffineMap::get(3, 0, {k, n}, ctx))
2144 return failure();
2145
2146 // At this point lhs and rhs are in row-major.
2147 VectorType lhsType = cast<VectorType>(lhs.getType());
2148 VectorType rhsType = cast<VectorType>(rhs.getType());
2149 int64_t lhsRows = lhsType.getDimSize(0);
2150 int64_t lhsColumns = lhsType.getDimSize(1);
2151 int64_t rhsColumns = rhsType.getDimSize(1);
2152
2153 Type flattenedLHSType =
2154 VectorType::get(lhsType.getNumElements(), lhsType.getElementType());
2155 lhs = vector::ShapeCastOp::create(rew, loc, flattenedLHSType, lhs);
2156
2157 Type flattenedRHSType =
2158 VectorType::get(rhsType.getNumElements(), rhsType.getElementType());
2159 rhs = vector::ShapeCastOp::create(rew, loc, flattenedRHSType, rhs);
2160
2161 Value mul = LLVM::MatrixMultiplyOp::create(
2162 rew, loc,
2163 VectorType::get(lhsRows * rhsColumns,
2164 cast<VectorType>(lhs.getType()).getElementType()),
2165 lhs, rhs, lhsRows, lhsColumns, rhsColumns);
2166
2167 mul = vector::ShapeCastOp::create(
2168 rew, loc,
2169 VectorType::get({lhsRows, rhsColumns},
2170 getElementTypeOrSelf(op.getAcc().getType())),
2171 mul);
2172
2173 // ACC must be C(m, n) or C(n, m).
2174 auto accMap = op.getIndexingMapsArray()[2];
2175 if (accMap == AffineMap::get(3, 0, {n, m}, ctx))
2176 mul = vector::TransposeOp::create(rew, loc, mul, ArrayRef<int64_t>{1, 0});
2177 else if (accMap != AffineMap::get(3, 0, {m, n}, ctx))
2178 llvm_unreachable("invalid contraction semantics");
2179
2180 Value res = isa<IntegerType>(elementType)
2181 ? static_cast<Value>(
2182 arith::AddIOp::create(rew, loc, op.getAcc(), mul))
2183 : static_cast<Value>(
2184 arith::AddFOp::create(rew, loc, op.getAcc(), mul));
2185
2186 return res;
2187}
2188
2189/// Lowers vector.transpose directly to llvm.intr.matrix.transpose
2190///
2191/// BEFORE:
2192/// ```mlir
2193/// %tr = vector.transpose %vec, [1, 0] : vector<2x4xf32> to vector<4x2xf32>
2194/// ```
2195/// AFTER:
2196/// ```mlir
2197/// %vec_cs = vector.shape_cast %vec : vector<2x4xf32> to vector<8xf32>
2198/// %tr = llvm.intr.matrix.transpose %vec_sc
2199/// {columns = 2 : i32, rows = 4 : i32} : vector<8xf32> into vector<8xf32>
2200/// %res = vector.shape_cast %tr : vector<8xf32> to vector<4x2xf32>
2201/// ```
2202class TransposeOpToMatrixTransposeOpLowering
2203 : public OpRewritePattern<vector::TransposeOp> {
2204public:
2205 using Base::Base;
2206
2207 LogicalResult matchAndRewrite(vector::TransposeOp op,
2208 PatternRewriter &rewriter) const override {
2209 auto loc = op.getLoc();
2210
2211 Value input = op.getVector();
2212 VectorType inputType = op.getSourceVectorType();
2213 VectorType resType = op.getResultVectorType();
2214
2215 if (inputType.isScalable())
2216 return rewriter.notifyMatchFailure(
2217 op, "This lowering does not support scalable vectors");
2218
2219 // Set up convenience transposition table.
2220 ArrayRef<int64_t> transp = op.getPermutation();
2221
2222 if (resType.getRank() != 2 || transp[0] != 1 || transp[1] != 0) {
2223 return failure();
2224 }
2225
2226 Type flattenedType =
2227 VectorType::get(resType.getNumElements(), resType.getElementType());
2228 auto matrix =
2229 vector::ShapeCastOp::create(rewriter, loc, flattenedType, input);
2230 auto rows = rewriter.getI32IntegerAttr(resType.getShape()[0]);
2231 auto columns = rewriter.getI32IntegerAttr(resType.getShape()[1]);
2232 Value trans = LLVM::MatrixTransposeOp::create(rewriter, loc, flattenedType,
2233 matrix, rows, columns);
2234 rewriter.replaceOpWithNewOp<vector::ShapeCastOp>(op, resType, trans);
2235 return success();
2236 }
2237};
2238
2239} // namespace
2240
2242 RewritePatternSet &patterns) {
2243 patterns.add<VectorFMAOpNDRewritePattern>(patterns.getContext());
2244}
2245
2247 RewritePatternSet &patterns, PatternBenefit benefit) {
2248 patterns.add<ContractionOpToMatmulOpLowering>(patterns.getContext(), benefit);
2249}
2250
2252 RewritePatternSet &patterns, PatternBenefit benefit) {
2253 patterns.add<TransposeOpToMatrixTransposeOpLowering>(patterns.getContext(),
2254 benefit);
2255}
2256
2257/// Populate the given list with patterns that convert from Vector to LLVM.
2259 const LLVMTypeConverter &converter, RewritePatternSet &patterns,
2260 bool reassociateFPReductions, bool force32BitVectorIndices,
2261 bool useVectorAlignment, bool enableGEPInboundsNuw) {
2262 // This function populates only ConversionPatterns, not RewritePatterns.
2263 MLIRContext *ctx = converter.getDialect()->getContext();
2264 patterns.add<VectorReductionOpConversion>(converter, reassociateFPReductions);
2265 patterns.add<VectorCreateMaskOpConversion>(ctx, force32BitVectorIndices);
2266 patterns.add<VectorLoadStoreConversion<vector::LoadOp>,
2267 VectorLoadStoreConversion<vector::MaskedLoadOp>,
2268 VectorLoadStoreConversion<vector::StoreOp>,
2269 VectorLoadStoreConversion<vector::MaskedStoreOp>>(
2270 converter, useVectorAlignment, enableGEPInboundsNuw);
2271 patterns.add<VectorGatherOpConversion, VectorScatterOpConversion>(
2272 converter, useVectorAlignment);
2273 patterns.add<VectorBitCastOpConversion, VectorShuffleOpConversion,
2274 VectorExtractOpConversion, VectorFMAOp1DConversion,
2275 VectorInsertOpConversion, VectorPrintOpConversion,
2276 VectorTypeCastOpConversion, VectorScaleOpConversion,
2277 VectorExpandLoadOpConversion, VectorCompressStoreOpConversion,
2278 VectorBroadcastScalarToLowRankLowering,
2279 VectorBroadcastScalarToNdLowering,
2280 VectorScalableInsertOpLowering, VectorScalableExtractOpLowering,
2281 MaskedReductionOpConversion, VectorInterleaveOpLowering,
2282 VectorDeinterleaveOpLowering, VectorFromElementsLowering,
2283 VectorToElementsLowering, VectorStepOpLowering>(converter);
2284}
2285
2286namespace {
2287struct VectorToLLVMDialectInterface : public ConvertToLLVMPatternInterface {
2288 VectorToLLVMDialectInterface(Dialect *dialect)
2289 : ConvertToLLVMPatternInterface(dialect) {}
2290
2291 using ConvertToLLVMPatternInterface::ConvertToLLVMPatternInterface;
2292 void loadDependentDialects(MLIRContext *context) const final {
2293 context->loadDialect<LLVM::LLVMDialect>();
2294 }
2295
2296 /// Hook for derived dialect interface to provide conversion patterns
2297 /// and mark dialect legal for the conversion target.
2298 void populateConvertToLLVMConversionPatterns(
2299 ConversionTarget &target, LLVMTypeConverter &typeConverter,
2300 RewritePatternSet &patterns) const final {
2301 populateVectorToLLVMConversionPatterns(typeConverter, patterns);
2302 }
2303};
2304} // namespace
2305
2307 DialectRegistry &registry) {
2308 registry.addExtension(+[](MLIRContext *ctx, vector::VectorDialect *dialect) {
2309 dialect->addInterfaces<VectorToLLVMDialectInterface>();
2310 });
2311}
return success()
static Value getIndexedPtrs(ConversionPatternRewriter &rewriter, Location loc, const LLVMTypeConverter &typeConverter, MemRefType memRefType, Value llvmMemref, Value base, Value index, VectorType vectorType)
LogicalResult getVectorToLLVMAlignment(const LLVMTypeConverter &typeConverter, VectorType vectorType, MemRefType memrefType, unsigned &align, bool useVectorAlignment)
LogicalResult getVectorAlignment(const LLVMTypeConverter &typeConverter, VectorType vectorType, unsigned &align)
LogicalResult getMemRefAlignment(const LLVMTypeConverter &typeConverter, MemRefType memrefType, unsigned &align)
static Value extractOne(ConversionPatternRewriter &rewriter, const LLVMTypeConverter &typeConverter, Location loc, Value val, Type llvmType, int64_t rank, int64_t pos)
static Value insertOne(ConversionPatternRewriter &rewriter, const LLVMTypeConverter &typeConverter, Location loc, Value val1, Value val2, Type llvmType, int64_t rank, int64_t pos)
static Value getAsLLVMValue(OpBuilder &builder, Location loc, OpFoldResult foldResult)
Convert foldResult into a Value.
static LogicalResult isMemRefTypeSupported(MemRefType memRefType, const LLVMTypeConverter &converter)
LogicalResult initialize(unsigned origNumLoops, ArrayRef< ReassociationIndices > foldedIterationDims)
lhs
static Value broadcast(Location loc, Value toBroadcast, unsigned numElements, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value to vector with numElements number of elements.
static void printOp(llvm::raw_ostream &os, Operation *op, OpPrintingFlags &flags)
Definition Unit.cpp:18
#define mul(a, b)
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
IntegerAttr getI32IntegerAttr(int32_t value)
Definition Builders.cpp:208
TypedAttr getZeroAttr(Type type)
Definition Builders.cpp:333
MLIRContext * getContext() const
Definition Builders.h:56
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition Pattern.h:233
ConvertOpToLLVMPattern(const LLVMTypeConverter &typeConverter, PatternBenefit benefit=1)
Definition Pattern.h:239
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition Dialect.h:38
Conversion from types to the LLVM IR dialect.
const llvm::DataLayout & getDataLayout() const
Returns the data layout to use during and after conversion.
FailureOr< unsigned > getMemRefAddressSpace(BaseMemRefType type) const
Return the LLVM address space corresponding to the memory space of the memref type type or failure if...
LLVM::LLVMDialect * getDialect() const
Returns the LLVM dialect.
Utility class to translate MLIR LLVM dialect types to LLVM IR.
Definition TypeToLLVM.h:39
unsigned getPreferredAlignment(Type type, const llvm::DataLayout &layout)
Returns the preferred alignment for the type given the data layout.
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
Helper class to produce LLVM dialect operations extracting or inserting elements of a MemRef descript...
LLVM::LLVMPointerType getElementPtrType()
Returns the (LLVM) pointer type this descriptor contains.
Generic implementation of one-to-one conversion from "SourceOp" to "TargetOp" where the latter belong...
Definition Pattern.h:336
This class helps build Operations.
Definition Builders.h:210
This class represents a single result from folding an operation.
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
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,...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIntOrIndex() const
Return true if this is an integer (of any signedness) or an index type.
Definition Types.cpp:114
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:118
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
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:398
void printType(Type type, AsmPrinter &printer)
Prints an LLVM Dialect type.
void nDVectorIterate(const NDVectorTypeInfo &info, OpBuilder &builder, function_ref< void(ArrayRef< int64_t >)> fun)
NDVectorTypeInfo extractNDVectorTypeInfo(VectorType vectorType, const LLVMTypeConverter &converter)
Value getStridedElementPtr(OpBuilder &builder, Location loc, const LLVMTypeConverter &converter, MemRefType type, Value memRefDesc, ValueRange indices, LLVM::GEPNoWrapFlags noWrapFlags=LLVM::GEPNoWrapFlags::none)
Performs the index computation to get to the element at indices of the memory pointed to by memRefDes...
Definition Pattern.cpp:620
Type getVectorType(Type elementType, unsigned numElements, bool isScalable=false)
Creates an LLVM dialect-compatible vector type with the given element type and length.
LLVM::FastmathFlags convertArithFastMathFlagsToLLVM(arith::FastMathFlags arithFMF)
Maps arithmetic fastmath enum values to LLVM enum values.
bool hasNegativeStaticStride(MemRefType memRefTy)
Returns true if any stride of memRefTy is statically known to be negative.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:732
bool isReductionIterator(Attribute attr)
Returns true if attr has "reduction" iterator type semantics.
Definition VectorOps.h:156
void populateVectorContractToMatrixMultiply(RewritePatternSet &patterns, PatternBenefit benefit=100)
Populate the pattern set with the following patterns:
void populateVectorRankReducingFMAPattern(RewritePatternSet &patterns)
Populates a pattern that rank-reduces n-D FMAs into (n-1)-D FMAs where n > 1.
bool isParallelIterator(Attribute attr)
Returns true if attr has "parallel" iterator type semantics.
Definition VectorOps.h:151
void registerConvertVectorToLLVMInterface(DialectRegistry &registry)
SmallVector< int64_t > getAsIntegers(ArrayRef< Value > values)
Returns the integer numbers in values.
void populateVectorTransposeToFlatTranspose(RewritePatternSet &patterns, PatternBenefit benefit=100)
Populate the pattern set with the following patterns:
Value createReductionNeutralValue(OpBuilder &builder, Location loc, Type type, vector::CombiningKind kind)
Creates a constant filled with the neutral (identity) value for the given reduction kind.
Include the generated interface declarations.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
void populateVectorToLLVMConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, bool reassociateFPReductions=false, bool force32BitVectorIndices=false, bool useVectorAlignment=false, bool enableGEPInboundsNuw=false)
Collect a set of patterns to convert from the Vector dialect to LLVM.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
Definition AffineExpr.h:311
Value getValueOrCreateCastToIndexLike(OpBuilder &b, Location loc, Type targetType, Value value)
Create a cast from an index-like value (index or integer) to another index-like value.
Definition Utils.cpp:122
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
A pattern for ops that implement MaskableOpInterface and that might be masked (i.e.