1 //===- LinalgToLLVM.cpp - conversion from Linalg to 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 
9 #include "mlir/Conversion/LinalgToLLVM/LinalgToLLVM.h"
10 
11 #include "../PassDetail.h"
12 #include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
13 #include "mlir/Conversion/LoopToStandard/ConvertLoopToStandard.h"
14 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"
15 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h"
16 #include "mlir/Conversion/VectorToLLVM/ConvertVectorToLLVM.h"
17 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
18 #include "mlir/Dialect/Linalg/IR/LinalgOps.h"
19 #include "mlir/Dialect/Linalg/IR/LinalgTypes.h"
20 #include "mlir/Dialect/Linalg/Passes.h"
21 #include "mlir/Dialect/StandardOps/EDSC/Intrinsics.h"
22 #include "mlir/IR/AffineExpr.h"
23 #include "mlir/IR/AffineMap.h"
24 #include "mlir/IR/Attributes.h"
25 #include "mlir/IR/Builders.h"
26 #include "mlir/IR/MLIRContext.h"
27 #include "mlir/IR/Module.h"
28 #include "mlir/IR/Operation.h"
29 #include "mlir/IR/PatternMatch.h"
30 #include "mlir/IR/StandardTypes.h"
31 #include "mlir/IR/Types.h"
32 #include "mlir/Support/LogicalResult.h"
33 #include "mlir/Transforms/DialectConversion.h"
34 #include "mlir/Transforms/Passes.h"
35 #include "llvm/ADT/SetVector.h"
36 #include "llvm/IR/DerivedTypes.h"
37 #include "llvm/IR/Module.h"
38 #include "llvm/IR/Type.h"
39 #include "llvm/Support/Allocator.h"
40 #include "llvm/Support/ErrorHandling.h"
41 
42 using namespace mlir;
43 using namespace mlir::edsc;
44 using namespace mlir::edsc::intrinsics;
45 using namespace mlir::LLVM;
46 using namespace mlir::linalg;
47 
48 using llvm_add = ValueBuilder<LLVM::AddOp>;
49 using llvm_bitcast = ValueBuilder<LLVM::BitcastOp>;
50 using llvm_constant = ValueBuilder<LLVM::ConstantOp>;
51 using llvm_extractvalue = ValueBuilder<LLVM::ExtractValueOp>;
52 using llvm_gep = ValueBuilder<LLVM::GEPOp>;
53 using llvm_insertvalue = ValueBuilder<LLVM::InsertValueOp>;
54 using llvm_call = OperationBuilder<LLVM::CallOp>;
55 using llvm_icmp = ValueBuilder<LLVM::ICmpOp>;
56 using llvm_load = ValueBuilder<LLVM::LoadOp>;
57 using llvm_store = OperationBuilder<LLVM::StoreOp>;
58 using llvm_select = ValueBuilder<LLVM::SelectOp>;
59 using llvm_mul = ValueBuilder<LLVM::MulOp>;
60 using llvm_ptrtoint = ValueBuilder<LLVM::PtrToIntOp>;
61 using llvm_sub = ValueBuilder<LLVM::SubOp>;
62 using llvm_undef = ValueBuilder<LLVM::UndefOp>;
63 using llvm_urem = ValueBuilder<LLVM::URemOp>;
64 using llvm_alloca = ValueBuilder<LLVM::AllocaOp>;
65 using llvm_return = OperationBuilder<LLVM::ReturnOp>;
66 
67 template <typename T>
68 static LLVMType getPtrToElementType(T containerType,
69                                     LLVMTypeConverter &lowering) {
70   return lowering.convertType(containerType.getElementType())
71       .template cast<LLVMType>()
72       .getPointerTo();
73 }
74 
75 /// Convert the given range descriptor type to the LLVMIR dialect.
76 /// Range descriptor contains the range bounds and the step as 64-bit integers.
77 ///
78 /// struct {
79 ///   int64_t min;
80 ///   int64_t max;
81 ///   int64_t step;
82 /// };
83 static Type convertRangeType(RangeType t, LLVMTypeConverter &converter) {
84   auto *context = t.getContext();
85   auto int64Ty = converter.convertType(IntegerType::get(64, context))
86                      .cast<LLVM::LLVMType>();
87   return LLVMType::getStructTy(int64Ty, int64Ty, int64Ty);
88 }
89 
90 namespace {
91 /// EDSC-compatible wrapper for MemRefDescriptor.
92 class BaseViewConversionHelper {
93 public:
94   BaseViewConversionHelper(Type type)
95       : d(MemRefDescriptor::undef(rewriter(), loc(), type)) {}
96 
97   BaseViewConversionHelper(Value v) : d(v) {}
98 
99   /// Wrappers around MemRefDescriptor that use EDSC builder and location.
100   Value allocatedPtr() { return d.allocatedPtr(rewriter(), loc()); }
101   void setAllocatedPtr(Value v) { d.setAllocatedPtr(rewriter(), loc(), v); }
102   Value alignedPtr() { return d.alignedPtr(rewriter(), loc()); }
103   void setAlignedPtr(Value v) { d.setAlignedPtr(rewriter(), loc(), v); }
104   Value offset() { return d.offset(rewriter(), loc()); }
105   void setOffset(Value v) { d.setOffset(rewriter(), loc(), v); }
106   Value size(unsigned i) { return d.size(rewriter(), loc(), i); }
107   void setSize(unsigned i, Value v) { d.setSize(rewriter(), loc(), i, v); }
108   void setConstantSize(unsigned i, int64_t v) {
109     d.setConstantSize(rewriter(), loc(), i, v);
110   }
111   Value stride(unsigned i) { return d.stride(rewriter(), loc(), i); }
112   void setStride(unsigned i, Value v) { d.setStride(rewriter(), loc(), i, v); }
113   void setConstantStride(unsigned i, int64_t v) {
114     d.setConstantStride(rewriter(), loc(), i, v);
115   }
116 
117   operator Value() { return d; }
118 
119 private:
120   OpBuilder &rewriter() { return ScopedContext::getBuilder(); }
121   Location loc() { return ScopedContext::getLocation(); }
122 
123   MemRefDescriptor d;
124 };
125 
126 // RangeOp creates a new range descriptor.
127 class RangeOpConversion : public ConvertToLLVMPattern {
128 public:
129   explicit RangeOpConversion(MLIRContext *context, LLVMTypeConverter &lowering_)
130       : ConvertToLLVMPattern(RangeOp::getOperationName(), context, lowering_) {}
131 
132   LogicalResult
133   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
134                   ConversionPatternRewriter &rewriter) const override {
135     auto rangeOp = cast<RangeOp>(op);
136     auto rangeDescriptorTy =
137         convertRangeType(rangeOp.getType().cast<RangeType>(), typeConverter);
138 
139     edsc::ScopedContext context(rewriter, op->getLoc());
140 
141     // Fill in an aggregate value of the descriptor.
142     RangeOpOperandAdaptor adaptor(operands);
143     Value desc = llvm_undef(rangeDescriptorTy);
144     desc = llvm_insertvalue(desc, adaptor.min(), rewriter.getI64ArrayAttr(0));
145     desc = llvm_insertvalue(desc, adaptor.max(), rewriter.getI64ArrayAttr(1));
146     desc = llvm_insertvalue(desc, adaptor.step(), rewriter.getI64ArrayAttr(2));
147     rewriter.replaceOp(op, desc);
148     return success();
149   }
150 };
151 
152 // ReshapeOp creates a new view descriptor of the proper rank.
153 // For now, the only conversion supported is for target MemRef with static sizes
154 // and strides.
155 class ReshapeOpConversion : public ConvertToLLVMPattern {
156 public:
157   explicit ReshapeOpConversion(MLIRContext *context,
158                                LLVMTypeConverter &lowering_)
159       : ConvertToLLVMPattern(ReshapeOp::getOperationName(), context,
160                              lowering_) {}
161 
162   LogicalResult
163   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
164                   ConversionPatternRewriter &rewriter) const override {
165     auto reshapeOp = cast<ReshapeOp>(op);
166     MemRefType dstType = reshapeOp.getResultType();
167 
168     if (!dstType.hasStaticShape())
169       return failure();
170 
171     int64_t offset;
172     SmallVector<int64_t, 4> strides;
173     auto res = getStridesAndOffset(dstType, strides, offset);
174     if (failed(res) || llvm::any_of(strides, [](int64_t val) {
175           return ShapedType::isDynamicStrideOrOffset(val);
176         }))
177       return failure();
178 
179     edsc::ScopedContext context(rewriter, op->getLoc());
180     ReshapeOpOperandAdaptor adaptor(operands);
181     BaseViewConversionHelper baseDesc(adaptor.src());
182     BaseViewConversionHelper desc(typeConverter.convertType(dstType));
183     desc.setAllocatedPtr(baseDesc.allocatedPtr());
184     desc.setAlignedPtr(baseDesc.alignedPtr());
185     desc.setOffset(baseDesc.offset());
186     for (auto en : llvm::enumerate(dstType.getShape()))
187       desc.setConstantSize(en.index(), en.value());
188     for (auto en : llvm::enumerate(strides))
189       desc.setConstantStride(en.index(), en.value());
190     rewriter.replaceOp(op, {desc});
191     return success();
192   }
193 };
194 
195 /// Conversion pattern that transforms a linalg.slice op into:
196 ///   1. An "undef" value for the ViewDescriptor.
197 ///   2. Updates to the ViewDescriptor to introduce the data ptr, offset, size
198 ///      and stride corresponding to the region of memory within the bounds of
199 ///      the parent view.
200 /// The linalg.slice op is replaced by the alloca'ed pointer.
201 class SliceOpConversion : public ConvertToLLVMPattern {
202 public:
203   explicit SliceOpConversion(MLIRContext *context, LLVMTypeConverter &lowering_)
204       : ConvertToLLVMPattern(SliceOp::getOperationName(), context, lowering_) {}
205 
206   LogicalResult
207   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
208                   ConversionPatternRewriter &rewriter) const override {
209     edsc::ScopedContext context(rewriter, op->getLoc());
210     SliceOpOperandAdaptor adaptor(operands);
211     BaseViewConversionHelper baseDesc(adaptor.view());
212 
213     auto sliceOp = cast<SliceOp>(op);
214     auto memRefType = sliceOp.getBaseViewType();
215     auto int64Ty = typeConverter.convertType(rewriter.getIntegerType(64))
216                        .cast<LLVM::LLVMType>();
217 
218     BaseViewConversionHelper desc(
219         typeConverter.convertType(sliceOp.getShapedType()));
220 
221     // TODO(ntv): extract sizes and emit asserts.
222     SmallVector<Value, 4> strides(memRefType.getRank());
223     for (int i = 0, e = memRefType.getRank(); i < e; ++i)
224       strides[i] = baseDesc.stride(i);
225 
226     auto pos = [&rewriter](ArrayRef<int64_t> values) {
227       return rewriter.getI64ArrayAttr(values);
228     };
229 
230     // Compute base offset.
231     Value baseOffset = baseDesc.offset();
232     for (int i = 0, e = memRefType.getRank(); i < e; ++i) {
233       Value indexing = adaptor.indexings()[i];
234       Value min = indexing;
235       if (sliceOp.indexing(i).getType().isa<RangeType>())
236         min = llvm_extractvalue(int64Ty, indexing, pos(0));
237       baseOffset = llvm_add(baseOffset, llvm_mul(min, strides[i]));
238     }
239 
240     // Insert the base and aligned pointers.
241     desc.setAllocatedPtr(baseDesc.allocatedPtr());
242     desc.setAlignedPtr(baseDesc.alignedPtr());
243 
244     // Insert base offset.
245     desc.setOffset(baseOffset);
246 
247     // Corner case, no sizes or strides: early return the descriptor.
248     if (sliceOp.getShapedType().getRank() == 0)
249       return rewriter.replaceOp(op, {desc}), success();
250 
251     Value zero = llvm_constant(
252         int64Ty, rewriter.getIntegerAttr(rewriter.getIndexType(), 0));
253     // Compute and insert view sizes (max - min along the range) and strides.
254     // Skip the non-range operands as they will be projected away from the view.
255     int numNewDims = 0;
256     for (auto en : llvm::enumerate(sliceOp.indexings())) {
257       Value indexing = en.value();
258       if (indexing.getType().isa<RangeType>()) {
259         int rank = en.index();
260         Value rangeDescriptor = adaptor.indexings()[rank];
261         Value min = llvm_extractvalue(int64Ty, rangeDescriptor, pos(0));
262         Value max = llvm_extractvalue(int64Ty, rangeDescriptor, pos(1));
263         Value step = llvm_extractvalue(int64Ty, rangeDescriptor, pos(2));
264         Value baseSize = baseDesc.size(rank);
265 
266         // Bound upper by base view upper bound.
267         max = llvm_select(llvm_icmp(ICmpPredicate::slt, max, baseSize), max,
268                           baseSize);
269         Value size = llvm_sub(max, min);
270         // Bound lower by zero.
271         size =
272             llvm_select(llvm_icmp(ICmpPredicate::slt, size, zero), zero, size);
273         Value stride = llvm_mul(strides[rank], step);
274         desc.setSize(numNewDims, size);
275         desc.setStride(numNewDims, stride);
276         ++numNewDims;
277       }
278     }
279 
280     rewriter.replaceOp(op, {desc});
281     return success();
282   }
283 };
284 
285 /// Conversion pattern that transforms a linalg.transpose op into:
286 ///   1. A function entry `alloca` operation to allocate a ViewDescriptor.
287 ///   2. A load of the ViewDescriptor from the pointer allocated in 1.
288 ///   3. Updates to the ViewDescriptor to introduce the data ptr, offset, size
289 ///      and stride. Size and stride are permutations of the original values.
290 ///   4. A store of the resulting ViewDescriptor to the alloca'ed pointer.
291 /// The linalg.transpose op is replaced by the alloca'ed pointer.
292 class TransposeOpConversion : public ConvertToLLVMPattern {
293 public:
294   explicit TransposeOpConversion(MLIRContext *context,
295                                  LLVMTypeConverter &lowering_)
296       : ConvertToLLVMPattern(TransposeOp::getOperationName(), context,
297                              lowering_) {}
298 
299   LogicalResult
300   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
301                   ConversionPatternRewriter &rewriter) const override {
302     // Initialize the common boilerplate and alloca at the top of the FuncOp.
303     edsc::ScopedContext context(rewriter, op->getLoc());
304     TransposeOpOperandAdaptor adaptor(operands);
305     BaseViewConversionHelper baseDesc(adaptor.view());
306 
307     auto transposeOp = cast<TransposeOp>(op);
308     // No permutation, early exit.
309     if (transposeOp.permutation().isIdentity())
310       return rewriter.replaceOp(op, {baseDesc}), success();
311 
312     BaseViewConversionHelper desc(
313         typeConverter.convertType(transposeOp.getShapedType()));
314 
315     // Copy the base and aligned pointers from the old descriptor to the new
316     // one.
317     desc.setAllocatedPtr(baseDesc.allocatedPtr());
318     desc.setAlignedPtr(baseDesc.alignedPtr());
319 
320     // Copy the offset pointer from the old descriptor to the new one.
321     desc.setOffset(baseDesc.offset());
322 
323     // Iterate over the dimensions and apply size/stride permutation.
324     for (auto en : llvm::enumerate(transposeOp.permutation().getResults())) {
325       int sourcePos = en.index();
326       int targetPos = en.value().cast<AffineDimExpr>().getPosition();
327       desc.setSize(targetPos, baseDesc.size(sourcePos));
328       desc.setStride(targetPos, baseDesc.stride(sourcePos));
329     }
330 
331     rewriter.replaceOp(op, {desc});
332     return success();
333   }
334 };
335 
336 // YieldOp produces and LLVM::ReturnOp.
337 class YieldOpConversion : public ConvertToLLVMPattern {
338 public:
339   explicit YieldOpConversion(MLIRContext *context, LLVMTypeConverter &lowering_)
340       : ConvertToLLVMPattern(YieldOp::getOperationName(), context, lowering_) {}
341 
342   LogicalResult
343   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
344                   ConversionPatternRewriter &rewriter) const override {
345     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, operands);
346     return success();
347   }
348 };
349 } // namespace
350 
351 template <typename LinalgOp>
352 static SmallVector<Type, 4> ExtractOperandTypes(Operation *op) {
353   return SmallVector<Type, 4>{op->getOperandTypes()};
354 }
355 
356 template <>
357 SmallVector<Type, 4> ExtractOperandTypes<IndexedGenericOp>(Operation *op) {
358   auto ctx = op->getContext();
359   auto indexedGenericOp = cast<IndexedGenericOp>(op);
360   auto numLoops = indexedGenericOp.getNumLoops();
361 
362   SmallVector<Type, 4> result;
363   result.reserve(numLoops + op->getNumOperands());
364   for (unsigned i = 0; i < numLoops; ++i) {
365     result.push_back(IndexType::get(ctx));
366   }
367   for (auto type : op->getOperandTypes()) {
368     result.push_back(type);
369   }
370   return result;
371 }
372 
373 // Get a SymbolRefAttr containing the library function name for the LinalgOp.
374 // If the library function does not exist, insert a declaration.
375 template <typename LinalgOp>
376 static FlatSymbolRefAttr getLibraryCallSymbolRef(Operation *op,
377                                                  PatternRewriter &rewriter) {
378   auto linalgOp = cast<LinalgOp>(op);
379   auto fnName = linalgOp.getLibraryCallName();
380   if (fnName.empty()) {
381     op->emitWarning("No library call defined for: ") << *op;
382     return {};
383   }
384 
385   // fnName is a dynamic std::String, unique it via a SymbolRefAttr.
386   FlatSymbolRefAttr fnNameAttr = rewriter.getSymbolRefAttr(fnName);
387   auto module = op->getParentOfType<ModuleOp>();
388   if (module.lookupSymbol(fnName)) {
389     return fnNameAttr;
390   }
391 
392   SmallVector<Type, 4> inputTypes(ExtractOperandTypes<LinalgOp>(op));
393   assert(op->getNumResults() == 0 &&
394          "Library call for linalg operation can be generated only for ops that "
395          "have void return types");
396   auto libFnType = FunctionType::get(inputTypes, {}, rewriter.getContext());
397 
398   OpBuilder::InsertionGuard guard(rewriter);
399   // Insert before module terminator.
400   rewriter.setInsertionPoint(module.getBody(),
401                              std::prev(module.getBody()->end()));
402   FuncOp funcOp =
403       rewriter.create<FuncOp>(op->getLoc(), fnNameAttr.getValue(), libFnType,
404                               ArrayRef<NamedAttribute>{});
405   // Insert a function attribute that will trigger the emission of the
406   // corresponding `_mlir_ciface_xxx` interface so that external libraries see
407   // a normalized ABI. This interface is added during std to llvm conversion.
408   funcOp.setAttr("llvm.emit_c_interface", UnitAttr::get(op->getContext()));
409   return fnNameAttr;
410 }
411 
412 namespace {
413 
414 // LinalgOpConversion<LinalgOp> creates a new call to the
415 // `LinalgOp::getLibraryCallName()` function.
416 // The implementation of the function can be either in the same module or in an
417 // externally linked library.
418 template <typename LinalgOp>
419 class LinalgOpConversion : public OpRewritePattern<LinalgOp> {
420 public:
421   using OpRewritePattern<LinalgOp>::OpRewritePattern;
422 
423   LogicalResult matchAndRewrite(LinalgOp op,
424                                 PatternRewriter &rewriter) const override {
425     auto libraryCallName = getLibraryCallSymbolRef<LinalgOp>(op, rewriter);
426     if (!libraryCallName)
427       return failure();
428 
429     rewriter.replaceOpWithNewOp<mlir::CallOp>(
430         op, libraryCallName.getValue(), ArrayRef<Type>{}, op.getOperands());
431     return success();
432   }
433 };
434 
435 /// Conversion pattern specialization for CopyOp. This kicks in when both input
436 /// and output permutations are left unspecified or are the identity.
437 template <> class LinalgOpConversion<CopyOp> : public OpRewritePattern<CopyOp> {
438 public:
439   using OpRewritePattern<CopyOp>::OpRewritePattern;
440 
441   LogicalResult matchAndRewrite(CopyOp op,
442                                 PatternRewriter &rewriter) const override {
443     auto inputPerm = op.inputPermutation();
444     if (inputPerm.hasValue() && !inputPerm->isIdentity())
445       return failure();
446     auto outputPerm = op.outputPermutation();
447     if (outputPerm.hasValue() && !outputPerm->isIdentity())
448       return failure();
449 
450     auto libraryCallName = getLibraryCallSymbolRef<CopyOp>(op, rewriter);
451     if (!libraryCallName)
452       return failure();
453 
454     rewriter.replaceOpWithNewOp<mlir::CallOp>(
455         op, libraryCallName.getValue(), ArrayRef<Type>{}, op.getOperands());
456     return success();
457   }
458 };
459 
460 /// Conversion pattern specialization for IndexedGenericOp.
461 template <>
462 class LinalgOpConversion<IndexedGenericOp>
463     : public OpRewritePattern<IndexedGenericOp> {
464 public:
465   using OpRewritePattern<IndexedGenericOp>::OpRewritePattern;
466 
467   LogicalResult matchAndRewrite(IndexedGenericOp op,
468                                 PatternRewriter &rewriter) const override {
469     auto libraryCallName =
470         getLibraryCallSymbolRef<IndexedGenericOp>(op, rewriter);
471     if (!libraryCallName)
472       return failure();
473 
474     // TODO(pifon, ntv): Use induction variables values instead of zeros, when
475     // IndexedGenericOp is tiled.
476     auto zero = rewriter.create<mlir::ConstantOp>(
477         op.getLoc(), rewriter.getIntegerAttr(rewriter.getIndexType(), 0));
478     auto indexedGenericOp = cast<IndexedGenericOp>(op);
479     auto numLoops = indexedGenericOp.getNumLoops();
480     SmallVector<Value, 4> operands;
481     operands.reserve(numLoops + op.getNumOperands());
482     for (unsigned i = 0; i < numLoops; ++i) {
483       operands.push_back(zero);
484     }
485     for (auto operand : op.getOperands()) {
486       operands.push_back(operand);
487     }
488     rewriter.replaceOpWithNewOp<mlir::CallOp>(op, libraryCallName.getValue(),
489                                               ArrayRef<Type>{}, operands);
490     return success();
491   }
492 };
493 
494 /// A non-conversion rewrite pattern kicks in to convert CopyOp with
495 /// permutations into a sequence of TransposeOp and permutation-free CopyOp.
496 /// This interplays together with TransposeOpConversion and
497 /// LinalgConversion<CopyOp> to create a path to the LLVM dialect.
498 class CopyTransposeConversion : public OpRewritePattern<CopyOp> {
499 public:
500   using OpRewritePattern<CopyOp>::OpRewritePattern;
501 
502   LogicalResult matchAndRewrite(CopyOp op,
503                                 PatternRewriter &rewriter) const override {
504     Value in = op.input(), out = op.output();
505 
506     // If either inputPerm or outputPerm are non-identities, insert transposes.
507     auto inputPerm = op.inputPermutation();
508     if (inputPerm.hasValue() && !inputPerm->isIdentity())
509       in = rewriter.create<linalg::TransposeOp>(op.getLoc(), in,
510                                                 AffineMapAttr::get(*inputPerm));
511     auto outputPerm = op.outputPermutation();
512     if (outputPerm.hasValue() && !outputPerm->isIdentity())
513       out = rewriter.create<linalg::TransposeOp>(
514           op.getLoc(), out, AffineMapAttr::get(*outputPerm));
515 
516     // If nothing was transposed, fail and let the conversion kick in.
517     if (in == op.input() && out == op.output())
518       return failure();
519 
520     rewriter.replaceOpWithNewOp<CopyOp>(op, in, out);
521     return success();
522   }
523 };
524 
525 /// Populate the given list with patterns that convert from Linalg to Standard.
526 static void
527 populateLinalgToStandardConversionPatterns(OwningRewritePatternList &patterns,
528                                            MLIRContext *ctx) {
529   // TODO(ntv) ConvOp conversion needs to export a descriptor with relevant
530   // attribute values such as kernel striding and dilation.
531   // clang-format off
532   patterns.insert<
533       CopyTransposeConversion,
534       LinalgOpConversion<ConvOp>,
535       LinalgOpConversion<PoolingMaxOp>,
536       LinalgOpConversion<PoolingMinOp>,
537       LinalgOpConversion<PoolingSumOp>,
538       LinalgOpConversion<CopyOp>,
539       LinalgOpConversion<DotOp>,
540       LinalgOpConversion<FillOp>,
541       LinalgOpConversion<GenericOp>,
542       LinalgOpConversion<IndexedGenericOp>,
543       LinalgOpConversion<MatmulOp>,
544       LinalgOpConversion<MatvecOp>>(ctx);
545   // clang-format on
546 }
547 
548 } // namespace
549 
550 /// Populate the given list with patterns that convert from Linalg to LLVM.
551 void mlir::populateLinalgToLLVMConversionPatterns(
552     LLVMTypeConverter &converter, OwningRewritePatternList &patterns,
553     MLIRContext *ctx) {
554   patterns.insert<RangeOpConversion, ReshapeOpConversion, SliceOpConversion,
555                   TransposeOpConversion, YieldOpConversion>(ctx, converter);
556 
557   // Populate the type conversions for the linalg types.
558   converter.addConversion(
559       [&](RangeType type) { return convertRangeType(type, converter); });
560 }
561 
562 namespace {
563 struct ConvertLinalgToLLVMPass
564     : public ConvertLinalgToLLVMBase<ConvertLinalgToLLVMPass> {
565   void runOnOperation() override;
566 };
567 } // namespace
568 
569 void ConvertLinalgToLLVMPass::runOnOperation() {
570   auto module = getOperation();
571 
572   // Convert to the LLVM IR dialect using the converter defined above.
573   OwningRewritePatternList patterns;
574   LLVMTypeConverter converter(&getContext());
575   populateAffineToStdConversionPatterns(patterns, &getContext());
576   populateLoopToStdConversionPatterns(patterns, &getContext());
577   populateStdToLLVMConversionPatterns(converter, patterns);
578   populateVectorToLLVMMatrixConversionPatterns(converter, patterns);
579   populateVectorToLLVMConversionPatterns(converter, patterns);
580   populateLinalgToStandardConversionPatterns(patterns, &getContext());
581   populateLinalgToLLVMConversionPatterns(converter, patterns, &getContext());
582 
583   LLVMConversionTarget target(getContext());
584   target.addDynamicallyLegalOp<FuncOp>(
585       [&](FuncOp op) { return converter.isSignatureLegal(op.getType()); });
586   target.addLegalOp<ModuleOp, ModuleTerminatorOp>();
587   if (failed(applyFullConversion(module, target, patterns, &converter)))
588     signalPassFailure();
589 }
590 
591 std::unique_ptr<OperationPass<ModuleOp>> mlir::createConvertLinalgToLLVMPass() {
592   return std::make_unique<ConvertLinalgToLLVMPass>();
593 }
594