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