1*684dfe8aSAlex Zinenko //===- Pattern.cpp - Conversion pattern to the LLVM dialect ---------------===//
2*684dfe8aSAlex Zinenko //
3*684dfe8aSAlex Zinenko // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*684dfe8aSAlex Zinenko // See https://llvm.org/LICENSE.txt for license information.
5*684dfe8aSAlex Zinenko // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*684dfe8aSAlex Zinenko //
7*684dfe8aSAlex Zinenko //===----------------------------------------------------------------------===//
8*684dfe8aSAlex Zinenko 
9*684dfe8aSAlex Zinenko #include "mlir/Conversion/LLVMCommon/Pattern.h"
10*684dfe8aSAlex Zinenko #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
11*684dfe8aSAlex Zinenko #include "mlir/Dialect/LLVMIR/LLVMTypes.h"
12*684dfe8aSAlex Zinenko #include "mlir/IR/AffineMap.h"
13*684dfe8aSAlex Zinenko 
14*684dfe8aSAlex Zinenko using namespace mlir;
15*684dfe8aSAlex Zinenko 
16*684dfe8aSAlex Zinenko //===----------------------------------------------------------------------===//
17*684dfe8aSAlex Zinenko // ConvertToLLVMPattern
18*684dfe8aSAlex Zinenko //===----------------------------------------------------------------------===//
19*684dfe8aSAlex Zinenko 
20*684dfe8aSAlex Zinenko ConvertToLLVMPattern::ConvertToLLVMPattern(StringRef rootOpName,
21*684dfe8aSAlex Zinenko                                            MLIRContext *context,
22*684dfe8aSAlex Zinenko                                            LLVMTypeConverter &typeConverter,
23*684dfe8aSAlex Zinenko                                            PatternBenefit benefit)
24*684dfe8aSAlex Zinenko     : ConversionPattern(typeConverter, rootOpName, benefit, context) {}
25*684dfe8aSAlex Zinenko 
26*684dfe8aSAlex Zinenko LLVMTypeConverter *ConvertToLLVMPattern::getTypeConverter() const {
27*684dfe8aSAlex Zinenko   return static_cast<LLVMTypeConverter *>(
28*684dfe8aSAlex Zinenko       ConversionPattern::getTypeConverter());
29*684dfe8aSAlex Zinenko }
30*684dfe8aSAlex Zinenko 
31*684dfe8aSAlex Zinenko LLVM::LLVMDialect &ConvertToLLVMPattern::getDialect() const {
32*684dfe8aSAlex Zinenko   return *getTypeConverter()->getDialect();
33*684dfe8aSAlex Zinenko }
34*684dfe8aSAlex Zinenko 
35*684dfe8aSAlex Zinenko Type ConvertToLLVMPattern::getIndexType() const {
36*684dfe8aSAlex Zinenko   return getTypeConverter()->getIndexType();
37*684dfe8aSAlex Zinenko }
38*684dfe8aSAlex Zinenko 
39*684dfe8aSAlex Zinenko Type ConvertToLLVMPattern::getIntPtrType(unsigned addressSpace) const {
40*684dfe8aSAlex Zinenko   return IntegerType::get(&getTypeConverter()->getContext(),
41*684dfe8aSAlex Zinenko                           getTypeConverter()->getPointerBitwidth(addressSpace));
42*684dfe8aSAlex Zinenko }
43*684dfe8aSAlex Zinenko 
44*684dfe8aSAlex Zinenko Type ConvertToLLVMPattern::getVoidType() const {
45*684dfe8aSAlex Zinenko   return LLVM::LLVMVoidType::get(&getTypeConverter()->getContext());
46*684dfe8aSAlex Zinenko }
47*684dfe8aSAlex Zinenko 
48*684dfe8aSAlex Zinenko Type ConvertToLLVMPattern::getVoidPtrType() const {
49*684dfe8aSAlex Zinenko   return LLVM::LLVMPointerType::get(
50*684dfe8aSAlex Zinenko       IntegerType::get(&getTypeConverter()->getContext(), 8));
51*684dfe8aSAlex Zinenko }
52*684dfe8aSAlex Zinenko 
53*684dfe8aSAlex Zinenko Value ConvertToLLVMPattern::createIndexAttrConstant(OpBuilder &builder,
54*684dfe8aSAlex Zinenko                                                     Location loc,
55*684dfe8aSAlex Zinenko                                                     Type resultType,
56*684dfe8aSAlex Zinenko                                                     int64_t value) {
57*684dfe8aSAlex Zinenko   return builder.create<LLVM::ConstantOp>(
58*684dfe8aSAlex Zinenko       loc, resultType, builder.getIntegerAttr(builder.getIndexType(), value));
59*684dfe8aSAlex Zinenko }
60*684dfe8aSAlex Zinenko 
61*684dfe8aSAlex Zinenko Value ConvertToLLVMPattern::createIndexConstant(
62*684dfe8aSAlex Zinenko     ConversionPatternRewriter &builder, Location loc, uint64_t value) const {
63*684dfe8aSAlex Zinenko   return createIndexAttrConstant(builder, loc, getIndexType(), value);
64*684dfe8aSAlex Zinenko }
65*684dfe8aSAlex Zinenko 
66*684dfe8aSAlex Zinenko Value ConvertToLLVMPattern::getStridedElementPtr(
67*684dfe8aSAlex Zinenko     Location loc, MemRefType type, Value memRefDesc, ValueRange indices,
68*684dfe8aSAlex Zinenko     ConversionPatternRewriter &rewriter) const {
69*684dfe8aSAlex Zinenko 
70*684dfe8aSAlex Zinenko   int64_t offset;
71*684dfe8aSAlex Zinenko   SmallVector<int64_t, 4> strides;
72*684dfe8aSAlex Zinenko   auto successStrides = getStridesAndOffset(type, strides, offset);
73*684dfe8aSAlex Zinenko   assert(succeeded(successStrides) && "unexpected non-strided memref");
74*684dfe8aSAlex Zinenko   (void)successStrides;
75*684dfe8aSAlex Zinenko 
76*684dfe8aSAlex Zinenko   MemRefDescriptor memRefDescriptor(memRefDesc);
77*684dfe8aSAlex Zinenko   Value base = memRefDescriptor.alignedPtr(rewriter, loc);
78*684dfe8aSAlex Zinenko 
79*684dfe8aSAlex Zinenko   Value index;
80*684dfe8aSAlex Zinenko   if (offset != 0) // Skip if offset is zero.
81*684dfe8aSAlex Zinenko     index = MemRefType::isDynamicStrideOrOffset(offset)
82*684dfe8aSAlex Zinenko                 ? memRefDescriptor.offset(rewriter, loc)
83*684dfe8aSAlex Zinenko                 : createIndexConstant(rewriter, loc, offset);
84*684dfe8aSAlex Zinenko 
85*684dfe8aSAlex Zinenko   for (int i = 0, e = indices.size(); i < e; ++i) {
86*684dfe8aSAlex Zinenko     Value increment = indices[i];
87*684dfe8aSAlex Zinenko     if (strides[i] != 1) { // Skip if stride is 1.
88*684dfe8aSAlex Zinenko       Value stride = MemRefType::isDynamicStrideOrOffset(strides[i])
89*684dfe8aSAlex Zinenko                          ? memRefDescriptor.stride(rewriter, loc, i)
90*684dfe8aSAlex Zinenko                          : createIndexConstant(rewriter, loc, strides[i]);
91*684dfe8aSAlex Zinenko       increment = rewriter.create<LLVM::MulOp>(loc, increment, stride);
92*684dfe8aSAlex Zinenko     }
93*684dfe8aSAlex Zinenko     index =
94*684dfe8aSAlex Zinenko         index ? rewriter.create<LLVM::AddOp>(loc, index, increment) : increment;
95*684dfe8aSAlex Zinenko   }
96*684dfe8aSAlex Zinenko 
97*684dfe8aSAlex Zinenko   Type elementPtrType = memRefDescriptor.getElementPtrType();
98*684dfe8aSAlex Zinenko   return index ? rewriter.create<LLVM::GEPOp>(loc, elementPtrType, base, index)
99*684dfe8aSAlex Zinenko                : base;
100*684dfe8aSAlex Zinenko }
101*684dfe8aSAlex Zinenko 
102*684dfe8aSAlex Zinenko // Check if the MemRefType `type` is supported by the lowering. We currently
103*684dfe8aSAlex Zinenko // only support memrefs with identity maps.
104*684dfe8aSAlex Zinenko bool ConvertToLLVMPattern::isConvertibleAndHasIdentityMaps(
105*684dfe8aSAlex Zinenko     MemRefType type) const {
106*684dfe8aSAlex Zinenko   if (!typeConverter->convertType(type.getElementType()))
107*684dfe8aSAlex Zinenko     return false;
108*684dfe8aSAlex Zinenko   return type.getAffineMaps().empty() ||
109*684dfe8aSAlex Zinenko          llvm::all_of(type.getAffineMaps(),
110*684dfe8aSAlex Zinenko                       [](AffineMap map) { return map.isIdentity(); });
111*684dfe8aSAlex Zinenko }
112*684dfe8aSAlex Zinenko 
113*684dfe8aSAlex Zinenko Type ConvertToLLVMPattern::getElementPtrType(MemRefType type) const {
114*684dfe8aSAlex Zinenko   auto elementType = type.getElementType();
115*684dfe8aSAlex Zinenko   auto structElementType = typeConverter->convertType(elementType);
116*684dfe8aSAlex Zinenko   return LLVM::LLVMPointerType::get(structElementType,
117*684dfe8aSAlex Zinenko                                     type.getMemorySpaceAsInt());
118*684dfe8aSAlex Zinenko }
119*684dfe8aSAlex Zinenko 
120*684dfe8aSAlex Zinenko void ConvertToLLVMPattern::getMemRefDescriptorSizes(
121*684dfe8aSAlex Zinenko     Location loc, MemRefType memRefType, ValueRange dynamicSizes,
122*684dfe8aSAlex Zinenko     ConversionPatternRewriter &rewriter, SmallVectorImpl<Value> &sizes,
123*684dfe8aSAlex Zinenko     SmallVectorImpl<Value> &strides, Value &sizeBytes) const {
124*684dfe8aSAlex Zinenko   assert(isConvertibleAndHasIdentityMaps(memRefType) &&
125*684dfe8aSAlex Zinenko          "layout maps must have been normalized away");
126*684dfe8aSAlex Zinenko   assert(count(memRefType.getShape(), ShapedType::kDynamicSize) ==
127*684dfe8aSAlex Zinenko              static_cast<ssize_t>(dynamicSizes.size()) &&
128*684dfe8aSAlex Zinenko          "dynamicSizes size doesn't match dynamic sizes count in memref shape");
129*684dfe8aSAlex Zinenko 
130*684dfe8aSAlex Zinenko   sizes.reserve(memRefType.getRank());
131*684dfe8aSAlex Zinenko   unsigned dynamicIndex = 0;
132*684dfe8aSAlex Zinenko   for (int64_t size : memRefType.getShape()) {
133*684dfe8aSAlex Zinenko     sizes.push_back(size == ShapedType::kDynamicSize
134*684dfe8aSAlex Zinenko                         ? dynamicSizes[dynamicIndex++]
135*684dfe8aSAlex Zinenko                         : createIndexConstant(rewriter, loc, size));
136*684dfe8aSAlex Zinenko   }
137*684dfe8aSAlex Zinenko 
138*684dfe8aSAlex Zinenko   // Strides: iterate sizes in reverse order and multiply.
139*684dfe8aSAlex Zinenko   int64_t stride = 1;
140*684dfe8aSAlex Zinenko   Value runningStride = createIndexConstant(rewriter, loc, 1);
141*684dfe8aSAlex Zinenko   strides.resize(memRefType.getRank());
142*684dfe8aSAlex Zinenko   for (auto i = memRefType.getRank(); i-- > 0;) {
143*684dfe8aSAlex Zinenko     strides[i] = runningStride;
144*684dfe8aSAlex Zinenko 
145*684dfe8aSAlex Zinenko     int64_t size = memRefType.getShape()[i];
146*684dfe8aSAlex Zinenko     if (size == 0)
147*684dfe8aSAlex Zinenko       continue;
148*684dfe8aSAlex Zinenko     bool useSizeAsStride = stride == 1;
149*684dfe8aSAlex Zinenko     if (size == ShapedType::kDynamicSize)
150*684dfe8aSAlex Zinenko       stride = ShapedType::kDynamicSize;
151*684dfe8aSAlex Zinenko     if (stride != ShapedType::kDynamicSize)
152*684dfe8aSAlex Zinenko       stride *= size;
153*684dfe8aSAlex Zinenko 
154*684dfe8aSAlex Zinenko     if (useSizeAsStride)
155*684dfe8aSAlex Zinenko       runningStride = sizes[i];
156*684dfe8aSAlex Zinenko     else if (stride == ShapedType::kDynamicSize)
157*684dfe8aSAlex Zinenko       runningStride =
158*684dfe8aSAlex Zinenko           rewriter.create<LLVM::MulOp>(loc, runningStride, sizes[i]);
159*684dfe8aSAlex Zinenko     else
160*684dfe8aSAlex Zinenko       runningStride = createIndexConstant(rewriter, loc, stride);
161*684dfe8aSAlex Zinenko   }
162*684dfe8aSAlex Zinenko 
163*684dfe8aSAlex Zinenko   // Buffer size in bytes.
164*684dfe8aSAlex Zinenko   Type elementPtrType = getElementPtrType(memRefType);
165*684dfe8aSAlex Zinenko   Value nullPtr = rewriter.create<LLVM::NullOp>(loc, elementPtrType);
166*684dfe8aSAlex Zinenko   Value gepPtr = rewriter.create<LLVM::GEPOp>(
167*684dfe8aSAlex Zinenko       loc, elementPtrType, ArrayRef<Value>{nullPtr, runningStride});
168*684dfe8aSAlex Zinenko   sizeBytes = rewriter.create<LLVM::PtrToIntOp>(loc, getIndexType(), gepPtr);
169*684dfe8aSAlex Zinenko }
170*684dfe8aSAlex Zinenko 
171*684dfe8aSAlex Zinenko Value ConvertToLLVMPattern::getSizeInBytes(
172*684dfe8aSAlex Zinenko     Location loc, Type type, ConversionPatternRewriter &rewriter) const {
173*684dfe8aSAlex Zinenko   // Compute the size of an individual element. This emits the MLIR equivalent
174*684dfe8aSAlex Zinenko   // of the following sizeof(...) implementation in LLVM IR:
175*684dfe8aSAlex Zinenko   //   %0 = getelementptr %elementType* null, %indexType 1
176*684dfe8aSAlex Zinenko   //   %1 = ptrtoint %elementType* %0 to %indexType
177*684dfe8aSAlex Zinenko   // which is a common pattern of getting the size of a type in bytes.
178*684dfe8aSAlex Zinenko   auto convertedPtrType =
179*684dfe8aSAlex Zinenko       LLVM::LLVMPointerType::get(typeConverter->convertType(type));
180*684dfe8aSAlex Zinenko   auto nullPtr = rewriter.create<LLVM::NullOp>(loc, convertedPtrType);
181*684dfe8aSAlex Zinenko   auto gep = rewriter.create<LLVM::GEPOp>(
182*684dfe8aSAlex Zinenko       loc, convertedPtrType,
183*684dfe8aSAlex Zinenko       ArrayRef<Value>{nullPtr, createIndexConstant(rewriter, loc, 1)});
184*684dfe8aSAlex Zinenko   return rewriter.create<LLVM::PtrToIntOp>(loc, getIndexType(), gep);
185*684dfe8aSAlex Zinenko }
186*684dfe8aSAlex Zinenko 
187*684dfe8aSAlex Zinenko Value ConvertToLLVMPattern::getNumElements(
188*684dfe8aSAlex Zinenko     Location loc, ArrayRef<Value> shape,
189*684dfe8aSAlex Zinenko     ConversionPatternRewriter &rewriter) const {
190*684dfe8aSAlex Zinenko   // Compute the total number of memref elements.
191*684dfe8aSAlex Zinenko   Value numElements =
192*684dfe8aSAlex Zinenko       shape.empty() ? createIndexConstant(rewriter, loc, 1) : shape.front();
193*684dfe8aSAlex Zinenko   for (unsigned i = 1, e = shape.size(); i < e; ++i)
194*684dfe8aSAlex Zinenko     numElements = rewriter.create<LLVM::MulOp>(loc, numElements, shape[i]);
195*684dfe8aSAlex Zinenko   return numElements;
196*684dfe8aSAlex Zinenko }
197*684dfe8aSAlex Zinenko 
198*684dfe8aSAlex Zinenko /// Creates and populates the memref descriptor struct given all its fields.
199*684dfe8aSAlex Zinenko MemRefDescriptor ConvertToLLVMPattern::createMemRefDescriptor(
200*684dfe8aSAlex Zinenko     Location loc, MemRefType memRefType, Value allocatedPtr, Value alignedPtr,
201*684dfe8aSAlex Zinenko     ArrayRef<Value> sizes, ArrayRef<Value> strides,
202*684dfe8aSAlex Zinenko     ConversionPatternRewriter &rewriter) const {
203*684dfe8aSAlex Zinenko   auto structType = typeConverter->convertType(memRefType);
204*684dfe8aSAlex Zinenko   auto memRefDescriptor = MemRefDescriptor::undef(rewriter, loc, structType);
205*684dfe8aSAlex Zinenko 
206*684dfe8aSAlex Zinenko   // Field 1: Allocated pointer, used for malloc/free.
207*684dfe8aSAlex Zinenko   memRefDescriptor.setAllocatedPtr(rewriter, loc, allocatedPtr);
208*684dfe8aSAlex Zinenko 
209*684dfe8aSAlex Zinenko   // Field 2: Actual aligned pointer to payload.
210*684dfe8aSAlex Zinenko   memRefDescriptor.setAlignedPtr(rewriter, loc, alignedPtr);
211*684dfe8aSAlex Zinenko 
212*684dfe8aSAlex Zinenko   // Field 3: Offset in aligned pointer.
213*684dfe8aSAlex Zinenko   memRefDescriptor.setOffset(rewriter, loc,
214*684dfe8aSAlex Zinenko                              createIndexConstant(rewriter, loc, 0));
215*684dfe8aSAlex Zinenko 
216*684dfe8aSAlex Zinenko   // Fields 4: Sizes.
217*684dfe8aSAlex Zinenko   for (auto en : llvm::enumerate(sizes))
218*684dfe8aSAlex Zinenko     memRefDescriptor.setSize(rewriter, loc, en.index(), en.value());
219*684dfe8aSAlex Zinenko 
220*684dfe8aSAlex Zinenko   // Field 5: Strides.
221*684dfe8aSAlex Zinenko   for (auto en : llvm::enumerate(strides))
222*684dfe8aSAlex Zinenko     memRefDescriptor.setStride(rewriter, loc, en.index(), en.value());
223*684dfe8aSAlex Zinenko 
224*684dfe8aSAlex Zinenko   return memRefDescriptor;
225*684dfe8aSAlex Zinenko }
226*684dfe8aSAlex Zinenko 
227*684dfe8aSAlex Zinenko //===----------------------------------------------------------------------===//
228*684dfe8aSAlex Zinenko // Detail methods
229*684dfe8aSAlex Zinenko //===----------------------------------------------------------------------===//
230*684dfe8aSAlex Zinenko 
231*684dfe8aSAlex Zinenko /// Replaces the given operation "op" with a new operation of type "targetOp"
232*684dfe8aSAlex Zinenko /// and given operands.
233*684dfe8aSAlex Zinenko LogicalResult LLVM::detail::oneToOneRewrite(
234*684dfe8aSAlex Zinenko     Operation *op, StringRef targetOp, ValueRange operands,
235*684dfe8aSAlex Zinenko     LLVMTypeConverter &typeConverter, ConversionPatternRewriter &rewriter) {
236*684dfe8aSAlex Zinenko   unsigned numResults = op->getNumResults();
237*684dfe8aSAlex Zinenko 
238*684dfe8aSAlex Zinenko   Type packedType;
239*684dfe8aSAlex Zinenko   if (numResults != 0) {
240*684dfe8aSAlex Zinenko     packedType = typeConverter.packFunctionResults(op->getResultTypes());
241*684dfe8aSAlex Zinenko     if (!packedType)
242*684dfe8aSAlex Zinenko       return failure();
243*684dfe8aSAlex Zinenko   }
244*684dfe8aSAlex Zinenko 
245*684dfe8aSAlex Zinenko   // Create the operation through state since we don't know its C++ type.
246*684dfe8aSAlex Zinenko   OperationState state(op->getLoc(), targetOp);
247*684dfe8aSAlex Zinenko   state.addTypes(packedType);
248*684dfe8aSAlex Zinenko   state.addOperands(operands);
249*684dfe8aSAlex Zinenko   state.addAttributes(op->getAttrs());
250*684dfe8aSAlex Zinenko   Operation *newOp = rewriter.createOperation(state);
251*684dfe8aSAlex Zinenko 
252*684dfe8aSAlex Zinenko   // If the operation produced 0 or 1 result, return them immediately.
253*684dfe8aSAlex Zinenko   if (numResults == 0)
254*684dfe8aSAlex Zinenko     return rewriter.eraseOp(op), success();
255*684dfe8aSAlex Zinenko   if (numResults == 1)
256*684dfe8aSAlex Zinenko     return rewriter.replaceOp(op, newOp->getResult(0)), success();
257*684dfe8aSAlex Zinenko 
258*684dfe8aSAlex Zinenko   // Otherwise, it had been converted to an operation producing a structure.
259*684dfe8aSAlex Zinenko   // Extract individual results from the structure and return them as list.
260*684dfe8aSAlex Zinenko   SmallVector<Value, 4> results;
261*684dfe8aSAlex Zinenko   results.reserve(numResults);
262*684dfe8aSAlex Zinenko   for (unsigned i = 0; i < numResults; ++i) {
263*684dfe8aSAlex Zinenko     auto type = typeConverter.convertType(op->getResult(i).getType());
264*684dfe8aSAlex Zinenko     results.push_back(rewriter.create<LLVM::ExtractValueOp>(
265*684dfe8aSAlex Zinenko         op->getLoc(), type, newOp->getResult(0), rewriter.getI64ArrayAttr(i)));
266*684dfe8aSAlex Zinenko   }
267*684dfe8aSAlex Zinenko   rewriter.replaceOp(op, results);
268*684dfe8aSAlex Zinenko   return success();
269*684dfe8aSAlex Zinenko }
270