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