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