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};
729template <>
730struct VectorToScalarMapper<LLVM::vector_reduce_fmaximumnum> {
731 using Type = LLVM::MaximumNumOp;
732};
733template <>
734struct VectorToScalarMapper<LLVM::vector_reduce_fminimumnum> {
735 using Type = LLVM::MinimumNumOp;
736};
737} // namespace
738
739template <class LLVMRedIntrinOp>
740static Value createFPReductionComparisonOpLowering(
741 ConversionPatternRewriter &rewriter, Location loc, Type llvmType,
742 Value vectorOperand, Value accumulator, LLVM::FastmathFlagsAttr fmf) {
743 Value result =
744 LLVMRedIntrinOp::create(rewriter, loc, llvmType, vectorOperand, fmf);
745
746 if (accumulator) {
747 result = VectorToScalarMapper<LLVMRedIntrinOp>::Type::create(
748 rewriter, loc, result, accumulator);
749 }
750
751 return result;
752}
753
754/// Mask neutral classes for overloading.
755class MaskNeutralFMaximumNum {};
756class MaskNeutralFMinimumNum {};
757
758/// Get the mask neutral value for a `fmaximumnum` reduction. `maximumnum`
759/// ignores NaN operands, making a quiet NaN the identity element. When `nnan`
760/// promises no NaN reaches the reduction, fall back to the smallest
761/// representable value, which `ninf` further constrains to be finite.
762static llvm::APFloat getMaskNeutralValue(MaskNeutralFMaximumNum,
763 const llvm::fltSemantics &semantics,
764 bool noNaNs, bool noInfs) {
765 if (!noNaNs)
766 return llvm::APFloat::getQNaN(semantics);
767 if (noInfs)
768 return llvm::APFloat::getLargest(semantics, /*Negative=*/true);
769 return llvm::APFloat::getInf(semantics, /*Negative=*/true);
770}
771
772/// Get the mask neutral value for a `fminimumnum` reduction. See the
773/// `fmaximumnum` overload above for the rationale.
774static llvm::APFloat getMaskNeutralValue(MaskNeutralFMinimumNum,
775 const llvm::fltSemantics &semantics,
776 bool noNaNs, bool noInfs) {
777 if (!noNaNs)
778 return llvm::APFloat::getQNaN(semantics);
779 if (noInfs)
780 return llvm::APFloat::getLargest(semantics);
781 return llvm::APFloat::getInf(semantics);
782}
783
784/// Lowers masked `fmaximumnum` and `fminimumnum` reductions using the
785/// non-masked intrinsics, since LLVM has no predicated counterpart for them.
786/// Inactive lanes are replaced by a mask neutral value before reducing.
787/// TODO: Switch to `lowerPredicatedReductionWithStartValue` once
788/// `llvm.vp.reduce.fmaximumnum`/`fminimumnum` are added to LLVM IR.
789template <class LLVMRedIntrinOp, class MaskNeutral>
790static Value
791lowerMaskedReductionWithRegular(ConversionPatternRewriter &rewriter,
792 Location loc, Type llvmType,
793 Value vectorOperand, Value accumulator,
794 Value mask, LLVM::FastmathFlagsAttr fmf) {
795 const auto &floatSemantics = cast<FloatType>(llvmType).getFloatSemantics();
796 auto value = getMaskNeutralValue(
797 MaskNeutral{}, floatSemantics,
798 LLVM::bitEnumContainsAny(fmf.getValue(), LLVM::FastmathFlags::nnan),
799 LLVM::bitEnumContainsAny(fmf.getValue(), LLVM::FastmathFlags::ninf));
800 Type vectorType = vectorOperand.getType();
801 auto denseValue = DenseElementsAttr::get(cast<ShapedType>(vectorType), value);
802 const Value vectorMaskNeutral =
803 LLVM::ConstantOp::create(rewriter, loc, vectorType, denseValue);
804 const Value selectedVectorByMask = LLVM::SelectOp::create(
805 rewriter, loc, mask, vectorOperand, vectorMaskNeutral);
806 return createFPReductionComparisonOpLowering<LLVMRedIntrinOp>(
807 rewriter, loc, llvmType, selectedVectorByMask, accumulator, fmf);
808}
809
810template <class LLVMRedIntrinOp, class ReductionNeutral>
811static Value
812lowerReductionWithStartValue(ConversionPatternRewriter &rewriter, Location loc,
813 Type llvmType, Value vectorOperand,
814 Value accumulator, LLVM::FastmathFlagsAttr fmf) {
815 accumulator = getOrCreateAccumulator<ReductionNeutral>(rewriter, loc,
816 llvmType, accumulator);
817 return LLVMRedIntrinOp::create(rewriter, loc, llvmType,
818 /*start_value=*/accumulator, vectorOperand,
819 fmf);
820}
821
822template <class LLVMVPRedIntrinOp, class ReductionNeutral>
823static Value lowerPredicatedReductionWithStartValue(
824 ConversionPatternRewriter &rewriter, Location loc, Type llvmType,
825 Value vectorOperand, Value accumulator, Value mask) {
826 accumulator = getOrCreateAccumulator<ReductionNeutral>(rewriter, loc,
827 llvmType, accumulator);
828 Value vectorLength =
829 createVectorLengthValue(rewriter, loc, vectorOperand.getType());
830 return LLVMVPRedIntrinOp::create(rewriter, loc, llvmType,
831 /*satrt_value=*/accumulator, vectorOperand,
832 mask, vectorLength);
833}
834
835template <class LLVMIntVPRedIntrinOp, class IntReductionNeutral,
836 class LLVMFPVPRedIntrinOp, class FPReductionNeutral>
837static Value lowerPredicatedReductionWithStartValue(
838 ConversionPatternRewriter &rewriter, Location loc, Type llvmType,
839 Value vectorOperand, Value accumulator, Value mask) {
840 if (llvmType.isIntOrIndex())
841 return lowerPredicatedReductionWithStartValue<LLVMIntVPRedIntrinOp,
842 IntReductionNeutral>(
843 rewriter, loc, llvmType, vectorOperand, accumulator, mask);
844
845 // FP dispatch.
846 return lowerPredicatedReductionWithStartValue<LLVMFPVPRedIntrinOp,
847 FPReductionNeutral>(
848 rewriter, loc, llvmType, vectorOperand, accumulator, mask);
849}
850
851/// Conversion pattern for all vector reductions.
852class VectorReductionOpConversion
853 : public ConvertOpToLLVMPattern<vector::ReductionOp> {
854public:
855 explicit VectorReductionOpConversion(const LLVMTypeConverter &typeConv,
856 bool reassociateFPRed)
857 : ConvertOpToLLVMPattern<vector::ReductionOp>(typeConv),
858 reassociateFPReductions(reassociateFPRed) {}
859
860 LogicalResult
861 matchAndRewrite(vector::ReductionOp reductionOp, OpAdaptor adaptor,
862 ConversionPatternRewriter &rewriter) const override {
863 auto kind = reductionOp.getKind();
864 Type eltType = reductionOp.getResult().getType();
865 Type llvmType = typeConverter->convertType(eltType);
866 Value operand = adaptor.getVector();
867 Value acc = adaptor.getAcc();
868 Location loc = reductionOp.getLoc();
869
870 if (eltType.isIntOrIndex()) {
871 // Integer reductions: add/mul/min/max/and/or/xor.
872 Value result;
873 switch (kind) {
874 case vector::CombiningKind::ADD:
875 result =
876 createIntegerReductionArithmeticOpLowering<LLVM::vector_reduce_add,
877 LLVM::AddOp>(
878 rewriter, loc, llvmType, operand, acc);
879 break;
880 case vector::CombiningKind::MUL:
881 result =
882 createIntegerReductionArithmeticOpLowering<LLVM::vector_reduce_mul,
883 LLVM::MulOp>(
884 rewriter, loc, llvmType, operand, acc);
885 break;
886 case vector::CombiningKind::MINUI:
887 result = createIntegerReductionComparisonOpLowering<
888 LLVM::vector_reduce_umin>(rewriter, loc, llvmType, operand, acc,
889 LLVM::ICmpPredicate::ule);
890 break;
891 case vector::CombiningKind::MINSI:
892 result = createIntegerReductionComparisonOpLowering<
893 LLVM::vector_reduce_smin>(rewriter, loc, llvmType, operand, acc,
894 LLVM::ICmpPredicate::sle);
895 break;
896 case vector::CombiningKind::MAXUI:
897 result = createIntegerReductionComparisonOpLowering<
898 LLVM::vector_reduce_umax>(rewriter, loc, llvmType, operand, acc,
899 LLVM::ICmpPredicate::uge);
900 break;
901 case vector::CombiningKind::MAXSI:
902 result = createIntegerReductionComparisonOpLowering<
903 LLVM::vector_reduce_smax>(rewriter, loc, llvmType, operand, acc,
904 LLVM::ICmpPredicate::sge);
905 break;
906 case vector::CombiningKind::AND:
907 result =
908 createIntegerReductionArithmeticOpLowering<LLVM::vector_reduce_and,
909 LLVM::AndOp>(
910 rewriter, loc, llvmType, operand, acc);
911 break;
912 case vector::CombiningKind::OR:
913 result =
914 createIntegerReductionArithmeticOpLowering<LLVM::vector_reduce_or,
915 LLVM::OrOp>(
916 rewriter, loc, llvmType, operand, acc);
917 break;
918 case vector::CombiningKind::XOR:
919 result =
920 createIntegerReductionArithmeticOpLowering<LLVM::vector_reduce_xor,
921 LLVM::XOrOp>(
922 rewriter, loc, llvmType, operand, acc);
923 break;
924 default:
925 return failure();
926 }
927 rewriter.replaceOp(reductionOp, result);
928
929 return success();
930 }
931
932 if (!isa<FloatType>(eltType))
933 return failure();
934
935 arith::FastMathFlagsAttr fMFAttr = reductionOp.getFastMathFlagsAttr();
936 LLVM::FastmathFlagsAttr fmf = LLVM::FastmathFlagsAttr::get(
937 reductionOp.getContext(),
938 convertArithFastMathFlagsToLLVM(fMFAttr.getValue()));
939 fmf = LLVM::FastmathFlagsAttr::get(
940 reductionOp.getContext(),
941 fmf.getValue() | (reassociateFPReductions ? LLVM::FastmathFlags::reassoc
942 : LLVM::FastmathFlags::none));
943
944 // Floating-point reductions: add/mul/min/max
945 Value result;
946 switch (kind) {
947 case vector::CombiningKind::ADD:
948 result = lowerReductionWithStartValue<LLVM::vector_reduce_fadd,
949 ReductionNeutralZero>(
950 rewriter, loc, llvmType, operand, acc, fmf);
951 break;
952 case vector::CombiningKind::MUL:
953 result = lowerReductionWithStartValue<LLVM::vector_reduce_fmul,
954 ReductionNeutralFPOne>(
955 rewriter, loc, llvmType, operand, acc, fmf);
956 break;
957 case vector::CombiningKind::MINIMUMF:
958 result =
959 createFPReductionComparisonOpLowering<LLVM::vector_reduce_fminimum>(
960 rewriter, loc, llvmType, operand, acc, fmf);
961 break;
962 case vector::CombiningKind::MAXIMUMF:
963 result =
964 createFPReductionComparisonOpLowering<LLVM::vector_reduce_fmaximum>(
965 rewriter, loc, llvmType, operand, acc, fmf);
966 break;
967 case vector::CombiningKind::MINNUMF:
968 result = createFPReductionComparisonOpLowering<LLVM::vector_reduce_fmin>(
969 rewriter, loc, llvmType, operand, acc, fmf);
970 break;
971 case vector::CombiningKind::MAXNUMF:
972 result = createFPReductionComparisonOpLowering<LLVM::vector_reduce_fmax>(
973 rewriter, loc, llvmType, operand, acc, fmf);
974 break;
975 case vector::CombiningKind::MAXIMUMNUMF:
976 result = createFPReductionComparisonOpLowering<
977 LLVM::vector_reduce_fmaximumnum>(rewriter, loc, llvmType, operand,
978 acc, fmf);
979 break;
980 case vector::CombiningKind::MINIMUMNUMF:
981 result = createFPReductionComparisonOpLowering<
982 LLVM::vector_reduce_fminimumnum>(rewriter, loc, llvmType, operand,
983 acc, fmf);
984 break;
985 default:
986 return failure();
987 }
988
989 rewriter.replaceOp(reductionOp, result);
990 return success();
991 }
992
993private:
994 const bool reassociateFPReductions;
995};
996
997/// Base class to convert a `vector.mask` operation while matching traits
998/// of the maskable operation nested inside. A `VectorMaskOpConversionBase`
999/// instance matches against a `vector.mask` operation. The `matchAndRewrite`
1000/// method performs a second match against the maskable operation `MaskedOp`.
1001/// Finally, it invokes the virtual method `matchAndRewriteMaskableOp` to be
1002/// implemented by the concrete conversion classes. This method can match
1003/// against specific traits of the `vector.mask` and the maskable operation. It
1004/// must replace the `vector.mask` operation.
1005template <class MaskedOp>
1006class VectorMaskOpConversionBase
1007 : public ConvertOpToLLVMPattern<vector::MaskOp> {
1008public:
1009 using ConvertOpToLLVMPattern<vector::MaskOp>::ConvertOpToLLVMPattern;
1010
1011 LogicalResult
1012 matchAndRewrite(vector::MaskOp maskOp, OpAdaptor adaptor,
1013 ConversionPatternRewriter &rewriter) const final {
1014 // Match against the maskable operation kind.
1015 auto maskedOp = llvm::dyn_cast_or_null<MaskedOp>(maskOp.getMaskableOp());
1016 if (!maskedOp)
1017 return failure();
1018 return matchAndRewriteMaskableOp(maskOp, maskedOp, rewriter);
1019 }
1020
1021protected:
1022 virtual LogicalResult
1023 matchAndRewriteMaskableOp(vector::MaskOp maskOp,
1024 vector::MaskableOpInterface maskableOp,
1025 ConversionPatternRewriter &rewriter) const = 0;
1026};
1027
1028class MaskedReductionOpConversion
1029 : public VectorMaskOpConversionBase<vector::ReductionOp> {
1030
1031public:
1032 using VectorMaskOpConversionBase<
1033 vector::ReductionOp>::VectorMaskOpConversionBase;
1034
1035 LogicalResult matchAndRewriteMaskableOp(
1036 vector::MaskOp maskOp, MaskableOpInterface maskableOp,
1037 ConversionPatternRewriter &rewriter) const override {
1038 auto reductionOp = cast<ReductionOp>(maskableOp.getOperation());
1039 auto kind = reductionOp.getKind();
1040 Type eltType = reductionOp.getResult().getType();
1041 Type llvmType = typeConverter->convertType(eltType);
1042 Value operand = reductionOp.getVector();
1043 Value acc = reductionOp.getAcc();
1044 Location loc = reductionOp.getLoc();
1045
1046 arith::FastMathFlagsAttr fMFAttr = reductionOp.getFastMathFlagsAttr();
1047 LLVM::FastmathFlagsAttr fmf = LLVM::FastmathFlagsAttr::get(
1048 reductionOp.getContext(),
1049 convertArithFastMathFlagsToLLVM(fMFAttr.getValue()));
1050 const bool noInfs =
1051 LLVM::bitEnumContainsAny(fmf.getValue(), LLVM::FastmathFlags::ninf);
1052
1053 Value result;
1054 switch (kind) {
1055 case vector::CombiningKind::ADD:
1056 result = lowerPredicatedReductionWithStartValue<
1057 LLVM::VPReduceAddOp, ReductionNeutralZero, LLVM::VPReduceFAddOp,
1058 ReductionNeutralZero>(rewriter, loc, llvmType, operand, acc,
1059 maskOp.getMask());
1060 break;
1061 case vector::CombiningKind::MUL:
1062 result = lowerPredicatedReductionWithStartValue<
1063 LLVM::VPReduceMulOp, ReductionNeutralIntOne, LLVM::VPReduceFMulOp,
1064 ReductionNeutralFPOne>(rewriter, loc, llvmType, operand, acc,
1065 maskOp.getMask());
1066 break;
1067 case vector::CombiningKind::MINUI:
1068 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceUMinOp,
1069 ReductionNeutralUIntMax>(
1070 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1071 break;
1072 case vector::CombiningKind::MINSI:
1073 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceSMinOp,
1074 ReductionNeutralSIntMax>(
1075 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1076 break;
1077 case vector::CombiningKind::MAXUI:
1078 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceUMaxOp,
1079 ReductionNeutralUIntMin>(
1080 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1081 break;
1082 case vector::CombiningKind::MAXSI:
1083 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceSMaxOp,
1084 ReductionNeutralSIntMin>(
1085 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1086 break;
1087 case vector::CombiningKind::AND:
1088 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceAndOp,
1089 ReductionNeutralAllOnes>(
1090 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1091 break;
1092 case vector::CombiningKind::OR:
1093 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceOrOp,
1094 ReductionNeutralZero>(
1095 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1096 break;
1097 case vector::CombiningKind::XOR:
1098 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceXorOp,
1099 ReductionNeutralZero>(
1100 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1101 break;
1102 case vector::CombiningKind::MINNUMF:
1103 result =
1104 lowerPredicatedReductionWithStartValue<LLVM::VPReduceFMinOp,
1105 ReductionNeutralFPNegQNaN>(
1106 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1107 break;
1108 case vector::CombiningKind::MAXNUMF:
1109 result = lowerPredicatedReductionWithStartValue<LLVM::VPReduceFMaxOp,
1110 ReductionNeutralFPQNaN>(
1111 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1112 break;
1113 case CombiningKind::MAXIMUMF:
1114 // `ninf` promises no infinity reaches the reduction, so the neutral start
1115 // value must stay finite.
1116 result =
1117 noInfs
1118 ? lowerPredicatedReductionWithStartValue<
1119 LLVM::VPReduceFMaximumOp, ReductionNeutralFPLowestFinite>(
1120 rewriter, loc, llvmType, operand, acc, maskOp.getMask())
1121 : lowerPredicatedReductionWithStartValue<
1122 LLVM::VPReduceFMaximumOp, ReductionNeutralFPNegInf>(
1123 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1124 break;
1125 case CombiningKind::MINIMUMF:
1126 result =
1127 noInfs
1128 ? lowerPredicatedReductionWithStartValue<
1129 LLVM::VPReduceFMinimumOp, ReductionNeutralFPLargestFinite>(
1130 rewriter, loc, llvmType, operand, acc, maskOp.getMask())
1131 : lowerPredicatedReductionWithStartValue<
1132 LLVM::VPReduceFMinimumOp, ReductionNeutralFPPosInf>(
1133 rewriter, loc, llvmType, operand, acc, maskOp.getMask());
1134 break;
1135 case CombiningKind::MAXIMUMNUMF:
1136 result = lowerMaskedReductionWithRegular<LLVM::vector_reduce_fmaximumnum,
1137 MaskNeutralFMaximumNum>(
1138 rewriter, loc, llvmType, operand, acc, maskOp.getMask(), fmf);
1139 break;
1140 case CombiningKind::MINIMUMNUMF:
1141 result = lowerMaskedReductionWithRegular<LLVM::vector_reduce_fminimumnum,
1142 MaskNeutralFMinimumNum>(
1143 rewriter, loc, llvmType, operand, acc, maskOp.getMask(), fmf);
1144 break;
1145 }
1146
1147 // Replace `vector.mask` operation altogether.
1148 rewriter.replaceOp(maskOp, result);
1149 return success();
1150 }
1151};
1152
1153class VectorShuffleOpConversion
1154 : public ConvertOpToLLVMPattern<vector::ShuffleOp> {
1155public:
1156 using ConvertOpToLLVMPattern<vector::ShuffleOp>::ConvertOpToLLVMPattern;
1157
1158 LogicalResult
1159 matchAndRewrite(vector::ShuffleOp shuffleOp, OpAdaptor adaptor,
1160 ConversionPatternRewriter &rewriter) const override {
1161 auto loc = shuffleOp->getLoc();
1162 auto v1Type = shuffleOp.getV1VectorType();
1163 auto v2Type = shuffleOp.getV2VectorType();
1164 auto vectorType = shuffleOp.getResultVectorType();
1165 Type llvmType = typeConverter->convertType(vectorType);
1166 ArrayRef<int64_t> mask = shuffleOp.getMask();
1167
1168 // Bail if result type cannot be lowered.
1169 if (!llvmType)
1170 return failure();
1171
1172 // Get rank and dimension sizes.
1173 int64_t rank = vectorType.getRank();
1174#ifndef NDEBUG
1175 bool wellFormed0DCase =
1176 v1Type.getRank() == 0 && v2Type.getRank() == 0 && rank == 1;
1177 bool wellFormedNDCase =
1178 v1Type.getRank() == rank && v2Type.getRank() == rank;
1179 assert((wellFormed0DCase || wellFormedNDCase) && "op is not well-formed");
1180#endif
1181
1182 // For rank 0 and 1, where both operands have *exactly* the same vector
1183 // type, there is direct shuffle support in LLVM. Use it!
1184 if (rank <= 1 && v1Type == v2Type) {
1185 Value llvmShuffleOp = LLVM::ShuffleVectorOp::create(
1186 rewriter, loc, adaptor.getV1(), adaptor.getV2(),
1187 llvm::to_vector_of<int32_t>(mask));
1188 rewriter.replaceOp(shuffleOp, llvmShuffleOp);
1189 return success();
1190 }
1191
1192 // For all other cases, insert the individual values individually.
1193 int64_t v1Dim = v1Type.getDimSize(0);
1194 Type eltType;
1195 if (auto arrayType = dyn_cast<LLVM::LLVMArrayType>(llvmType))
1196 eltType = arrayType.getElementType();
1197 else
1198 eltType = cast<VectorType>(llvmType).getElementType();
1199 Value insert = LLVM::PoisonOp::create(rewriter, loc, llvmType);
1200 int64_t insPos = 0;
1201 for (int64_t extPos : mask) {
1202 // The result starts as poison, so a poison index needs no insertion.
1203 if (extPos == vector::ShuffleOp::kPoisonIndex) {
1204 ++insPos;
1205 continue;
1206 }
1207 Value value = adaptor.getV1();
1208 if (extPos >= v1Dim) {
1209 extPos -= v1Dim;
1210 value = adaptor.getV2();
1211 }
1212 Value extract = extractOne(rewriter, *getTypeConverter(), loc, value,
1213 eltType, rank, extPos);
1214 insert = insertOne(rewriter, *getTypeConverter(), loc, insert, extract,
1215 llvmType, rank, insPos++);
1216 }
1217 rewriter.replaceOp(shuffleOp, insert);
1218 return success();
1219 }
1220};
1221
1222class VectorExtractOpConversion
1223 : public ConvertOpToLLVMPattern<vector::ExtractOp> {
1224public:
1225 using ConvertOpToLLVMPattern<vector::ExtractOp>::ConvertOpToLLVMPattern;
1226
1227 LogicalResult
1228 matchAndRewrite(vector::ExtractOp extractOp, OpAdaptor adaptor,
1229 ConversionPatternRewriter &rewriter) const override {
1230 auto loc = extractOp->getLoc();
1231 auto resultType = extractOp.getResult().getType();
1232 auto llvmResultType = typeConverter->convertType(resultType);
1233 // Bail if result type cannot be lowered.
1234 if (!llvmResultType)
1235 return failure();
1236
1237 SmallVector<OpFoldResult> positionVec = getMixedValues(
1238 adaptor.getStaticPosition(), adaptor.getDynamicPosition(), rewriter);
1239
1240 // The Vector -> LLVM lowering models N-D vectors as nested aggregates of
1241 // 1-d vectors. This nesting is modeled using arrays. We do this conversion
1242 // from a N-d vector extract to a nested aggregate vector extract in two
1243 // steps:
1244 // - Extract a member from the nested aggregate. The result can be
1245 // a lower rank nested aggregate or a vector (1-D). This is done using
1246 // `llvm.extractvalue`.
1247 // - Extract a scalar out of the vector if needed. This is done using
1248 // `llvm.extractelement`.
1249
1250 // Determine if we need to extract a member out of the aggregate. We
1251 // always need to extract a member if the input rank >= 2.
1252 bool extractsAggregate = extractOp.getSourceVectorType().getRank() >= 2;
1253 // Determine if we need to extract a scalar as the result. We extract
1254 // a scalar if the extract is full rank, i.e., the number of indices is
1255 // equal to source vector rank.
1256 bool extractsScalar = static_cast<int64_t>(positionVec.size()) ==
1257 extractOp.getSourceVectorType().getRank();
1258
1259 // Since the LLVM type converter converts 0-d vectors to 1-d vectors, we
1260 // need to add a position for this change.
1261 if (extractOp.getSourceVectorType().getRank() == 0) {
1262 Type idxType = typeConverter->convertType(rewriter.getIndexType());
1263 positionVec.push_back(rewriter.getZeroAttr(idxType));
1264 }
1265
1266 Value extracted = adaptor.getSource();
1267 if (extractsAggregate) {
1268 ArrayRef<OpFoldResult> position(positionVec);
1269 if (extractsScalar) {
1270 // If we are extracting a scalar from the extracted member, we drop
1271 // the last index, which will be used to extract the scalar out of the
1272 // vector.
1273 position = position.drop_back();
1274 }
1275 // llvm.extractvalue does not support dynamic dimensions.
1276 if (!llvm::all_of(position, llvm::IsaPred<Attribute>)) {
1277 return failure();
1278 }
1279 extracted = LLVM::ExtractValueOp::create(rewriter, loc, extracted,
1280 getAsIntegers(position));
1281 }
1282
1283 if (extractsScalar) {
1284 extracted = LLVM::ExtractElementOp::create(
1285 rewriter, loc, extracted,
1286 getAsLLVMValue(rewriter, loc, positionVec.back()));
1287 }
1288
1289 rewriter.replaceOp(extractOp, extracted);
1290 return success();
1291 }
1292};
1293
1294/// Conversion pattern that turns a vector.fma on a 1-D vector
1295/// into an llvm.intr.fmuladd. This is a trivial 1-1 conversion.
1296/// This does not match vectors of n >= 2 rank.
1297///
1298/// Example:
1299/// ```
1300/// vector.fma %a, %a, %a : vector<8xf32>
1301/// ```
1302/// is converted to:
1303/// ```
1304/// llvm.intr.fmuladd %va, %va, %va:
1305/// (!llvm."<8 x f32>">, !llvm<"<8 x f32>">, !llvm<"<8 x f32>">)
1306/// -> !llvm."<8 x f32>">
1307/// ```
1308class VectorFMAOp1DConversion : public ConvertOpToLLVMPattern<vector::FMAOp> {
1309public:
1310 using ConvertOpToLLVMPattern<vector::FMAOp>::ConvertOpToLLVMPattern;
1311
1312 LogicalResult
1313 matchAndRewrite(vector::FMAOp fmaOp, OpAdaptor adaptor,
1314 ConversionPatternRewriter &rewriter) const override {
1315 VectorType vType = fmaOp.getVectorType();
1316 if (vType.getRank() > 1)
1317 return failure();
1318
1319 rewriter.replaceOpWithNewOp<LLVM::FMulAddOp>(
1320 fmaOp, adaptor.getLhs(), adaptor.getRhs(), adaptor.getAcc());
1321 return success();
1322 }
1323};
1324
1325class VectorInsertOpConversion
1326 : public ConvertOpToLLVMPattern<vector::InsertOp> {
1327public:
1328 using ConvertOpToLLVMPattern<vector::InsertOp>::ConvertOpToLLVMPattern;
1329
1330 LogicalResult
1331 matchAndRewrite(vector::InsertOp insertOp, OpAdaptor adaptor,
1332 ConversionPatternRewriter &rewriter) const override {
1333 auto loc = insertOp->getLoc();
1334 auto destVectorType = insertOp.getDestVectorType();
1335 auto llvmResultType = typeConverter->convertType(destVectorType);
1336 // Bail if result type cannot be lowered.
1337 if (!llvmResultType)
1338 return failure();
1339
1340 SmallVector<OpFoldResult> positionVec = getMixedValues(
1341 adaptor.getStaticPosition(), adaptor.getDynamicPosition(), rewriter);
1342
1343 // The logic in this pattern mirrors VectorExtractOpConversion. Refer to
1344 // its explanatory comment about how N-D vectors are converted as nested
1345 // aggregates (llvm.array's) of 1D vectors.
1346 //
1347 // The innermost dimension of the destination vector, when converted to a
1348 // nested aggregate form, will always be a 1D vector.
1349 //
1350 // * If the insertion is happening into the innermost dimension of the
1351 // destination vector:
1352 // - If the destination is a nested aggregate, extract a 1D vector out of
1353 // the aggregate. This can be done using llvm.extractvalue. The
1354 // destination is now guaranteed to be a 1D vector, to which we are
1355 // inserting.
1356 // - Do the insertion into the 1D destination vector, and make the result
1357 // the new source nested aggregate. This can be done using
1358 // llvm.insertelement.
1359 // * Insert the source nested aggregate into the destination nested
1360 // aggregate.
1361
1362 // Determine if we need to extract/insert a 1D vector out of the aggregate.
1363 bool isNestedAggregate = isa<LLVM::LLVMArrayType>(llvmResultType);
1364 // Determine if we need to insert a scalar into the 1D vector.
1365 bool insertIntoInnermostDim =
1366 static_cast<int64_t>(positionVec.size()) == destVectorType.getRank();
1367
1368 ArrayRef<OpFoldResult> positionOf1DVectorWithinAggregate(
1369 positionVec.begin(),
1370 insertIntoInnermostDim ? positionVec.size() - 1 : positionVec.size());
1371 OpFoldResult positionOfScalarWithin1DVector;
1372 if (destVectorType.getRank() == 0) {
1373 // Since the LLVM type converter converts 0D vectors to 1D vectors, we
1374 // need to create a 0 here as the position into the 1D vector.
1375 Type idxType = typeConverter->convertType(rewriter.getIndexType());
1376 positionOfScalarWithin1DVector = rewriter.getZeroAttr(idxType);
1377 } else if (insertIntoInnermostDim) {
1378 positionOfScalarWithin1DVector = positionVec.back();
1379 }
1380
1381 // We are going to mutate this 1D vector until it is either the final
1382 // result (in the non-aggregate case) or the value that needs to be
1383 // inserted into the aggregate result.
1384 Value sourceAggregate = adaptor.getValueToStore();
1385 if (insertIntoInnermostDim) {
1386 // Scalar-into-1D-vector case, so we know we will have to create a
1387 // InsertElementOp. The question is into what destination.
1388 if (isNestedAggregate) {
1389 // Aggregate case: the destination for the InsertElementOp needs to be
1390 // extracted from the aggregate.
1391 if (!llvm::all_of(positionOf1DVectorWithinAggregate,
1392 llvm::IsaPred<Attribute>)) {
1393 // llvm.extractvalue does not support dynamic dimensions.
1394 return failure();
1395 }
1396 sourceAggregate = LLVM::ExtractValueOp::create(
1397 rewriter, loc, adaptor.getDest(),
1398 getAsIntegers(positionOf1DVectorWithinAggregate));
1399 } else {
1400 // No-aggregate case. The destination for the InsertElementOp is just
1401 // the insertOp's destination.
1402 sourceAggregate = adaptor.getDest();
1403 }
1404 // Insert the scalar into the 1D vector.
1405 sourceAggregate = LLVM::InsertElementOp::create(
1406 rewriter, loc, sourceAggregate.getType(), sourceAggregate,
1407 adaptor.getValueToStore(),
1408 getAsLLVMValue(rewriter, loc, positionOfScalarWithin1DVector));
1409 }
1410
1411 Value result = sourceAggregate;
1412 if (isNestedAggregate) {
1413 if (!llvm::all_of(positionOf1DVectorWithinAggregate,
1414 llvm::IsaPred<Attribute>)) {
1415 // llvm.insertvalue does not support dynamic dimensions.
1416 return failure();
1417 }
1418 result = LLVM::InsertValueOp::create(
1419 rewriter, loc, adaptor.getDest(), sourceAggregate,
1420 getAsIntegers(positionOf1DVectorWithinAggregate));
1421 }
1422
1423 rewriter.replaceOp(insertOp, result);
1424 return success();
1425 }
1426};
1427
1428/// Lower vector.scalable.insert ops to LLVM vector.insert
1429struct VectorScalableInsertOpLowering
1430 : public ConvertOpToLLVMPattern<vector::ScalableInsertOp> {
1431 using ConvertOpToLLVMPattern<
1432 vector::ScalableInsertOp>::ConvertOpToLLVMPattern;
1433
1434 LogicalResult
1435 matchAndRewrite(vector::ScalableInsertOp insOp, OpAdaptor adaptor,
1436 ConversionPatternRewriter &rewriter) const override {
1437 rewriter.replaceOpWithNewOp<LLVM::vector_insert>(
1438 insOp, adaptor.getDest(), adaptor.getValueToStore(), adaptor.getPos());
1439 return success();
1440 }
1441};
1442
1443/// Lower vector.scalable.extract ops to LLVM vector.extract
1444struct VectorScalableExtractOpLowering
1445 : public ConvertOpToLLVMPattern<vector::ScalableExtractOp> {
1446 using ConvertOpToLLVMPattern<
1447 vector::ScalableExtractOp>::ConvertOpToLLVMPattern;
1448
1449 LogicalResult
1450 matchAndRewrite(vector::ScalableExtractOp extOp, OpAdaptor adaptor,
1451 ConversionPatternRewriter &rewriter) const override {
1452 rewriter.replaceOpWithNewOp<LLVM::vector_extract>(
1453 extOp, typeConverter->convertType(extOp.getResultVectorType()),
1454 adaptor.getSource(), adaptor.getPos());
1455 return success();
1456 }
1457};
1458
1459/// Rank reducing rewrite for n-D FMA into (n-1)-D FMA where n > 1.
1460///
1461/// Example:
1462/// ```
1463/// %d = vector.fma %a, %b, %c : vector<2x4xf32>
1464/// ```
1465/// is rewritten into:
1466/// ```
1467/// %r = vector.broadcast %f0 : f32 to vector<2x4xf32>
1468/// %va = vector.extractvalue %a[0] : vector<2x4xf32>
1469/// %vb = vector.extractvalue %b[0] : vector<2x4xf32>
1470/// %vc = vector.extractvalue %c[0] : vector<2x4xf32>
1471/// %vd = vector.fma %va, %vb, %vc : vector<4xf32>
1472/// %r2 = vector.insertvalue %vd, %r[0] : vector<4xf32> into vector<2x4xf32>
1473/// %va2 = vector.extractvalue %a2[1] : vector<2x4xf32>
1474/// %vb2 = vector.extractvalue %b2[1] : vector<2x4xf32>
1475/// %vc2 = vector.extractvalue %c2[1] : vector<2x4xf32>
1476/// %vd2 = vector.fma %va2, %vb2, %vc2 : vector<4xf32>
1477/// %r3 = vector.insertvalue %vd2, %r2[1] : vector<4xf32> into vector<2x4xf32>
1478/// // %r3 holds the final value.
1479/// ```
1480class VectorFMAOpNDRewritePattern : public OpRewritePattern<FMAOp> {
1481public:
1482 using Base::Base;
1483
1484 void initialize() {
1485 // This pattern recursively unpacks one dimension at a time. The recursion
1486 // bounded as the rank is strictly decreasing.
1487 setHasBoundedRewriteRecursion();
1488 }
1489
1490 LogicalResult matchAndRewrite(FMAOp op,
1491 PatternRewriter &rewriter) const override {
1492 auto vType = op.getVectorType();
1493 if (vType.getRank() < 2)
1494 return failure();
1495
1496 auto loc = op.getLoc();
1497 auto elemType = vType.getElementType();
1498 Value zero = arith::ConstantOp::create(rewriter, loc, elemType,
1499 rewriter.getZeroAttr(elemType));
1500 Value desc = vector::BroadcastOp::create(rewriter, loc, vType, zero);
1501 for (int64_t i = 0, e = vType.getShape().front(); i != e; ++i) {
1502 Value extrLHS = ExtractOp::create(rewriter, loc, op.getLhs(), i);
1503 Value extrRHS = ExtractOp::create(rewriter, loc, op.getRhs(), i);
1504 Value extrACC = ExtractOp::create(rewriter, loc, op.getAcc(), i);
1505 Value fma = FMAOp::create(rewriter, loc, extrLHS, extrRHS, extrACC);
1506 desc = InsertOp::create(rewriter, loc, fma, desc, i);
1507 }
1508 rewriter.replaceOp(op, desc);
1509 return success();
1510 }
1511};
1512
1513/// Returns the strides if the memory underlying `memRefType` has a contiguous
1514/// static layout.
1515static std::optional<SmallVector<int64_t, 4>>
1516computeContiguousStrides(MemRefType memRefType) {
1517 int64_t offset;
1519 if (failed(memRefType.getStridesAndOffset(strides, offset)))
1520 return std::nullopt;
1521 if (!strides.empty() && strides.back() != 1)
1522 return std::nullopt;
1523 // If no layout or identity layout, this is contiguous by definition.
1524 if (memRefType.getLayout().isIdentity())
1525 return strides;
1526
1527 // Otherwise, we must determine contiguity form shapes. This can only ever
1528 // work in static cases because MemRefType is underspecified to represent
1529 // contiguous dynamic shapes in other ways than with just empty/identity
1530 // layout.
1531 auto sizes = memRefType.getShape();
1532 for (int index = 0, e = strides.size() - 1; index < e; ++index) {
1533 if (ShapedType::isDynamic(sizes[index + 1]) ||
1534 ShapedType::isDynamic(strides[index]) ||
1535 ShapedType::isDynamic(strides[index + 1]))
1536 return std::nullopt;
1537 if (strides[index] != strides[index + 1] * sizes[index + 1])
1538 return std::nullopt;
1539 }
1540 return strides;
1541}
1542
1543class VectorTypeCastOpConversion
1544 : public ConvertOpToLLVMPattern<vector::TypeCastOp> {
1545public:
1546 using ConvertOpToLLVMPattern<vector::TypeCastOp>::ConvertOpToLLVMPattern;
1547
1548 LogicalResult
1549 matchAndRewrite(vector::TypeCastOp castOp, OpAdaptor adaptor,
1550 ConversionPatternRewriter &rewriter) const override {
1551 auto loc = castOp->getLoc();
1552 MemRefType sourceMemRefType =
1553 cast<MemRefType>(castOp.getOperand().getType());
1554 MemRefType targetMemRefType = castOp.getType();
1555
1556 // Only static shape casts supported atm.
1557 if (!sourceMemRefType.hasStaticShape() ||
1558 !targetMemRefType.hasStaticShape())
1559 return failure();
1560
1561 auto llvmSourceDescriptorTy =
1562 dyn_cast<LLVM::LLVMStructType>(adaptor.getOperands()[0].getType());
1563 if (!llvmSourceDescriptorTy)
1564 return failure();
1565 MemRefDescriptor sourceMemRef(adaptor.getOperands()[0]);
1566
1567 auto llvmTargetDescriptorTy = dyn_cast_or_null<LLVM::LLVMStructType>(
1568 typeConverter->convertType(targetMemRefType));
1569 if (!llvmTargetDescriptorTy)
1570 return failure();
1571
1572 // Only contiguous source buffers supported atm.
1573 auto sourceStrides = computeContiguousStrides(sourceMemRefType);
1574 if (!sourceStrides)
1575 return failure();
1576 auto targetStrides = computeContiguousStrides(targetMemRefType);
1577 if (!targetStrides)
1578 return failure();
1579 // Only support static strides for now, regardless of contiguity.
1580 if (llvm::any_of(*targetStrides, ShapedType::isDynamic))
1581 return failure();
1582
1583 // The offset, size and stride fields of a memref descriptor use the
1584 // converted index type.
1585 Type indexTy = getTypeConverter()->getIndexType();
1586
1587 // Create descriptor.
1588 auto desc = MemRefDescriptor::poison(rewriter, loc, llvmTargetDescriptorTy);
1589 // Set allocated ptr.
1590 Value allocated = sourceMemRef.allocatedPtr(rewriter, loc);
1591 desc.setAllocatedPtr(rewriter, loc, allocated);
1592
1593 // Set aligned ptr.
1594 Value ptr = sourceMemRef.alignedPtr(rewriter, loc);
1595 desc.setAlignedPtr(rewriter, loc, ptr);
1596 // Fill offset 0.
1597 desc.setOffset(rewriter, loc,
1598 LLVM::createIndexAttrConstant(rewriter, loc, indexTy, 0));
1599
1600 // Fill size and stride descriptors in memref.
1601 for (const auto &indexedSize :
1602 llvm::enumerate(targetMemRefType.getShape())) {
1603 int64_t index = indexedSize.index();
1604 desc.setSize(rewriter, loc, index,
1605 LLVM::createIndexAttrConstant(rewriter, loc, indexTy,
1606 indexedSize.value()));
1607 desc.setStride(rewriter, loc, index,
1608 LLVM::createIndexAttrConstant(rewriter, loc, indexTy,
1609 (*targetStrides)[index]));
1610 }
1611
1612 rewriter.replaceOp(castOp, {desc});
1613 return success();
1614 }
1615};
1616
1617/// Conversion pattern for a `vector.create_mask` (1-D scalable vectors only).
1618/// Non-scalable versions of this operation are handled in Vector Transforms.
1619class VectorCreateMaskOpConversion
1620 : public OpConversionPattern<vector::CreateMaskOp> {
1621public:
1622 explicit VectorCreateMaskOpConversion(MLIRContext *context,
1623 bool enableIndexOpt)
1624 : OpConversionPattern<vector::CreateMaskOp>(context),
1625 force32BitVectorIndices(enableIndexOpt) {}
1626
1627 LogicalResult
1628 matchAndRewrite(vector::CreateMaskOp op, OpAdaptor adaptor,
1629 ConversionPatternRewriter &rewriter) const override {
1630 auto dstType = op.getType();
1631 if (dstType.getRank() != 1 || !cast<VectorType>(dstType).isScalable())
1632 return failure();
1633 IntegerType idxType =
1634 force32BitVectorIndices ? rewriter.getI32Type() : rewriter.getI64Type();
1635 auto loc = op->getLoc();
1636 Value indices = LLVM::StepVectorOp::create(
1637 rewriter, loc,
1638 LLVM::getVectorType(idxType, dstType.getShape()[0],
1639 /*isScalable=*/true));
1640 Value maskBound = adaptor.getOperands()[0];
1641 // When using 32-bit indices, cap the bound at INT32_MAX in index type
1642 // before casting. For scalable vectors the runtime size (vscale * dim) is
1643 // unknown at compile time, so we can't clamp to `dim` as in the fixed-size
1644 // path. Clamping to INT32_MAX is safe because any realistic scalable vector
1645 // size fits well below this limit, so a bound >= vscale*dim still produces
1646 // an all-true mask after the comparison.
1647 if (force32BitVectorIndices) {
1648 Value maxBound =
1649 arith::ConstantIndexOp::create(rewriter, loc, (1LL << 31) - 1);
1650 maskBound = arith::MinSIOp::create(rewriter, loc, maskBound, maxBound);
1651 }
1652 auto bound =
1653 getValueOrCreateCastToIndexLike(rewriter, loc, idxType, maskBound);
1654 Value bounds = BroadcastOp::create(rewriter, loc, indices.getType(), bound);
1655 Value comp = arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::slt,
1656 indices, bounds);
1657 rewriter.replaceOp(op, comp);
1658 return success();
1659 }
1660
1661private:
1662 const bool force32BitVectorIndices;
1663};
1664
1665class VectorPrintOpConversion : public ConvertOpToLLVMPattern<vector::PrintOp> {
1666 SymbolTableCollection *symbolTables = nullptr;
1667
1668public:
1669 explicit VectorPrintOpConversion(
1670 const LLVMTypeConverter &typeConverter,
1671 SymbolTableCollection *symbolTables = nullptr)
1672 : ConvertOpToLLVMPattern<vector::PrintOp>(typeConverter),
1673 symbolTables(symbolTables) {}
1674
1675 // Lowering implementation that relies on a small runtime support library,
1676 // which only needs to provide a few printing methods (single value for all
1677 // data types, opening/closing bracket, comma, newline). The lowering splits
1678 // the vector into elementary printing operations. The advantage of this
1679 // approach is that the library can remain unaware of all low-level
1680 // implementation details of vectors while still supporting output of any
1681 // shaped and dimensioned vector.
1682 //
1683 // Note: This lowering only handles scalars, n-D vectors are broken into
1684 // printing scalars in loops in VectorToSCF.
1685 //
1686 // TODO: rely solely on libc in future? something else?
1687 //
1688 LogicalResult
1689 matchAndRewrite(vector::PrintOp printOp, OpAdaptor adaptor,
1690 ConversionPatternRewriter &rewriter) const override {
1691 auto parent = printOp->getParentOfType<ModuleOp>();
1692 if (!parent)
1693 return failure();
1694
1695 auto loc = printOp->getLoc();
1696
1697 if (auto value = adaptor.getSource()) {
1698 Type printType = printOp.getPrintType();
1699 if (isa<VectorType>(printType)) {
1700 // Vectors should be broken into elementary print ops in VectorToSCF.
1701 return failure();
1702 }
1703 if (failed(emitScalarPrint(rewriter, parent, loc, printType, value)))
1704 return failure();
1705 }
1706
1707 auto punct = printOp.getPunctuation();
1708 if (auto stringLiteral = printOp.getStringLiteral()) {
1709 auto createResult =
1710 LLVM::createPrintStrCall(rewriter, loc, parent, "vector_print_str",
1711 *stringLiteral, *getTypeConverter(),
1712 /*addNewline=*/false);
1713 if (createResult.failed())
1714 return failure();
1715
1716 } else if (punct != PrintPunctuation::NoPunctuation) {
1717 FailureOr<LLVM::LLVMFuncOp> op = [&]() {
1718 switch (punct) {
1719 case PrintPunctuation::Close:
1720 return LLVM::lookupOrCreatePrintCloseFn(rewriter, parent,
1721 symbolTables);
1722 case PrintPunctuation::Open:
1723 return LLVM::lookupOrCreatePrintOpenFn(rewriter, parent,
1724 symbolTables);
1725 case PrintPunctuation::Comma:
1726 return LLVM::lookupOrCreatePrintCommaFn(rewriter, parent,
1727 symbolTables);
1728 case PrintPunctuation::NewLine:
1729 return LLVM::lookupOrCreatePrintNewlineFn(rewriter, parent,
1730 symbolTables);
1731 default:
1732 llvm_unreachable("unexpected punctuation");
1733 }
1734 }();
1735 if (failed(op))
1736 return failure();
1737 emitCall(rewriter, printOp->getLoc(), op.value());
1738 }
1739
1740 rewriter.eraseOp(printOp);
1741 return success();
1742 }
1743
1744private:
1745 enum class PrintConversion {
1746 // clang-format off
1747 None,
1748 ZeroExt64,
1749 SignExt64,
1750 Bitcast16
1751 // clang-format on
1752 };
1753
1754 LogicalResult emitScalarPrint(ConversionPatternRewriter &rewriter,
1755 ModuleOp parent, Location loc, Type printType,
1756 Value value) const {
1757 if (typeConverter->convertType(printType) == nullptr)
1758 return failure();
1759
1760 // Make sure element type has runtime support.
1761 PrintConversion conversion = PrintConversion::None;
1762 FailureOr<Operation *> printer;
1763 if (printType.isF32()) {
1764 printer = LLVM::lookupOrCreatePrintF32Fn(rewriter, parent, symbolTables);
1765 } else if (printType.isF64()) {
1766 printer = LLVM::lookupOrCreatePrintF64Fn(rewriter, parent, symbolTables);
1767 } else if (printType.isF16()) {
1768 conversion = PrintConversion::Bitcast16; // bits!
1769 printer = LLVM::lookupOrCreatePrintF16Fn(rewriter, parent, symbolTables);
1770 } else if (printType.isBF16()) {
1771 conversion = PrintConversion::Bitcast16; // bits!
1772 printer = LLVM::lookupOrCreatePrintBF16Fn(rewriter, parent, symbolTables);
1773 } else if (printType.isIndex()) {
1774 printer = LLVM::lookupOrCreatePrintU64Fn(rewriter, parent, symbolTables);
1775 } else if (auto intTy = dyn_cast<IntegerType>(printType)) {
1776 // Integers need a zero or sign extension on the operand
1777 // (depending on the source type) as well as a signed or
1778 // unsigned print method. Up to 64-bit is supported.
1779 unsigned width = intTy.getWidth();
1780 if (intTy.isUnsigned()) {
1781 if (width <= 64) {
1782 if (width < 64)
1783 conversion = PrintConversion::ZeroExt64;
1784 printer =
1785 LLVM::lookupOrCreatePrintU64Fn(rewriter, parent, symbolTables);
1786 } else {
1787 return failure();
1788 }
1789 } else {
1790 assert(intTy.isSignless() || intTy.isSigned());
1791 if (width <= 64) {
1792 // Note that we *always* zero extend booleans (1-bit integers),
1793 // so that true/false is printed as 1/0 rather than -1/0.
1794 if (width == 1)
1795 conversion = PrintConversion::ZeroExt64;
1796 else if (width < 64)
1797 conversion = PrintConversion::SignExt64;
1798 printer =
1799 LLVM::lookupOrCreatePrintI64Fn(rewriter, parent, symbolTables);
1800 } else {
1801 return failure();
1802 }
1803 }
1804 } else if (auto floatTy = dyn_cast<FloatType>(printType)) {
1805 // Print other floating-point types using the APFloat runtime library.
1806 int32_t sem =
1807 llvm::APFloatBase::SemanticsToEnum(floatTy.getFloatSemantics());
1808 Value semValue = LLVM::ConstantOp::create(
1809 rewriter, loc, rewriter.getI32Type(),
1810 rewriter.getIntegerAttr(rewriter.getI32Type(), sem));
1811 Value floatBits =
1812 LLVM::ZExtOp::create(rewriter, loc, rewriter.getI64Type(), value);
1813 printer =
1814 LLVM::lookupOrCreateApFloatPrintFn(rewriter, parent, symbolTables);
1815 emitCall(rewriter, loc, printer.value(),
1816 ValueRange({semValue, floatBits}));
1817 return success();
1818 } else {
1819 return failure();
1820 }
1821 if (failed(printer))
1822 return failure();
1823
1824 switch (conversion) {
1825 case PrintConversion::ZeroExt64:
1826 value = arith::ExtUIOp::create(
1827 rewriter, loc, IntegerType::get(rewriter.getContext(), 64), value);
1828 break;
1829 case PrintConversion::SignExt64:
1830 value = arith::ExtSIOp::create(
1831 rewriter, loc, IntegerType::get(rewriter.getContext(), 64), value);
1832 break;
1833 case PrintConversion::Bitcast16:
1834 value = LLVM::BitcastOp::create(
1835 rewriter, loc, IntegerType::get(rewriter.getContext(), 16), value);
1836 break;
1837 case PrintConversion::None:
1838 break;
1839 }
1840 emitCall(rewriter, loc, printer.value(), value);
1841 return success();
1842 }
1843
1844 // Helper to emit a call.
1845 static void emitCall(ConversionPatternRewriter &rewriter, Location loc,
1846 Operation *ref, ValueRange params = ValueRange()) {
1847 LLVM::CallOp::create(rewriter, loc, TypeRange(), SymbolRefAttr::get(ref),
1848 params);
1849 }
1850};
1851
1852/// A broadcast of a scalar is lowered to an insertelement + a shufflevector
1853/// operation. Only broadcasts to 0-d and 1-d vectors are lowered by this
1854/// pattern, the higher rank cases are handled by another pattern.
1855struct VectorBroadcastScalarToLowRankLowering
1856 : public ConvertOpToLLVMPattern<vector::BroadcastOp> {
1857 using ConvertOpToLLVMPattern<vector::BroadcastOp>::ConvertOpToLLVMPattern;
1858
1859 LogicalResult
1860 matchAndRewrite(vector::BroadcastOp broadcast, OpAdaptor adaptor,
1861 ConversionPatternRewriter &rewriter) const override {
1862 if (isa<VectorType>(broadcast.getSourceType()))
1863 return rewriter.notifyMatchFailure(
1864 broadcast, "broadcast from vector type not handled");
1865
1866 VectorType resultType = broadcast.getType();
1867 if (resultType.getRank() > 1)
1868 return rewriter.notifyMatchFailure(broadcast,
1869 "broadcast to 2+-d handled elsewhere");
1870
1871 // First insert it into a poison vector so we can shuffle it.
1872 auto vectorType = typeConverter->convertType(broadcast.getType());
1873 Value poison =
1874 LLVM::PoisonOp::create(rewriter, broadcast.getLoc(), vectorType);
1875 auto zero = LLVM::ConstantOp::create(
1876 rewriter, broadcast.getLoc(),
1877 typeConverter->convertType(rewriter.getIntegerType(32)),
1878 rewriter.getZeroAttr(rewriter.getIntegerType(32)));
1879
1880 // For 0-d vector, we simply do `insertelement`.
1881 if (resultType.getRank() == 0) {
1882 rewriter.replaceOpWithNewOp<LLVM::InsertElementOp>(
1883 broadcast, vectorType, poison, adaptor.getSource(), zero);
1884 return success();
1885 }
1886
1887 auto v =
1888 LLVM::InsertElementOp::create(rewriter, broadcast.getLoc(), vectorType,
1889 poison, adaptor.getSource(), zero);
1890
1891 // For 1-d vector, we additionally do a `shufflevector`.
1892 int64_t width = cast<VectorType>(broadcast.getType()).getDimSize(0);
1893 SmallVector<int32_t> zeroValues(width, 0);
1894
1895 // Shuffle the value across the desired number of elements.
1896 auto shuffle = rewriter.createOrFold<LLVM::ShuffleVectorOp>(
1897 broadcast.getLoc(), v, poison, zeroValues);
1898 rewriter.replaceOp(broadcast, shuffle);
1899 return success();
1900 }
1901};
1902
1903/// The broadcast of a scalar is lowered to an insertelement + a shufflevector
1904/// operation. Only broadcasts to 2+-d vector result types are lowered by this
1905/// pattern, the 1-d case is handled by another pattern. Broadcasts from vectors
1906/// are not converted to LLVM, only broadcasts from scalars are.
1907struct VectorBroadcastScalarToNdLowering
1908 : public ConvertOpToLLVMPattern<BroadcastOp> {
1909 using ConvertOpToLLVMPattern<BroadcastOp>::ConvertOpToLLVMPattern;
1910
1911 LogicalResult
1912 matchAndRewrite(BroadcastOp broadcast, OpAdaptor adaptor,
1913 ConversionPatternRewriter &rewriter) const override {
1914 if (isa<VectorType>(broadcast.getSourceType()))
1915 return rewriter.notifyMatchFailure(
1916 broadcast, "broadcast from vector type not handled");
1917
1918 VectorType resultType = broadcast.getType();
1919 if (resultType.getRank() <= 1)
1920 return rewriter.notifyMatchFailure(
1921 broadcast, "broadcast to 1-d or 0-d handled elsewhere");
1922
1923 // First insert it into a poison vector so we can shuffle it.
1924 auto loc = broadcast.getLoc();
1925 auto vectorTypeInfo =
1926 LLVM::detail::extractNDVectorTypeInfo(resultType, *getTypeConverter());
1927 auto llvmNDVectorTy = vectorTypeInfo.llvmNDVectorTy;
1928 auto llvm1DVectorTy = vectorTypeInfo.llvm1DVectorTy;
1929 if (!llvmNDVectorTy || !llvm1DVectorTy)
1930 return failure();
1931
1932 // Construct returned value.
1933 Value desc = LLVM::PoisonOp::create(rewriter, loc, llvmNDVectorTy);
1934
1935 // Construct a 1-D vector with the broadcasted value that we insert in all
1936 // the places within the returned descriptor.
1937 Value vdesc = LLVM::PoisonOp::create(rewriter, loc, llvm1DVectorTy);
1938 auto zero = LLVM::ConstantOp::create(
1939 rewriter, loc, typeConverter->convertType(rewriter.getIntegerType(32)),
1940 rewriter.getZeroAttr(rewriter.getIntegerType(32)));
1941 Value v = LLVM::InsertElementOp::create(rewriter, loc, llvm1DVectorTy,
1942 vdesc, adaptor.getSource(), zero);
1943
1944 // Shuffle the value across the desired number of elements.
1945 int64_t width = resultType.getDimSize(resultType.getRank() - 1);
1946 SmallVector<int32_t> zeroValues(width, 0);
1947 v = LLVM::ShuffleVectorOp::create(rewriter, loc, v, v, zeroValues);
1948
1949 // Iterate of linear index, convert to coords space and insert broadcasted
1950 // 1-D vector in each position.
1951 nDVectorIterate(vectorTypeInfo, rewriter, [&](ArrayRef<int64_t> position) {
1952 desc = LLVM::InsertValueOp::create(rewriter, loc, desc, v, position);
1953 });
1954 rewriter.replaceOp(broadcast, desc);
1955 return success();
1956 }
1957};
1958
1959/// Conversion pattern for a `vector.interleave`.
1960/// This supports fixed-sized vectors and scalable vectors.
1961struct VectorInterleaveOpLowering
1962 : public ConvertOpToLLVMPattern<vector::InterleaveOp> {
1964
1965 LogicalResult
1966 matchAndRewrite(vector::InterleaveOp interleaveOp, OpAdaptor adaptor,
1967 ConversionPatternRewriter &rewriter) const override {
1968 VectorType resultType = interleaveOp.getResultVectorType();
1969 // n-D interleaves should have been lowered already.
1970 if (resultType.getRank() != 1)
1971 return rewriter.notifyMatchFailure(interleaveOp,
1972 "InterleaveOp not rank 1");
1973 // If the result is rank 1, then this directly maps to LLVM.
1974 if (resultType.isScalable()) {
1975 rewriter.replaceOpWithNewOp<LLVM::vector_interleave2>(
1976 interleaveOp, typeConverter->convertType(resultType),
1977 adaptor.getLhs(), adaptor.getRhs());
1978 return success();
1979 }
1980 // Lower fixed-size interleaves to a shufflevector. While the
1981 // vector.interleave2 intrinsic supports fixed and scalable vectors, the
1982 // langref still recommends fixed-vectors use shufflevector, see:
1983 // https://llvm.org/docs/LangRef.html#id876.
1984 int64_t resultVectorSize = resultType.getNumElements();
1985 SmallVector<int32_t> interleaveShuffleMask;
1986 interleaveShuffleMask.reserve(resultVectorSize);
1987 for (int i = 0, end = resultVectorSize / 2; i < end; ++i) {
1988 interleaveShuffleMask.push_back(i);
1989 interleaveShuffleMask.push_back((resultVectorSize / 2) + i);
1990 }
1991 rewriter.replaceOpWithNewOp<LLVM::ShuffleVectorOp>(
1992 interleaveOp, adaptor.getLhs(), adaptor.getRhs(),
1993 interleaveShuffleMask);
1994 return success();
1995 }
1996};
1997
1998/// Conversion pattern for a `vector.deinterleave`.
1999/// This supports fixed-sized vectors and scalable vectors.
2000struct VectorDeinterleaveOpLowering
2001 : public ConvertOpToLLVMPattern<vector::DeinterleaveOp> {
2003
2004 LogicalResult
2005 matchAndRewrite(vector::DeinterleaveOp deinterleaveOp, OpAdaptor adaptor,
2006 ConversionPatternRewriter &rewriter) const override {
2007 VectorType resultType = deinterleaveOp.getResultVectorType();
2008 VectorType sourceType = deinterleaveOp.getSourceVectorType();
2009 auto loc = deinterleaveOp.getLoc();
2010
2011 // Note: n-D deinterleave operations should be lowered to the 1-D before
2012 // converting to LLVM.
2013 if (resultType.getRank() != 1)
2014 return rewriter.notifyMatchFailure(deinterleaveOp,
2015 "DeinterleaveOp not rank 1");
2016
2017 if (resultType.isScalable()) {
2018 const auto *llvmTypeConverter = this->getTypeConverter();
2019 auto deinterleaveResults = deinterleaveOp.getResultTypes();
2020 auto packedOpResults =
2021 llvmTypeConverter->packOperationResults(deinterleaveResults);
2022 auto intrinsic = LLVM::vector_deinterleave2::create(
2023 rewriter, loc, packedOpResults, adaptor.getSource());
2024
2025 auto evenResult = LLVM::ExtractValueOp::create(
2026 rewriter, loc, intrinsic->getResult(0), 0);
2027 auto oddResult = LLVM::ExtractValueOp::create(rewriter, loc,
2028 intrinsic->getResult(0), 1);
2029
2030 rewriter.replaceOp(deinterleaveOp, ValueRange{evenResult, oddResult});
2031 return success();
2032 }
2033 // Lower fixed-size deinterleave to two shufflevectors. While the
2034 // vector.deinterleave2 intrinsic supports fixed and scalable vectors, the
2035 // langref still recommends fixed-vectors use shufflevector, see:
2036 // https://llvm.org/docs/LangRef.html#id889.
2037 int64_t resultVectorSize = resultType.getNumElements();
2038 SmallVector<int32_t> evenShuffleMask;
2039 SmallVector<int32_t> oddShuffleMask;
2040
2041 evenShuffleMask.reserve(resultVectorSize);
2042 oddShuffleMask.reserve(resultVectorSize);
2043
2044 for (int i = 0; i < sourceType.getNumElements(); ++i) {
2045 if (i % 2 == 0)
2046 evenShuffleMask.push_back(i);
2047 else
2048 oddShuffleMask.push_back(i);
2049 }
2050
2051 auto poison = LLVM::PoisonOp::create(rewriter, loc, sourceType);
2052 auto evenShuffle = LLVM::ShuffleVectorOp::create(
2053 rewriter, loc, adaptor.getSource(), poison, evenShuffleMask);
2054 auto oddShuffle = LLVM::ShuffleVectorOp::create(
2055 rewriter, loc, adaptor.getSource(), poison, oddShuffleMask);
2056
2057 rewriter.replaceOp(deinterleaveOp, ValueRange{evenShuffle, oddShuffle});
2058 return success();
2059 }
2060};
2061
2062/// Conversion pattern for a `vector.from_elements`.
2063struct VectorFromElementsLowering
2064 : public ConvertOpToLLVMPattern<vector::FromElementsOp> {
2066
2067 LogicalResult
2068 matchAndRewrite(vector::FromElementsOp fromElementsOp, OpAdaptor adaptor,
2069 ConversionPatternRewriter &rewriter) const override {
2070 Location loc = fromElementsOp.getLoc();
2071 VectorType vectorType = fromElementsOp.getType();
2072 // Only support 1-D vectors. Multi-dimensional vectors should have been
2073 // transformed to 1-D vectors by the vector-to-vector transformations before
2074 // this.
2075 if (vectorType.getRank() > 1)
2076 return rewriter.notifyMatchFailure(fromElementsOp,
2077 "rank > 1 vectors are not supported");
2078 Type llvmType = typeConverter->convertType(vectorType);
2079 Type llvmIndexType = typeConverter->convertType(rewriter.getIndexType());
2080 Value result = LLVM::PoisonOp::create(rewriter, loc, llvmType);
2081 for (auto [idx, val] : llvm::enumerate(adaptor.getElements())) {
2082 auto constIdx =
2083 LLVM::ConstantOp::create(rewriter, loc, llvmIndexType, idx);
2084 result = LLVM::InsertElementOp::create(rewriter, loc, llvmType, result,
2085 val, constIdx);
2086 }
2087 rewriter.replaceOp(fromElementsOp, result);
2088 return success();
2089 }
2090};
2091
2092/// Conversion pattern for a `vector.to_elements`.
2093struct VectorToElementsLowering
2094 : public ConvertOpToLLVMPattern<vector::ToElementsOp> {
2096
2097 LogicalResult
2098 matchAndRewrite(vector::ToElementsOp toElementsOp, OpAdaptor adaptor,
2099 ConversionPatternRewriter &rewriter) const override {
2100 Location loc = toElementsOp.getLoc();
2101 auto idxType = typeConverter->convertType(rewriter.getIndexType());
2102 Value source = adaptor.getSource();
2103
2104 SmallVector<Value> results(toElementsOp->getNumResults());
2105 for (auto [idx, element] : llvm::enumerate(toElementsOp.getElements())) {
2106 // Create an extractelement operation only for results that are not dead.
2107 if (element.use_empty())
2108 continue;
2109
2110 auto constIdx = LLVM::ConstantOp::create(
2111 rewriter, loc, idxType, rewriter.getIntegerAttr(idxType, idx));
2112 auto llvmType = typeConverter->convertType(element.getType());
2113
2114 Value result = LLVM::ExtractElementOp::create(rewriter, loc, llvmType,
2115 source, constIdx);
2116 results[idx] = result;
2117 }
2118
2119 rewriter.replaceOp(toElementsOp, results);
2120 return success();
2121 }
2122};
2123
2124/// Conversion pattern for vector.step.
2125struct VectorStepOpLowering : public ConvertOpToLLVMPattern<vector::StepOp> {
2127
2128 LogicalResult
2129 matchAndRewrite(vector::StepOp stepOp, OpAdaptor adaptor,
2130 ConversionPatternRewriter &rewriter) const override {
2131 Type llvmType = typeConverter->convertType(stepOp.getType());
2132 rewriter.replaceOpWithNewOp<LLVM::StepVectorOp>(stepOp, llvmType);
2133 return success();
2134 }
2135};
2136
2137/// Progressive lowering of a `vector.contract %a, %b, %c` with row-major matmul
2138/// semantics to:
2139/// ```
2140/// %flattened_a = vector.shape_cast %a
2141/// %flattened_b = vector.shape_cast %b
2142/// %flattened_d = vector.matrix_multiply %flattened_a, %flattened_b
2143/// %d = vector.shape_cast %%flattened_d
2144/// %e = add %c, %d
2145/// ```
2146/// `vector.matrix_multiply` later lowers to `llvm.matrix.multiply`.
2147class ContractionOpToMatmulOpLowering
2148 : public vector::MaskableOpRewritePattern<vector::ContractionOp> {
2149public:
2150 using MaskableOpRewritePattern::MaskableOpRewritePattern;
2151
2152 ContractionOpToMatmulOpLowering(MLIRContext *context,
2153 PatternBenefit benefit = 100)
2154 : MaskableOpRewritePattern<vector::ContractionOp>(context, benefit) {}
2155
2156 FailureOr<Value>
2157 matchAndRewriteMaskableOp(vector::ContractionOp op, MaskingOpInterface maskOp,
2158 PatternRewriter &rewriter) const override;
2159};
2160
2161/// Lower a qualifying `vector.contract %a, %b, %c` (with row-major matmul
2162/// semantics directly into `llvm.intr.matrix.multiply`:
2163/// BEFORE:
2164/// ```mlir
2165/// %res = vector.contract #matmat_trait %lhs, %rhs, %acc
2166/// : vector<2x4xf32>, vector<4x3xf32> into vector<2x3xf32>
2167/// ```
2168///
2169/// AFTER:
2170/// ```mlir
2171/// %lhs = vector.shape_cast %arg0 : vector<2x4xf32> to vector<8xf32>
2172/// %rhs = vector.shape_cast %arg1 : vector<4x3xf32> to vector<12xf32>
2173/// %matmul = llvm.intr.matrix.multiply %lhs, %rhs
2174/// %res = arith.addf %acc, %matmul : vector<2x3xf32>
2175/// ```
2176//
2177/// Scalable vectors are not supported.
2178FailureOr<Value> ContractionOpToMatmulOpLowering::matchAndRewriteMaskableOp(
2179 vector::ContractionOp op, MaskingOpInterface maskOp,
2180 PatternRewriter &rew) const {
2181 // TODO: Support vector.mask.
2182 if (maskOp)
2183 return failure();
2184
2185 auto iteratorTypes = op.getIteratorTypes().getValue();
2186 if (!isParallelIterator(iteratorTypes[0]) ||
2187 !isParallelIterator(iteratorTypes[1]) ||
2188 !isReductionIterator(iteratorTypes[2]))
2189 return failure();
2190
2191 Type opResType = op.getType();
2192 VectorType vecType = dyn_cast<VectorType>(opResType);
2193 if (vecType && vecType.isScalable())
2194 return failure();
2195 // The result type only reflects the M and N dims, so a scalable K
2196 // (reduction) dim would slip through the check above.
2197 if (op.getLhsType().isScalable() || op.getRhsType().isScalable())
2198 return failure();
2199
2200 Type elementType = op.getLhsType().getElementType();
2201 if (!elementType.isIntOrFloat())
2202 return failure();
2203
2204 Type dstElementType = vecType ? vecType.getElementType() : opResType;
2205 if (elementType != dstElementType)
2206 return failure();
2207
2208 // Perform lhs + rhs transpositions to conform to matmul row-major semantics.
2209 // Bail out if the contraction cannot be put in this form.
2210 MLIRContext *ctx = op.getContext();
2211 Location loc = op.getLoc();
2212 AffineExpr m, n, k;
2213 bindDims(rew.getContext(), m, n, k);
2214 // LHS must be A(m, k) or A(k, m).
2215 Value lhs = op.getLhs();
2216 auto lhsMap = op.getIndexingMapsArray()[0];
2217 if (lhsMap == AffineMap::get(3, 0, {k, m}, ctx))
2218 lhs = vector::TransposeOp::create(rew, loc, lhs, ArrayRef<int64_t>{1, 0});
2219 else if (lhsMap != AffineMap::get(3, 0, {m, k}, ctx))
2220 return failure();
2221
2222 // RHS must be B(k, n) or B(n, k).
2223 Value rhs = op.getRhs();
2224 auto rhsMap = op.getIndexingMapsArray()[1];
2225 if (rhsMap == AffineMap::get(3, 0, {n, k}, ctx))
2226 rhs = vector::TransposeOp::create(rew, loc, rhs, ArrayRef<int64_t>{1, 0});
2227 else if (rhsMap != AffineMap::get(3, 0, {k, n}, ctx))
2228 return failure();
2229
2230 // At this point lhs and rhs are in row-major.
2231 VectorType lhsType = cast<VectorType>(lhs.getType());
2232 VectorType rhsType = cast<VectorType>(rhs.getType());
2233 int64_t lhsRows = lhsType.getDimSize(0);
2234 int64_t lhsColumns = lhsType.getDimSize(1);
2235 int64_t rhsColumns = rhsType.getDimSize(1);
2236
2237 Type flattenedLHSType =
2238 VectorType::get(lhsType.getNumElements(), lhsType.getElementType());
2239 lhs = vector::ShapeCastOp::create(rew, loc, flattenedLHSType, lhs);
2240
2241 Type flattenedRHSType =
2242 VectorType::get(rhsType.getNumElements(), rhsType.getElementType());
2243 rhs = vector::ShapeCastOp::create(rew, loc, flattenedRHSType, rhs);
2244
2245 Value mul = LLVM::MatrixMultiplyOp::create(
2246 rew, loc,
2247 VectorType::get(lhsRows * rhsColumns,
2248 cast<VectorType>(lhs.getType()).getElementType()),
2249 lhs, rhs, lhsRows, lhsColumns, rhsColumns);
2250
2251 mul = vector::ShapeCastOp::create(
2252 rew, loc,
2253 VectorType::get({lhsRows, rhsColumns},
2254 getElementTypeOrSelf(op.getAcc().getType())),
2255 mul);
2256
2257 // ACC must be C(m, n) or C(n, m).
2258 auto accMap = op.getIndexingMapsArray()[2];
2259 if (accMap == AffineMap::get(3, 0, {n, m}, ctx))
2260 mul = vector::TransposeOp::create(rew, loc, mul, ArrayRef<int64_t>{1, 0});
2261 else if (accMap != AffineMap::get(3, 0, {m, n}, ctx))
2262 llvm_unreachable("invalid contraction semantics");
2263
2264 Value res = isa<IntegerType>(elementType)
2265 ? static_cast<Value>(
2266 arith::AddIOp::create(rew, loc, op.getAcc(), mul))
2267 : static_cast<Value>(
2268 arith::AddFOp::create(rew, loc, op.getAcc(), mul));
2269
2270 return res;
2271}
2272
2273/// Lowers vector.transpose directly to llvm.intr.matrix.transpose
2274///
2275/// BEFORE:
2276/// ```mlir
2277/// %tr = vector.transpose %vec, [1, 0] : vector<2x4xf32> to vector<4x2xf32>
2278/// ```
2279/// AFTER:
2280/// ```mlir
2281/// %vec_cs = vector.shape_cast %vec : vector<2x4xf32> to vector<8xf32>
2282/// %tr = llvm.intr.matrix.transpose %vec_sc
2283/// {columns = 2 : i32, rows = 4 : i32} : vector<8xf32> into vector<8xf32>
2284/// %res = vector.shape_cast %tr : vector<8xf32> to vector<4x2xf32>
2285/// ```
2286class TransposeOpToMatrixTransposeOpLowering
2287 : public OpRewritePattern<vector::TransposeOp> {
2288public:
2289 using Base::Base;
2290
2291 LogicalResult matchAndRewrite(vector::TransposeOp op,
2292 PatternRewriter &rewriter) const override {
2293 auto loc = op.getLoc();
2294
2295 Value input = op.getVector();
2296 VectorType inputType = op.getSourceVectorType();
2297 VectorType resType = op.getResultVectorType();
2298
2299 if (inputType.isScalable())
2300 return rewriter.notifyMatchFailure(
2301 op, "This lowering does not support scalable vectors");
2302
2303 // Set up convenience transposition table.
2304 ArrayRef<int64_t> transp = op.getPermutation();
2305
2306 if (resType.getRank() != 2 || transp[0] != 1 || transp[1] != 0) {
2307 return failure();
2308 }
2309
2310 Type flattenedType =
2311 VectorType::get(resType.getNumElements(), resType.getElementType());
2312 auto matrix =
2313 vector::ShapeCastOp::create(rewriter, loc, flattenedType, input);
2314 auto rows = rewriter.getI32IntegerAttr(resType.getShape()[0]);
2315 auto columns = rewriter.getI32IntegerAttr(resType.getShape()[1]);
2316 Value trans = LLVM::MatrixTransposeOp::create(rewriter, loc, flattenedType,
2317 matrix, rows, columns);
2318 rewriter.replaceOpWithNewOp<vector::ShapeCastOp>(op, resType, trans);
2319 return success();
2320 }
2321};
2322
2323} // namespace
2324
2326 RewritePatternSet &patterns) {
2327 patterns.add<VectorFMAOpNDRewritePattern>(patterns.getContext());
2328}
2329
2331 RewritePatternSet &patterns, PatternBenefit benefit) {
2332 patterns.add<ContractionOpToMatmulOpLowering>(patterns.getContext(), benefit);
2333}
2334
2336 RewritePatternSet &patterns, PatternBenefit benefit) {
2337 patterns.add<TransposeOpToMatrixTransposeOpLowering>(patterns.getContext(),
2338 benefit);
2339}
2340
2341/// Populate the given list with patterns that convert from Vector to LLVM.
2343 const LLVMTypeConverter &converter, RewritePatternSet &patterns,
2344 bool reassociateFPReductions, bool force32BitVectorIndices,
2345 bool useVectorAlignment, bool enableGEPInboundsNuw) {
2346 // This function populates only ConversionPatterns, not RewritePatterns.
2347 MLIRContext *ctx = converter.getDialect()->getContext();
2348 patterns.add<VectorReductionOpConversion>(converter, reassociateFPReductions);
2349 patterns.add<VectorCreateMaskOpConversion>(ctx, force32BitVectorIndices);
2350 patterns.add<VectorLoadStoreConversion<vector::LoadOp>,
2351 VectorLoadStoreConversion<vector::MaskedLoadOp>,
2352 VectorLoadStoreConversion<vector::StoreOp>,
2353 VectorLoadStoreConversion<vector::MaskedStoreOp>>(
2354 converter, useVectorAlignment, enableGEPInboundsNuw);
2355 patterns.add<VectorGatherOpConversion, VectorScatterOpConversion>(
2356 converter, useVectorAlignment);
2357 patterns.add<VectorBitCastOpConversion, VectorShuffleOpConversion,
2358 VectorExtractOpConversion, VectorFMAOp1DConversion,
2359 VectorInsertOpConversion, VectorPrintOpConversion,
2360 VectorTypeCastOpConversion, VectorScaleOpConversion,
2361 VectorExpandLoadOpConversion, VectorCompressStoreOpConversion,
2362 VectorBroadcastScalarToLowRankLowering,
2363 VectorBroadcastScalarToNdLowering,
2364 VectorScalableInsertOpLowering, VectorScalableExtractOpLowering,
2365 MaskedReductionOpConversion, VectorInterleaveOpLowering,
2366 VectorDeinterleaveOpLowering, VectorFromElementsLowering,
2367 VectorToElementsLowering, VectorStepOpLowering>(converter);
2368}
2369
2370namespace {
2371struct VectorToLLVMDialectInterface : public ConvertToLLVMPatternInterface {
2372 VectorToLLVMDialectInterface(Dialect *dialect)
2373 : ConvertToLLVMPatternInterface(dialect) {}
2374
2375 using ConvertToLLVMPatternInterface::ConvertToLLVMPatternInterface;
2376 void loadDependentDialects(MLIRContext *context) const final {
2377 context->loadDialect<LLVM::LLVMDialect>();
2378 }
2379
2380 /// Hook for derived dialect interface to provide conversion patterns
2381 /// and mark dialect legal for the conversion target.
2382 void populateConvertToLLVMConversionPatterns(
2383 ConversionTarget &target, LLVMTypeConverter &typeConverter,
2384 RewritePatternSet &patterns) const final {
2385 populateVectorToLLVMConversionPatterns(typeConverter, patterns);
2386 }
2387};
2388} // namespace
2389
2391 DialectRegistry &registry) {
2392 registry.addExtension(+[](MLIRContext *ctx, vector::VectorDialect *dialect) {
2393 dialect->addInterfaces<VectorToLLVMDialectInterface>();
2394 });
2395}
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
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
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:71
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:434
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)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreatePrintBF16Fn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreatePrintOpenFn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
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.
FailureOr< LLVM::LLVMFuncOp > lookupOrCreatePrintCommaFn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreatePrintI64Fn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
Helper functions to look up or create the declaration for commonly used external C function calls.
Value createIndexAttrConstant(OpBuilder &builder, Location loc, Type resultType, int64_t value)
Creates an llvm.mlir.constant producing value as resultType, which is expected to be the converted in...
Definition Pattern.cpp:58
FailureOr< LLVM::LLVMFuncOp > lookupOrCreatePrintNewlineFn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreatePrintCloseFn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreatePrintU64Fn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreatePrintF32Fn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreateApFloatPrintFn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
LogicalResult createPrintStrCall(OpBuilder &builder, Location loc, ModuleOp moduleOp, StringRef symbolName, StringRef string, const LLVMTypeConverter &typeConverter, bool addNewline=true, std::optional< StringRef > runtimeFunctionName={}, SymbolTableCollection *symbolTables=nullptr)
Generate IR that prints the given string to stdout.
FailureOr< LLVM::LLVMFuncOp > lookupOrCreatePrintF16Fn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreatePrintF64Fn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
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:853
bool isReductionIterator(Attribute attr)
Returns true if attr has "reduction" iterator type semantics.
Definition VectorOps.h:146
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:141
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, arith::FastMathFlagsAttr fastmath=nullptr)
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:313
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.
@ None
An explicit data construct.
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.