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