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