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