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/LLVMCommon/ConversionTarget.h" 14 #include "mlir/Conversion/LLVMCommon/Pattern.h" 15 #include "mlir/Conversion/LLVMCommon/TypeConverter.h" 16 #include "mlir/Conversion/SCFToStandard/SCFToStandard.h" 17 #include "mlir/Conversion/VectorToLLVM/ConvertVectorToLLVM.h" 18 #include "mlir/Conversion/VectorToSCF/VectorToSCF.h" 19 #include "mlir/Dialect/LLVMIR/LLVMDialect.h" 20 #include "mlir/Dialect/Linalg/IR/LinalgOps.h" 21 #include "mlir/Dialect/Linalg/IR/LinalgTypes.h" 22 #include "mlir/Dialect/Linalg/Passes.h" 23 #include "mlir/Dialect/SCF/SCF.h" 24 #include "mlir/IR/AffineExpr.h" 25 #include "mlir/IR/AffineMap.h" 26 #include "mlir/IR/Attributes.h" 27 #include "mlir/IR/Builders.h" 28 #include "mlir/IR/BuiltinOps.h" 29 #include "mlir/IR/BuiltinTypes.h" 30 #include "mlir/IR/MLIRContext.h" 31 #include "mlir/IR/Operation.h" 32 #include "mlir/IR/PatternMatch.h" 33 #include "mlir/IR/Types.h" 34 #include "mlir/Support/LogicalResult.h" 35 #include "mlir/Transforms/DialectConversion.h" 36 #include "mlir/Transforms/Passes.h" 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::LLVM; 46 using namespace mlir::linalg; 47 48 template <typename T> 49 static Type getPtrToElementType(T containerType, LLVMTypeConverter &lowering) { 50 return LLVMPointerType::get( 51 lowering.convertType(containerType.getElementType())); 52 } 53 54 /// Convert the given range descriptor type to the LLVMIR dialect. 55 /// Range descriptor contains the range bounds and the step as 64-bit integers. 56 /// 57 /// struct { 58 /// int64_t min; 59 /// int64_t max; 60 /// int64_t step; 61 /// }; 62 static Type convertRangeType(RangeType t, LLVMTypeConverter &converter) { 63 auto *context = t.getContext(); 64 auto int64Ty = converter.convertType(IntegerType::get(context, 64)); 65 return LLVMStructType::getLiteral(context, {int64Ty, int64Ty, int64Ty}); 66 } 67 68 namespace { 69 // RangeOp creates a new range descriptor. 70 class RangeOpConversion : public ConvertOpToLLVMPattern<RangeOp> { 71 public: 72 using ConvertOpToLLVMPattern<RangeOp>::ConvertOpToLLVMPattern; 73 74 LogicalResult 75 matchAndRewrite(RangeOp rangeOp, ArrayRef<Value> operands, 76 ConversionPatternRewriter &rewriter) const override { 77 auto rangeDescriptorTy = convertRangeType( 78 rangeOp.getType().cast<RangeType>(), *getTypeConverter()); 79 80 ImplicitLocOpBuilder b(rangeOp->getLoc(), rewriter); 81 82 // Fill in an aggregate value of the descriptor. 83 RangeOpAdaptor adaptor(operands); 84 Value desc = b.create<LLVM::UndefOp>(rangeDescriptorTy); 85 desc = b.create<LLVM::InsertValueOp>(desc, adaptor.min(), 86 rewriter.getI64ArrayAttr(0)); 87 desc = b.create<LLVM::InsertValueOp>(desc, adaptor.max(), 88 rewriter.getI64ArrayAttr(1)); 89 desc = b.create<LLVM::InsertValueOp>(desc, adaptor.step(), 90 rewriter.getI64ArrayAttr(2)); 91 rewriter.replaceOp(rangeOp, desc); 92 return success(); 93 } 94 }; 95 96 // ReshapeOp creates a new view descriptor of the proper rank. 97 // For now, the only conversion supported is for target MemRef with static sizes 98 // and strides. 99 template <typename ReshapeOp> 100 class ReshapeOpConversion : public ConvertOpToLLVMPattern<ReshapeOp> { 101 public: 102 using ConvertOpToLLVMPattern<ReshapeOp>::ConvertOpToLLVMPattern; 103 using ReshapeOpAdaptor = typename ReshapeOp::Adaptor; 104 105 LogicalResult 106 matchAndRewrite(ReshapeOp reshapeOp, ArrayRef<Value> operands, 107 ConversionPatternRewriter &rewriter) const override { 108 MemRefType dstType = reshapeOp.getResultType(); 109 110 if (!dstType.hasStaticShape()) 111 return failure(); 112 113 int64_t offset; 114 SmallVector<int64_t, 4> strides; 115 auto res = getStridesAndOffset(dstType, strides, offset); 116 if (failed(res) || llvm::any_of(strides, [](int64_t val) { 117 return ShapedType::isDynamicStrideOrOffset(val); 118 })) 119 return failure(); 120 121 ReshapeOpAdaptor adaptor(operands); 122 MemRefDescriptor baseDesc(adaptor.src()); 123 Location loc = reshapeOp->getLoc(); 124 auto desc = 125 MemRefDescriptor::undef(rewriter, reshapeOp->getLoc(), 126 this->typeConverter->convertType(dstType)); 127 desc.setAllocatedPtr(rewriter, loc, baseDesc.allocatedPtr(rewriter, loc)); 128 desc.setAlignedPtr(rewriter, loc, baseDesc.alignedPtr(rewriter, loc)); 129 desc.setOffset(rewriter, loc, baseDesc.offset(rewriter, loc)); 130 for (auto en : llvm::enumerate(dstType.getShape())) 131 desc.setConstantSize(rewriter, loc, en.index(), en.value()); 132 for (auto en : llvm::enumerate(strides)) 133 desc.setConstantStride(rewriter, loc, en.index(), en.value()); 134 rewriter.replaceOp(reshapeOp, {desc}); 135 return success(); 136 } 137 }; 138 139 // YieldOp produces and LLVM::ReturnOp. 140 class YieldOpConversion : public ConvertOpToLLVMPattern<linalg::YieldOp> { 141 public: 142 using ConvertOpToLLVMPattern<linalg::YieldOp>::ConvertOpToLLVMPattern; 143 144 LogicalResult 145 matchAndRewrite(linalg::YieldOp op, ArrayRef<Value> operands, 146 ConversionPatternRewriter &rewriter) const override { 147 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, operands); 148 return success(); 149 } 150 }; 151 } // namespace 152 153 /// Populate the given list with patterns that convert from Linalg to LLVM. 154 void mlir::populateLinalgToLLVMConversionPatterns(LLVMTypeConverter &converter, 155 RewritePatternSet &patterns) { 156 patterns.add<RangeOpConversion, ReshapeOpConversion<ExpandShapeOp>, 157 ReshapeOpConversion<CollapseShapeOp>, YieldOpConversion>( 158 converter); 159 160 // Populate the type conversions for the linalg types. 161 converter.addConversion( 162 [&](RangeType type) { return convertRangeType(type, converter); }); 163 } 164 165 namespace { 166 struct ConvertLinalgToLLVMPass 167 : public ConvertLinalgToLLVMBase<ConvertLinalgToLLVMPass> { 168 void runOnOperation() override; 169 }; 170 } // namespace 171 172 void ConvertLinalgToLLVMPass::runOnOperation() { 173 auto module = getOperation(); 174 175 // Convert to the LLVM IR dialect using the converter defined above. 176 RewritePatternSet patterns(&getContext()); 177 LLVMTypeConverter converter(&getContext()); 178 populateLinalgToLLVMConversionPatterns(converter, patterns); 179 180 LLVMConversionTarget target(getContext()); 181 target.addIllegalOp<RangeOp>(); 182 target.addLegalOp<ModuleOp, LLVM::DialectCastOp>(); 183 if (failed(applyPartialConversion(module, target, std::move(patterns)))) 184 signalPassFailure(); 185 } 186 187 std::unique_ptr<OperationPass<ModuleOp>> mlir::createConvertLinalgToLLVMPass() { 188 return std::make_unique<ConvertLinalgToLLVMPass>(); 189 } 190