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