MLIR 23.0.0git
MemRefToLLVM.cpp
Go to the documentation of this file.
1//===- MemRefToLLVM.cpp - MemRef to LLVM dialect conversion ---------------===//
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
10
23#include "mlir/IR/AffineMap.h"
25#include "mlir/IR/IRMapping.h"
26#include "mlir/Pass/Pass.h"
27#include "llvm/Support/DebugLog.h"
28#include "llvm/Support/MathExtras.h"
29
30#include <optional>
31
32#define DEBUG_TYPE "memref-to-llvm"
33
34namespace mlir {
35#define GEN_PASS_DEF_FINALIZEMEMREFTOLLVMCONVERSIONPASS
36#include "mlir/Conversion/Passes.h.inc"
37} // namespace mlir
38
39using namespace mlir;
40
41/// Returns GEP no-wrap flags for a memref load/store.
42/// inbounds is always valid when indices are in-bounds per the memref spec.
43/// nuw requires every index*stride term to not unsigned-wrap, which holds iff
44/// all strides are statically non-negative. Negative strides would make the
45/// intermediate mul nuw overflow (e.g., idx * (-1 as u64) wraps for idx > 0).
46static LLVM::GEPNoWrapFlags getLoadStoreNoWrapFlags(MemRefType type) {
47 auto [strides, offset] = type.getStridesAndOffset();
48 LLVM::GEPNoWrapFlags flags = LLVM::GEPNoWrapFlags::inbounds;
49 if (llvm::all_of(strides, [](int64_t s) {
50 return !ShapedType::isDynamic(s) && s >= 0;
51 }))
52 flags = flags | LLVM::GEPNoWrapFlags::nuw;
53 return flags;
54}
55
56namespace {
57
58static bool isStaticStrideOrOffset(int64_t strideOrOffset) {
59 return ShapedType::isStatic(strideOrOffset);
60}
61
62static FailureOr<LLVM::LLVMFuncOp>
63getFreeFn(OpBuilder &b, const LLVMTypeConverter *typeConverter,
64 Operation *module, SymbolTableCollection *symbolTables) {
65 bool useGenericFn = typeConverter->getOptions().useGenericFunctions;
66
67 if (useGenericFn)
68 return LLVM::lookupOrCreateGenericFreeFn(b, module, symbolTables);
69
70 return LLVM::lookupOrCreateFreeFn(b, module, symbolTables);
71}
72
73static FailureOr<LLVM::LLVMFuncOp>
74getNotalignedAllocFn(OpBuilder &b, const LLVMTypeConverter *typeConverter,
75 Operation *module, Type indexType,
76 SymbolTableCollection *symbolTables) {
77 bool useGenericFn = typeConverter->getOptions().useGenericFunctions;
78 if (useGenericFn)
79 return LLVM::lookupOrCreateGenericAllocFn(b, module, indexType,
80 symbolTables);
81
82 return LLVM::lookupOrCreateMallocFn(b, module, indexType, symbolTables);
83}
84
85static FailureOr<LLVM::LLVMFuncOp>
86getAlignedAllocFn(OpBuilder &b, const LLVMTypeConverter *typeConverter,
87 Operation *module, Type indexType,
88 SymbolTableCollection *symbolTables) {
89 bool useGenericFn = typeConverter->getOptions().useGenericFunctions;
90
91 if (useGenericFn)
92 return LLVM::lookupOrCreateGenericAlignedAllocFn(b, module, indexType,
93 symbolTables);
94
95 return LLVM::lookupOrCreateAlignedAllocFn(b, module, indexType, symbolTables);
96}
97
98/// Computes the aligned value for 'input' as follows:
99/// bumped = input + alignement - 1
100/// aligned = bumped - bumped % alignment
101static Value createAligned(ConversionPatternRewriter &rewriter, Location loc,
102 Value input, Value alignment) {
103 Value one = LLVM::ConstantOp::create(rewriter, loc, alignment.getType(),
104 rewriter.getIndexAttr(1));
105 Value bump = LLVM::SubOp::create(rewriter, loc, alignment, one);
106 Value bumped = LLVM::AddOp::create(rewriter, loc, input, bump);
107 Value mod = LLVM::URemOp::create(rewriter, loc, bumped, alignment);
108 return LLVM::SubOp::create(rewriter, loc, bumped, mod);
109}
110
111/// Computes the byte size for the MemRef element type.
112static unsigned getMemRefEltSizeInBytes(const LLVMTypeConverter *typeConverter,
113 MemRefType memRefType, Operation *op,
114 const DataLayout *defaultLayout) {
115 const DataLayout *layout = defaultLayout;
116 if (const DataLayoutAnalysis *analysis =
117 typeConverter->getDataLayoutAnalysis()) {
118 layout = &analysis->getAbove(op);
119 }
120 Type elementType = memRefType.getElementType();
121 if (auto memRefElementType = dyn_cast<MemRefType>(elementType))
122 return typeConverter->getMemRefDescriptorSize(memRefElementType, *layout);
123 if (auto memRefElementType = dyn_cast<UnrankedMemRefType>(elementType))
124 return typeConverter->getUnrankedMemRefDescriptorSize(memRefElementType,
125 *layout);
126 return layout->getTypeSize(elementType);
127}
128
129static Value castAllocFuncResult(ConversionPatternRewriter &rewriter,
130 Location loc, Value allocatedPtr,
131 MemRefType memRefType, Type elementPtrType,
132 const LLVMTypeConverter &typeConverter) {
133 auto allocatedPtrTy = cast<LLVM::LLVMPointerType>(allocatedPtr.getType());
134 FailureOr<unsigned> maybeMemrefAddrSpace =
135 typeConverter.getMemRefAddressSpace(memRefType);
136 assert(succeeded(maybeMemrefAddrSpace) && "unsupported address space");
137 unsigned memrefAddrSpace = *maybeMemrefAddrSpace;
138 if (allocatedPtrTy.getAddressSpace() != memrefAddrSpace)
139 allocatedPtr = LLVM::AddrSpaceCastOp::create(
140 rewriter, loc,
141 LLVM::LLVMPointerType::get(rewriter.getContext(), memrefAddrSpace),
142 allocatedPtr);
143 return allocatedPtr;
144}
145
146class AllocOpLowering : public ConvertOpToLLVMPattern<memref::AllocOp> {
147 SymbolTableCollection *symbolTables = nullptr;
148
149public:
150 explicit AllocOpLowering(const LLVMTypeConverter &typeConverter,
151 SymbolTableCollection *symbolTables = nullptr,
152 PatternBenefit benefit = 1)
153 : ConvertOpToLLVMPattern<memref::AllocOp>(typeConverter, benefit),
154 symbolTables(symbolTables) {}
155
156 LogicalResult
157 matchAndRewrite(memref::AllocOp op, OpAdaptor adaptor,
158 ConversionPatternRewriter &rewriter) const override {
159 auto loc = op.getLoc();
160 MemRefType memRefType = op.getType();
161 if (!isConvertibleAndHasIdentityMaps(memRefType))
162 return rewriter.notifyMatchFailure(op, "incompatible memref type");
163
164 // Get or insert alloc function into the module.
165 FailureOr<LLVM::LLVMFuncOp> allocFuncOp =
166 getNotalignedAllocFn(rewriter, getTypeConverter(),
167 op->getParentWithTrait<OpTrait::SymbolTable>(),
168 getIndexType(), symbolTables);
169 if (failed(allocFuncOp))
170 return failure();
171
172 // Get actual sizes of the memref as values: static sizes are constant
173 // values and dynamic sizes are passed to 'alloc' as operands. In case of
174 // zero-dimensional memref, assume a scalar (size 1).
175 SmallVector<Value, 4> sizes;
176 SmallVector<Value, 4> strides;
177 Value sizeBytes;
178
179 this->getMemRefDescriptorSizes(loc, memRefType, adaptor.getOperands(),
180 rewriter, sizes, strides, sizeBytes, true);
181
182 Value alignment = getAlignment(rewriter, loc, op);
183 if (alignment) {
184 // Adjust the allocation size to consider alignment.
185 sizeBytes = LLVM::AddOp::create(rewriter, loc, sizeBytes, alignment);
186 }
187
188 // Allocate the underlying buffer.
189 Type elementPtrType = this->getElementPtrType(memRefType);
190 assert(elementPtrType && "could not compute element ptr type");
191 auto results =
192 LLVM::CallOp::create(rewriter, loc, allocFuncOp.value(), sizeBytes);
193
194 Value allocatedPtr =
195 castAllocFuncResult(rewriter, loc, results.getResult(), memRefType,
196 elementPtrType, *getTypeConverter());
197 Value alignedPtr = allocatedPtr;
198 if (alignment) {
199 // Compute the aligned pointer.
200 Value allocatedInt =
201 LLVM::PtrToIntOp::create(rewriter, loc, getIndexType(), allocatedPtr);
202 Value alignmentInt =
203 createAligned(rewriter, loc, allocatedInt, alignment);
204 alignedPtr =
205 LLVM::IntToPtrOp::create(rewriter, loc, elementPtrType, alignmentInt);
206 }
207
208 // Create the MemRef descriptor.
209 auto memRefDescriptor = this->createMemRefDescriptor(
210 loc, memRefType, allocatedPtr, alignedPtr, sizes, strides, rewriter);
211
212 // Return the final value of the descriptor.
213 rewriter.replaceOp(op, {memRefDescriptor});
214 return success();
215 }
216
217 /// Computes the alignment for the given memory allocation op.
218 template <typename OpType>
219 Value getAlignment(ConversionPatternRewriter &rewriter, Location loc,
220 OpType op) const {
221 MemRefType memRefType = op.getType();
222 Value alignment;
223 if (auto alignmentAttr = op.getAlignment()) {
224 Type indexType = getIndexType();
225 alignment =
226 createIndexAttrConstant(rewriter, loc, indexType, *alignmentAttr);
227 } else if (!memRefType.getElementType().isSignlessIntOrIndexOrFloat()) {
228 // In the case where no alignment is specified, we may want to override
229 // `malloc's` behavior. `malloc` typically aligns at the size of the
230 // biggest scalar on a target HW. For non-scalars, use the natural
231 // alignment of the LLVM type given by the LLVM DataLayout.
232 alignment = getSizeInBytes(loc, memRefType.getElementType(), rewriter);
233 }
234 return alignment;
235 }
236};
237
238class AlignedAllocOpLowering : public ConvertOpToLLVMPattern<memref::AllocOp> {
239 SymbolTableCollection *symbolTables = nullptr;
240
241public:
242 explicit AlignedAllocOpLowering(const LLVMTypeConverter &typeConverter,
243 SymbolTableCollection *symbolTables = nullptr,
244 PatternBenefit benefit = 1)
245 : ConvertOpToLLVMPattern<memref::AllocOp>(typeConverter, benefit),
246 symbolTables(symbolTables) {}
247
248 LogicalResult
249 matchAndRewrite(memref::AllocOp op, OpAdaptor adaptor,
250 ConversionPatternRewriter &rewriter) const override {
251 auto loc = op.getLoc();
252 MemRefType memRefType = op.getType();
253 if (!isConvertibleAndHasIdentityMaps(memRefType))
254 return rewriter.notifyMatchFailure(op, "incompatible memref type");
255
256 // Get or insert alloc function into module.
257 FailureOr<LLVM::LLVMFuncOp> allocFuncOp =
258 getAlignedAllocFn(rewriter, getTypeConverter(),
259 op->getParentWithTrait<OpTrait::SymbolTable>(),
260 getIndexType(), symbolTables);
261 if (failed(allocFuncOp))
262 return failure();
263
264 // Get actual sizes of the memref as values: static sizes are constant
265 // values and dynamic sizes are passed to 'alloc' as operands. In case of
266 // zero-dimensional memref, assume a scalar (size 1).
267 SmallVector<Value, 4> sizes;
268 SmallVector<Value, 4> strides;
269 Value sizeBytes;
270
271 this->getMemRefDescriptorSizes(loc, memRefType, adaptor.getOperands(),
272 rewriter, sizes, strides, sizeBytes, !false);
273
274 int64_t alignment = alignedAllocationGetAlignment(op, &defaultLayout);
275
276 Value allocAlignment =
277 createIndexAttrConstant(rewriter, loc, getIndexType(), alignment);
278
279 // Function aligned_alloc requires size to be a multiple of alignment; we
280 // pad the size to the next multiple if necessary.
281 if (!isMemRefSizeMultipleOf(memRefType, alignment, op, &defaultLayout))
282 sizeBytes = createAligned(rewriter, loc, sizeBytes, allocAlignment);
283
284 Type elementPtrType = this->getElementPtrType(memRefType);
285 auto results =
286 LLVM::CallOp::create(rewriter, loc, allocFuncOp.value(),
287 ValueRange({allocAlignment, sizeBytes}));
288
289 Value ptr =
290 castAllocFuncResult(rewriter, loc, results.getResult(), memRefType,
291 elementPtrType, *getTypeConverter());
292
293 // Create the MemRef descriptor.
294 auto memRefDescriptor = this->createMemRefDescriptor(
295 loc, memRefType, ptr, ptr, sizes, strides, rewriter);
296
297 // Return the final value of the descriptor.
298 rewriter.replaceOp(op, {memRefDescriptor});
299 return success();
300 }
301
302 /// The minimum alignment to use with aligned_alloc (has to be a power of 2).
303 static constexpr uint64_t kMinAlignedAllocAlignment = 16UL;
304
305 /// Computes the alignment for aligned_alloc used to allocate the buffer for
306 /// the memory allocation op.
307 ///
308 /// Aligned_alloc requires the allocation size to be a power of two, and the
309 /// allocation size to be a multiple of the alignment.
310 int64_t alignedAllocationGetAlignment(memref::AllocOp op,
311 const DataLayout *defaultLayout) const {
312 if (std::optional<uint64_t> alignment = op.getAlignment())
313 return *alignment;
314
315 // Whenever we don't have alignment set, we will use an alignment
316 // consistent with the element type; since the allocation size has to be a
317 // power of two, we will bump to the next power of two if it isn't.
318 unsigned eltSizeBytes = getMemRefEltSizeInBytes(
319 getTypeConverter(), op.getType(), op, defaultLayout);
320 return std::max(kMinAlignedAllocAlignment,
321 llvm::PowerOf2Ceil(eltSizeBytes));
322 }
323
324 /// Returns true if the memref size in bytes is known to be a multiple of
325 /// factor.
326 bool isMemRefSizeMultipleOf(MemRefType type, uint64_t factor, Operation *op,
327 const DataLayout *defaultLayout) const {
328 uint64_t sizeDivisor =
329 getMemRefEltSizeInBytes(getTypeConverter(), type, op, defaultLayout);
330 for (unsigned i = 0, e = type.getRank(); i < e; i++) {
331 if (type.isDynamicDim(i))
332 continue;
333 sizeDivisor = sizeDivisor * type.getDimSize(i);
334 }
335 return sizeDivisor % factor == 0;
336 }
337
338private:
339 /// Default layout to use in absence of the corresponding analysis.
340 DataLayout defaultLayout;
341};
342
343struct AllocaOpLowering : public ConvertOpToLLVMPattern<memref::AllocaOp> {
344 using ConvertOpToLLVMPattern<memref::AllocaOp>::ConvertOpToLLVMPattern;
345
346 /// Allocates the underlying buffer using the right call. `allocatedBytePtr`
347 /// is set to null for stack allocations. `accessAlignment` is set if
348 /// alignment is needed post allocation (for eg. in conjunction with malloc).
349 LogicalResult
350 matchAndRewrite(memref::AllocaOp op, OpAdaptor adaptor,
351 ConversionPatternRewriter &rewriter) const override {
352 auto loc = op.getLoc();
353 MemRefType memRefType = op.getType();
354 if (!isConvertibleAndHasIdentityMaps(memRefType))
355 return rewriter.notifyMatchFailure(op, "incompatible memref type");
356
357 // Get actual sizes of the memref as values: static sizes are constant
358 // values and dynamic sizes are passed to 'alloc' as operands. In case of
359 // zero-dimensional memref, assume a scalar (size 1).
360 SmallVector<Value, 4> sizes;
361 SmallVector<Value, 4> strides;
362 Value size;
363
364 this->getMemRefDescriptorSizes(loc, memRefType, adaptor.getOperands(),
365 rewriter, sizes, strides, size, !true);
366
367 // With alloca, one gets a pointer to the element type right away.
368 // For stack allocations.
369 auto elementType =
370 typeConverter->convertType(op.getType().getElementType());
371 FailureOr<unsigned> maybeAddressSpace =
372 getTypeConverter()->getMemRefAddressSpace(op.getType());
373 assert(succeeded(maybeAddressSpace) && "unsupported address space");
374 unsigned addrSpace = *maybeAddressSpace;
375 auto elementPtrType =
376 LLVM::LLVMPointerType::get(rewriter.getContext(), addrSpace);
377
378 auto allocatedElementPtr =
379 LLVM::AllocaOp::create(rewriter, loc, elementPtrType, elementType, size,
380 op.getAlignment().value_or(0));
381
382 // Create the MemRef descriptor.
383 auto memRefDescriptor = this->createMemRefDescriptor(
384 loc, memRefType, allocatedElementPtr, allocatedElementPtr, sizes,
385 strides, rewriter);
386
387 // Return the final value of the descriptor.
388 rewriter.replaceOp(op, {memRefDescriptor});
389 return success();
390 }
391};
392
393struct AllocaScopeOpLowering
394 : public ConvertOpToLLVMPattern<memref::AllocaScopeOp> {
395 using ConvertOpToLLVMPattern<memref::AllocaScopeOp>::ConvertOpToLLVMPattern;
396
397 LogicalResult
398 matchAndRewrite(memref::AllocaScopeOp allocaScopeOp, OpAdaptor adaptor,
399 ConversionPatternRewriter &rewriter) const override {
400 OpBuilder::InsertionGuard guard(rewriter);
401 Location loc = allocaScopeOp.getLoc();
402
403 // Split the current block before the AllocaScopeOp to create the inlining
404 // point.
405 auto *currentBlock = rewriter.getInsertionBlock();
406 auto *remainingOpsBlock =
407 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
408 Block *continueBlock;
409 if (allocaScopeOp.getNumResults() == 0) {
410 continueBlock = remainingOpsBlock;
411 } else {
412 continueBlock = rewriter.createBlock(
413 remainingOpsBlock, allocaScopeOp.getResultTypes(),
414 SmallVector<Location>(allocaScopeOp->getNumResults(),
415 allocaScopeOp.getLoc()));
416 LLVM::BrOp::create(rewriter, loc, ValueRange(), remainingOpsBlock);
417 }
418
419 // Inline body region.
420 Block *beforeBody = &allocaScopeOp.getBodyRegion().front();
421 Block *afterBody = &allocaScopeOp.getBodyRegion().back();
422 rewriter.inlineRegionBefore(allocaScopeOp.getBodyRegion(), continueBlock);
423
424 // Save stack and then branch into the body of the region.
425 rewriter.setInsertionPointToEnd(currentBlock);
426 auto stackSaveOp = LLVM::StackSaveOp::create(rewriter, loc, getPtrType());
427 LLVM::BrOp::create(rewriter, loc, ValueRange(), beforeBody);
428
429 // Replace the alloca_scope return with a branch that jumps out of the body.
430 // Stack restore before leaving the body region.
431 rewriter.setInsertionPointToEnd(afterBody);
432 auto returnOp =
433 cast<memref::AllocaScopeReturnOp>(afterBody->getTerminator());
434 auto branchOp = rewriter.replaceOpWithNewOp<LLVM::BrOp>(
435 returnOp, returnOp.getResults(), continueBlock);
436
437 // Insert stack restore before jumping out the body of the region.
438 rewriter.setInsertionPoint(branchOp);
439 LLVM::StackRestoreOp::create(rewriter, loc, stackSaveOp);
440
441 // Replace the op with values return from the body region.
442 rewriter.replaceOp(allocaScopeOp, continueBlock->getArguments());
443
444 return success();
445 }
446};
447
448struct AssumeAlignmentOpLowering
449 : public ConvertOpToLLVMPattern<memref::AssumeAlignmentOp> {
450 using ConvertOpToLLVMPattern<
451 memref::AssumeAlignmentOp>::ConvertOpToLLVMPattern;
452 explicit AssumeAlignmentOpLowering(const LLVMTypeConverter &converter)
453 : ConvertOpToLLVMPattern<memref::AssumeAlignmentOp>(converter) {}
454
455 LogicalResult
456 matchAndRewrite(memref::AssumeAlignmentOp op, OpAdaptor adaptor,
457 ConversionPatternRewriter &rewriter) const override {
458 Value memref = adaptor.getMemref();
459 unsigned alignment = op.getAlignment();
460 auto loc = op.getLoc();
461
462 auto srcMemRefType = cast<MemRefType>(op.getMemref().getType());
463 Value ptr = getStridedElementPtr(rewriter, loc, srcMemRefType, memref,
464 /*indices=*/{});
465
466 // Emit llvm.assume(true) ["align"(memref, alignment)].
467 // This is more direct than ptrtoint-based checks, is explicitly supported,
468 // and works with non-integral address spaces.
469 Value trueCond =
470 LLVM::ConstantOp::create(rewriter, loc, rewriter.getBoolAttr(true));
471 Value alignmentConst =
472 createIndexAttrConstant(rewriter, loc, getIndexType(), alignment);
473 LLVM::AssumeOp::create(rewriter, loc, trueCond, LLVM::AssumeAlignTag(), ptr,
474 alignmentConst);
475 rewriter.replaceOp(op, memref);
476 return success();
477 }
478};
479
480struct DistinctObjectsOpLowering
481 : public ConvertOpToLLVMPattern<memref::DistinctObjectsOp> {
482 using ConvertOpToLLVMPattern<
483 memref::DistinctObjectsOp>::ConvertOpToLLVMPattern;
484 explicit DistinctObjectsOpLowering(const LLVMTypeConverter &converter)
485 : ConvertOpToLLVMPattern<memref::DistinctObjectsOp>(converter) {}
486
487 LogicalResult
488 matchAndRewrite(memref::DistinctObjectsOp op, OpAdaptor adaptor,
489 ConversionPatternRewriter &rewriter) const override {
490 ValueRange operands = adaptor.getOperands();
491 if (operands.size() <= 1) {
492 // Fast path.
493 rewriter.replaceOp(op, operands);
494 return success();
495 }
496
497 Location loc = op.getLoc();
498 SmallVector<Value> ptrs;
499 for (auto [origOperand, newOperand] :
500 llvm::zip_equal(op.getOperands(), operands)) {
501 auto memrefType = cast<MemRefType>(origOperand.getType());
502 MemRefDescriptor memRefDescriptor(newOperand);
503 Value ptr = memRefDescriptor.bufferPtr(rewriter, loc, *getTypeConverter(),
504 memrefType);
505 ptrs.push_back(ptr);
506 }
507
508 auto cond =
509 LLVM::ConstantOp::create(rewriter, loc, rewriter.getI1Type(), 1);
510 // Generate separate_storage assumptions for each pair of pointers.
511 for (auto i : llvm::seq<size_t>(ptrs.size() - 1)) {
512 for (auto j : llvm::seq<size_t>(i + 1, ptrs.size())) {
513 Value ptr1 = ptrs[i];
514 Value ptr2 = ptrs[j];
515 LLVM::AssumeOp::create(rewriter, loc, cond,
516 LLVM::AssumeSeparateStorageTag{}, ptr1, ptr2);
517 }
518 }
519
520 rewriter.replaceOp(op, operands);
521 return success();
522 }
523};
524
525// A `dealloc` is converted into a call to `free` on the underlying data buffer.
526// The memref descriptor being an SSA value, there is no need to clean it up
527// in any way.
528class DeallocOpLowering : public ConvertOpToLLVMPattern<memref::DeallocOp> {
529 SymbolTableCollection *symbolTables = nullptr;
530
531public:
532 explicit DeallocOpLowering(const LLVMTypeConverter &typeConverter,
533 SymbolTableCollection *symbolTables = nullptr,
534 PatternBenefit benefit = 1)
535 : ConvertOpToLLVMPattern<memref::DeallocOp>(typeConverter, benefit),
536 symbolTables(symbolTables) {}
537
538 LogicalResult
539 matchAndRewrite(memref::DeallocOp op, OpAdaptor adaptor,
540 ConversionPatternRewriter &rewriter) const override {
541 // Insert the `free` declaration if it is not already present.
542 FailureOr<LLVM::LLVMFuncOp> freeFunc =
543 getFreeFn(rewriter, getTypeConverter(),
544 op->getParentWithTrait<OpTrait::SymbolTable>(), symbolTables);
545 if (failed(freeFunc))
546 return failure();
547 Value allocatedPtr;
548 if (auto unrankedTy =
549 llvm::dyn_cast<UnrankedMemRefType>(op.getMemref().getType())) {
550 auto elementPtrTy = LLVM::LLVMPointerType::get(
551 rewriter.getContext(), unrankedTy.getMemorySpaceAsInt());
553 rewriter, op.getLoc(),
554 UnrankedMemRefDescriptor(adaptor.getMemref())
555 .memRefDescPtr(rewriter, op.getLoc()),
556 elementPtrTy);
557 } else {
558 allocatedPtr = MemRefDescriptor(adaptor.getMemref())
559 .allocatedPtr(rewriter, op.getLoc());
560 }
561 rewriter.replaceOpWithNewOp<LLVM::CallOp>(op, freeFunc.value(),
562 allocatedPtr);
563 return success();
564 }
565};
566
567// A `dim` is converted to a constant for static sizes and to an access to the
568// size stored in the memref descriptor for dynamic sizes.
569struct DimOpLowering : public ConvertOpToLLVMPattern<memref::DimOp> {
570 using ConvertOpToLLVMPattern<memref::DimOp>::ConvertOpToLLVMPattern;
571
572 LogicalResult
573 matchAndRewrite(memref::DimOp dimOp, OpAdaptor adaptor,
574 ConversionPatternRewriter &rewriter) const override {
575 Type operandType = dimOp.getSource().getType();
576 if (isa<UnrankedMemRefType>(operandType)) {
577 FailureOr<Value> extractedSize = extractSizeOfUnrankedMemRef(
578 operandType, dimOp, adaptor.getOperands(), rewriter);
579 if (failed(extractedSize))
580 return failure();
581 rewriter.replaceOp(dimOp, {*extractedSize});
582 return success();
583 }
584 if (isa<MemRefType>(operandType)) {
585 rewriter.replaceOp(
586 dimOp, {extractSizeOfRankedMemRef(operandType, dimOp,
587 adaptor.getOperands(), rewriter)});
588 return success();
589 }
590 llvm_unreachable("expected MemRefType or UnrankedMemRefType");
591 }
592
593private:
594 FailureOr<Value>
595 extractSizeOfUnrankedMemRef(Type operandType, memref::DimOp dimOp,
596 OpAdaptor adaptor,
597 ConversionPatternRewriter &rewriter) const {
598 Location loc = dimOp.getLoc();
599
600 auto unrankedMemRefType = cast<UnrankedMemRefType>(operandType);
601 auto scalarMemRefType =
602 MemRefType::get({}, unrankedMemRefType.getElementType());
603 FailureOr<unsigned> maybeAddressSpace =
604 getTypeConverter()->getMemRefAddressSpace(unrankedMemRefType);
605 if (failed(maybeAddressSpace)) {
606 dimOp.emitOpError("memref memory space must be convertible to an integer "
607 "address space");
608 return failure();
609 }
610 unsigned addressSpace = *maybeAddressSpace;
611
612 // Extract pointer to the underlying ranked descriptor and bitcast it to a
613 // memref<element_type> descriptor pointer to minimize the number of GEP
614 // operations.
615 UnrankedMemRefDescriptor unrankedDesc(adaptor.getSource());
616 Value underlyingRankedDesc = unrankedDesc.memRefDescPtr(rewriter, loc);
617
618 Type elementType = typeConverter->convertType(scalarMemRefType);
619
620 // Get pointer to offset field of memref<element_type> descriptor.
621 auto indexPtrTy =
622 LLVM::LLVMPointerType::get(rewriter.getContext(), addressSpace);
623 Value offsetPtr =
624 LLVM::GEPOp::create(rewriter, loc, indexPtrTy, elementType,
625 underlyingRankedDesc, ArrayRef<LLVM::GEPArg>{0, 2});
626
627 // The size value that we have to extract can be obtained using GEPop with
628 // `dimOp.index() + 1` index argument.
629 Value idxPlusOne = LLVM::AddOp::create(
630 rewriter, loc,
631 createIndexAttrConstant(rewriter, loc, getIndexType(), 1),
632 adaptor.getIndex());
633 Value sizePtr = LLVM::GEPOp::create(rewriter, loc, indexPtrTy,
634 getTypeConverter()->getIndexType(),
635 offsetPtr, idxPlusOne);
636 return LLVM::LoadOp::create(rewriter, loc,
637 getTypeConverter()->getIndexType(), sizePtr)
638 .getResult();
639 }
640
641 std::optional<int64_t> getConstantDimIndex(memref::DimOp dimOp) const {
642 if (auto idx = dimOp.getConstantIndex())
643 return idx;
644
645 if (auto constantOp = dimOp.getIndex().getDefiningOp<LLVM::ConstantOp>())
646 return cast<IntegerAttr>(constantOp.getValue()).getValue().getSExtValue();
647
648 return std::nullopt;
649 }
650
651 Value extractSizeOfRankedMemRef(Type operandType, memref::DimOp dimOp,
652 OpAdaptor adaptor,
653 ConversionPatternRewriter &rewriter) const {
654 Location loc = dimOp.getLoc();
655
656 // Take advantage if index is constant.
657 MemRefType memRefType = cast<MemRefType>(operandType);
658 Type indexType = getIndexType();
659 if (std::optional<int64_t> index = getConstantDimIndex(dimOp)) {
660 int64_t i = *index;
661 if (i >= 0 && i < memRefType.getRank()) {
662 if (memRefType.isDynamicDim(i)) {
663 // extract dynamic size from the memref descriptor.
664 MemRefDescriptor descriptor(adaptor.getSource());
665 return descriptor.size(rewriter, loc, i);
666 }
667 // Use constant for static size.
668 int64_t dimSize = memRefType.getDimSize(i);
669 return createIndexAttrConstant(rewriter, loc, indexType, dimSize);
670 }
671 }
672 Value index = adaptor.getIndex();
673 int64_t rank = memRefType.getRank();
674 MemRefDescriptor memrefDescriptor(adaptor.getSource());
675 return memrefDescriptor.size(rewriter, loc, index, rank);
676 }
677};
678
679/// Common base for load and store operations on MemRefs. Restricts the match
680/// to supported MemRef types. Provides functionality to emit code accessing a
681/// specific element of the underlying data buffer.
682template <typename Derived>
683struct LoadStoreOpLowering : public ConvertOpToLLVMPattern<Derived> {
684 using ConvertOpToLLVMPattern<Derived>::ConvertOpToLLVMPattern;
685 using ConvertOpToLLVMPattern<Derived>::isConvertibleAndHasIdentityMaps;
686 using Base = LoadStoreOpLowering<Derived>;
687};
688
689/// Wrap a llvm.cmpxchg operation in a while loop so that the operation can be
690/// retried until it succeeds in atomically storing a new value into memory.
691///
692/// +---------------------------------+
693/// | <code before the AtomicRMWOp> |
694/// | <compute initial %loaded> |
695/// | cf.br loop(%loaded) |
696/// +---------------------------------+
697/// |
698/// -------| |
699/// | v v
700/// | +--------------------------------+
701/// | | loop(%loaded): |
702/// | | <body contents> |
703/// | | %pair = cmpxchg |
704/// | | %ok = %pair[0] |
705/// | | %new = %pair[1] |
706/// | | cf.cond_br %ok, end, loop(%new) |
707/// | +--------------------------------+
708/// | | |
709/// |----------- |
710/// v
711/// +--------------------------------+
712/// | end: |
713/// | <code after the AtomicRMWOp> |
714/// +--------------------------------+
715///
716struct GenericAtomicRMWOpLowering
717 : public LoadStoreOpLowering<memref::GenericAtomicRMWOp> {
718 using Base::Base;
719
720 LogicalResult
721 matchAndRewrite(memref::GenericAtomicRMWOp atomicOp, OpAdaptor adaptor,
722 ConversionPatternRewriter &rewriter) const override {
723 auto loc = atomicOp.getLoc();
724 Type valueType = typeConverter->convertType(atomicOp.getResult().getType());
725
726 // `llvm.cmpxchg` only supports integer or pointer operands. For
727 // floating-point element types, perform the CAS on a same-width integer
728 // and bitcast at the boundaries.
729 bool needsBitcast = isa<FloatType>(valueType);
730 Type cmpxchgType = valueType;
731 if (needsBitcast) {
732 unsigned bitWidth = cast<FloatType>(valueType).getWidth();
733 cmpxchgType = rewriter.getIntegerType(bitWidth);
734 }
735
736 // Split the block into initial, loop, and ending parts.
737 auto *initBlock = rewriter.getInsertionBlock();
738 auto *loopBlock = rewriter.splitBlock(initBlock, Block::iterator(atomicOp));
739 loopBlock->addArgument(cmpxchgType, loc);
740
741 auto *endBlock =
742 rewriter.splitBlock(loopBlock, Block::iterator(atomicOp)++);
743
744 // Compute the loaded value and branch to the loop block.
745 rewriter.setInsertionPointToEnd(initBlock);
746 auto memRefType = cast<MemRefType>(atomicOp.getMemref().getType());
747 auto dataPtr = getStridedElementPtr(
748 rewriter, loc, memRefType, adaptor.getMemref(), adaptor.getIndices());
749 Value init = LLVM::LoadOp::create(
750 rewriter, loc, typeConverter->convertType(memRefType.getElementType()),
751 dataPtr);
752 if (needsBitcast)
753 init = LLVM::BitcastOp::create(rewriter, loc, cmpxchgType, init);
754 LLVM::BrOp::create(rewriter, loc, init, loopBlock);
755
756 // Prepare the body of the loop block.
757 rewriter.setInsertionPointToStart(loopBlock);
758
759 // Clone the GenericAtomicRMWOp region and extract the result.
760 Value loopArgument = loopBlock->getArgument(0);
761 Value loopArgForBody = loopArgument;
762 if (needsBitcast)
763 loopArgForBody =
764 LLVM::BitcastOp::create(rewriter, loc, valueType, loopArgument);
765 IRMapping mapping;
766 mapping.map(atomicOp.getCurrentValue(), loopArgForBody);
767 Block &entryBlock = atomicOp.body().front();
768 for (auto &nestedOp : entryBlock.without_terminator()) {
769 Operation *clone = rewriter.clone(nestedOp, mapping);
770 mapping.map(nestedOp.getResults(), clone->getResults());
771 }
772
773 Value result =
774 mapping.lookupOrNull(entryBlock.getTerminator()->getOperand(0));
775 if (!result) {
776 return atomicOp.emitError("result not defined in region");
777 }
778 if (needsBitcast)
779 result = LLVM::BitcastOp::create(rewriter, loc, cmpxchgType, result);
780
781 // Prepare the epilog of the loop block.
782 // Append the cmpxchg op to the end of the loop block.
783 auto successOrdering = LLVM::AtomicOrdering::acq_rel;
784 auto failureOrdering = LLVM::AtomicOrdering::monotonic;
785 auto cmpxchg =
786 LLVM::AtomicCmpXchgOp::create(rewriter, loc, dataPtr, loopArgument,
787 result, successOrdering, failureOrdering);
788 // Extract the %new_loaded and %ok values from the pair.
789 Value newLoaded = LLVM::ExtractValueOp::create(rewriter, loc, cmpxchg, 0);
790 Value ok = LLVM::ExtractValueOp::create(rewriter, loc, cmpxchg, 1);
791
792 // Conditionally branch to the end or back to the loop depending on %ok.
793 LLVM::CondBrOp::create(rewriter, loc, ok, endBlock, ArrayRef<Value>(),
794 loopBlock, newLoaded);
795
796 // The 'result' of the atomic_rmw op is the newly loaded value. Bitcast
797 // back to the float type if needed. Insert at the start of `endBlock` so
798 // the bitcast precedes the existing terminator (split into endBlock).
799 if (needsBitcast) {
800 rewriter.setInsertionPointToStart(endBlock);
801 newLoaded = LLVM::BitcastOp::create(rewriter, loc, valueType, newLoaded);
802 }
803 rewriter.setInsertionPointToEnd(endBlock);
804 rewriter.replaceOp(atomicOp, {newLoaded});
805
806 return success();
807 }
808};
809
810/// Returns the LLVM type of the global variable given the memref type `type`.
811static Type
812convertGlobalMemrefTypeToLLVM(MemRefType type,
813 const LLVMTypeConverter &typeConverter) {
814 // LLVM type for a global memref will be a multi-dimension array. For
815 // declarations or uninitialized global memrefs, we can potentially flatten
816 // this to a 1D array. However, for memref.global's with an initial value,
817 // we do not intend to flatten the ElementsAttribute when going from std ->
818 // LLVM dialect, so the LLVM type needs to me a multi-dimension array.
819 Type elementType = typeConverter.convertType(type.getElementType());
820 Type arrayTy = elementType;
821 // Shape has the outermost dim at index 0, so need to walk it backwards
822 for (int64_t dim : llvm::reverse(type.getShape()))
823 arrayTy = LLVM::LLVMArrayType::get(arrayTy, dim);
824 return arrayTy;
825}
826
827/// GlobalMemrefOp is lowered to a LLVM Global Variable.
828class GlobalMemrefOpLowering : public ConvertOpToLLVMPattern<memref::GlobalOp> {
829 SymbolTableCollection *symbolTables = nullptr;
830
831public:
832 explicit GlobalMemrefOpLowering(const LLVMTypeConverter &typeConverter,
833 SymbolTableCollection *symbolTables = nullptr,
834 PatternBenefit benefit = 1)
835 : ConvertOpToLLVMPattern<memref::GlobalOp>(typeConverter, benefit),
836 symbolTables(symbolTables) {}
837
838 LogicalResult
839 matchAndRewrite(memref::GlobalOp global, OpAdaptor adaptor,
840 ConversionPatternRewriter &rewriter) const override {
841 MemRefType type = global.getType();
842 if (!isConvertibleAndHasIdentityMaps(type))
843 return failure();
844
845 Type arrayTy = convertGlobalMemrefTypeToLLVM(type, *getTypeConverter());
846
847 LLVM::Linkage linkage =
848 global.isPublic() ? LLVM::Linkage::External : LLVM::Linkage::Private;
849 bool isExternal = global.isExternal();
850 bool isUninitialized = global.isUninitialized();
851
852 Attribute initialValue = nullptr;
853 if (!isExternal && !isUninitialized) {
854 auto elementsAttr = llvm::cast<ElementsAttr>(*global.getInitialValue());
855 initialValue = elementsAttr;
856
857 // For scalar memrefs, the global variable created is of the element type,
858 // so unpack the elements attribute to extract the value.
859 if (type.getRank() == 0)
860 initialValue = elementsAttr.getSplatValue<Attribute>();
861 }
862
863 uint64_t alignment = global.getAlignment().value_or(0);
864 FailureOr<unsigned> addressSpace =
865 getTypeConverter()->getMemRefAddressSpace(type);
866 if (failed(addressSpace))
867 return global.emitOpError(
868 "memory space cannot be converted to an integer address space");
869
870 // Remove old operation from symbol table.
871 SymbolTable *symbolTable = nullptr;
872 if (symbolTables) {
873 Operation *symbolTableOp =
874 global->getParentWithTrait<OpTrait::SymbolTable>();
875 symbolTable = &symbolTables->getSymbolTable(symbolTableOp);
876 symbolTable->remove(global);
877 }
878
879 // Create new operation.
880 auto newGlobal = rewriter.replaceOpWithNewOp<LLVM::GlobalOp>(
881 global, arrayTy, global.getConstant(), linkage, global.getSymName(),
882 initialValue, alignment, *addressSpace);
883
884 // Insert new operation into symbol table.
885 if (symbolTable)
886 symbolTable->insert(newGlobal, rewriter.getInsertionPoint());
887
888 if (!isExternal && isUninitialized) {
889 rewriter.createBlock(&newGlobal.getInitializerRegion());
890 Value undef[] = {
891 LLVM::UndefOp::create(rewriter, newGlobal.getLoc(), arrayTy)};
892 LLVM::ReturnOp::create(rewriter, newGlobal.getLoc(), undef);
893 }
894 return success();
895 }
896};
897
898/// GetGlobalMemrefOp is lowered into a Memref descriptor with the pointer to
899/// the first element stashed into the descriptor. This reuses
900/// `AllocLikeOpLowering` to reuse the Memref descriptor construction.
901struct GetGlobalMemrefOpLowering
902 : public ConvertOpToLLVMPattern<memref::GetGlobalOp> {
903 using ConvertOpToLLVMPattern<memref::GetGlobalOp>::ConvertOpToLLVMPattern;
904
905 /// Buffer "allocation" for memref.get_global op is getting the address of
906 /// the global variable referenced.
907 LogicalResult
908 matchAndRewrite(memref::GetGlobalOp op, OpAdaptor adaptor,
909 ConversionPatternRewriter &rewriter) const override {
910 auto loc = op.getLoc();
911 MemRefType memRefType = op.getType();
912 if (!isConvertibleAndHasIdentityMaps(memRefType))
913 return rewriter.notifyMatchFailure(op, "incompatible memref type");
914
915 // Get actual sizes of the memref as values: static sizes are constant
916 // values and dynamic sizes are passed to 'alloc' as operands. In case of
917 // zero-dimensional memref, assume a scalar (size 1).
918 SmallVector<Value, 4> sizes;
919 SmallVector<Value, 4> strides;
920 Value sizeBytes;
921
922 this->getMemRefDescriptorSizes(loc, memRefType, adaptor.getOperands(),
923 rewriter, sizes, strides, sizeBytes, !false);
924
925 MemRefType type = cast<MemRefType>(op.getResult().getType());
926
927 // This is called after a type conversion, which would have failed if this
928 // call fails.
929 FailureOr<unsigned> maybeAddressSpace =
930 getTypeConverter()->getMemRefAddressSpace(type);
931 assert(succeeded(maybeAddressSpace) && "unsupported address space");
932 unsigned memSpace = *maybeAddressSpace;
933
934 Type arrayTy = convertGlobalMemrefTypeToLLVM(type, *getTypeConverter());
935 auto ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext(), memSpace);
936 auto addressOf =
937 LLVM::AddressOfOp::create(rewriter, loc, ptrTy, op.getName());
938
939 // Get the address of the first element in the array by creating a GEP with
940 // the address of the GV as the base, and (rank + 1) number of 0 indices.
941 auto gep =
942 LLVM::GEPOp::create(rewriter, loc, ptrTy, arrayTy, addressOf,
943 SmallVector<LLVM::GEPArg>(type.getRank() + 1, 0));
944
945 // We do not expect the memref obtained using `memref.get_global` to be
946 // ever deallocated. Set the allocated pointer to be known bad value to
947 // help debug if that ever happens.
948 auto intPtrType = getIntPtrType(memSpace);
949 Value deadBeefConst =
950 createIndexAttrConstant(rewriter, op->getLoc(), intPtrType, 0xdeadbeef);
951 auto deadBeefPtr =
952 LLVM::IntToPtrOp::create(rewriter, loc, ptrTy, deadBeefConst);
953
954 // Both allocated and aligned pointers are same. We could potentially stash
955 // a nullptr for the allocated pointer since we do not expect any dealloc.
956 // Create the MemRef descriptor.
957 auto memRefDescriptor = this->createMemRefDescriptor(
958 loc, memRefType, deadBeefPtr, gep, sizes, strides, rewriter);
959
960 // Return the final value of the descriptor.
961 rewriter.replaceOp(op, {memRefDescriptor});
962 return success();
963 }
964};
965
966// Load operation is lowered to obtaining a pointer to the indexed element
967// and loading it.
968struct LoadOpLowering : public LoadStoreOpLowering<memref::LoadOp> {
969 using Base::Base;
970
971 LogicalResult
972 matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
973 ConversionPatternRewriter &rewriter) const override {
974 auto type = loadOp.getMemRefType();
975
976 // Per memref.load spec, the indices must be in-bounds:
977 // 0 <= idx < dim_size, and additionally all offsets are non-negative,
978 // hence inbounds and nuw are used when lowering to llvm.getelementptr.
979 Value dataPtr = getStridedElementPtr(
980 rewriter, loadOp.getLoc(), type, adaptor.getMemref(),
981 adaptor.getIndices(), getLoadStoreNoWrapFlags(type));
982 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(
983 loadOp, typeConverter->convertType(type.getElementType()), dataPtr,
984 loadOp.getAlignment().value_or(0), false, loadOp.getNontemporal(),
985 /*isInvariant=*/loadOp.getInvariant());
986 return success();
987 }
988};
989
990// Store operation is lowered to obtaining a pointer to the indexed element,
991// and storing the given value to it.
992struct StoreOpLowering : public LoadStoreOpLowering<memref::StoreOp> {
993 using Base::Base;
994
995 LogicalResult
996 matchAndRewrite(memref::StoreOp op, OpAdaptor adaptor,
997 ConversionPatternRewriter &rewriter) const override {
998 auto type = op.getMemRefType();
999
1000 // Per memref.store spec, the indices must be in-bounds:
1001 // 0 <= idx < dim_size, and additionally all offsets are non-negative,
1002 // hence inbounds and nuw are used when lowering to llvm.getelementptr.
1003 Value dataPtr = getStridedElementPtr(
1004 rewriter, op.getLoc(), type, adaptor.getMemref(), adaptor.getIndices(),
1006 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getValue(), dataPtr,
1007 op.getAlignment().value_or(0),
1008 false, op.getNontemporal());
1009 return success();
1010 }
1011};
1012
1013// The prefetch operation is lowered in a way similar to the load operation
1014// except that the llvm.prefetch operation is used for replacement.
1015struct PrefetchOpLowering : public LoadStoreOpLowering<memref::PrefetchOp> {
1016 using Base::Base;
1017
1018 LogicalResult
1019 matchAndRewrite(memref::PrefetchOp prefetchOp, OpAdaptor adaptor,
1020 ConversionPatternRewriter &rewriter) const override {
1021 auto type = prefetchOp.getMemRefType();
1022 auto loc = prefetchOp.getLoc();
1023
1024 Value dataPtr = getStridedElementPtr(
1025 rewriter, loc, type, adaptor.getMemref(), adaptor.getIndices());
1026
1027 // Replace with llvm.prefetch.
1028 IntegerAttr isWrite = rewriter.getI32IntegerAttr(prefetchOp.getIsWrite());
1029 IntegerAttr localityHint = prefetchOp.getLocalityHintAttr();
1030 IntegerAttr isData =
1031 rewriter.getI32IntegerAttr(prefetchOp.getIsDataCache());
1032 rewriter.replaceOpWithNewOp<LLVM::Prefetch>(prefetchOp, dataPtr, isWrite,
1033 localityHint, isData);
1034 return success();
1035 }
1036};
1037
1038struct RankOpLowering : public ConvertOpToLLVMPattern<memref::RankOp> {
1039 using ConvertOpToLLVMPattern<memref::RankOp>::ConvertOpToLLVMPattern;
1040
1041 LogicalResult
1042 matchAndRewrite(memref::RankOp op, OpAdaptor adaptor,
1043 ConversionPatternRewriter &rewriter) const override {
1044 Location loc = op.getLoc();
1045 Type operandType = op.getMemref().getType();
1046 if (isa<UnrankedMemRefType>(operandType)) {
1047 UnrankedMemRefDescriptor desc(adaptor.getMemref());
1048 rewriter.replaceOp(op, {desc.rank(rewriter, loc)});
1049 return success();
1050 }
1051 if (auto rankedMemRefType = dyn_cast<MemRefType>(operandType)) {
1052 Type indexType = getIndexType();
1053 rewriter.replaceOp(op,
1054 {createIndexAttrConstant(rewriter, loc, indexType,
1055 rankedMemRefType.getRank())});
1056 return success();
1057 }
1058 return failure();
1059 }
1060};
1061
1062struct MemRefCastOpLowering : public ConvertOpToLLVMPattern<memref::CastOp> {
1064
1065 LogicalResult
1066 matchAndRewrite(memref::CastOp memRefCastOp, OpAdaptor adaptor,
1067 ConversionPatternRewriter &rewriter) const override {
1068 Type srcType = memRefCastOp.getOperand().getType();
1069 Type dstType = memRefCastOp.getType();
1070
1071 // memref::CastOp reduce to bitcast in the ranked MemRef case and can be
1072 // used for type erasure. For now they must preserve underlying element type
1073 // and require source and result type to have the same rank. Therefore,
1074 // perform a sanity check that the underlying structs are the same. Once op
1075 // semantics are relaxed we can revisit.
1076 if (isa<MemRefType>(srcType) && isa<MemRefType>(dstType))
1077 if (typeConverter->convertType(srcType) !=
1078 typeConverter->convertType(dstType))
1079 return failure();
1080
1081 // Unranked to unranked cast is disallowed
1082 if (isa<UnrankedMemRefType>(srcType) && isa<UnrankedMemRefType>(dstType))
1083 return failure();
1084
1085 auto targetStructType = typeConverter->convertType(memRefCastOp.getType());
1086 auto loc = memRefCastOp.getLoc();
1087
1088 // For ranked/ranked case, just keep the original descriptor.
1089 if (isa<MemRefType>(srcType) && isa<MemRefType>(dstType)) {
1090 rewriter.replaceOp(memRefCastOp, {adaptor.getSource()});
1091 return success();
1092 }
1093
1094 if (isa<MemRefType>(srcType) && isa<UnrankedMemRefType>(dstType)) {
1095 // Casting ranked to unranked memref type
1096 // Set the rank in the destination from the memref type
1097 // Allocate space on the stack and copy the src memref descriptor
1098 // Set the ptr in the destination to the stack space
1099 auto srcMemRefType = cast<MemRefType>(srcType);
1100 int64_t rank = srcMemRefType.getRank();
1101 // ptr = AllocaOp sizeof(MemRefDescriptor)
1102 auto ptr = getTypeConverter()->promoteOneMemRefDescriptor(
1103 loc, adaptor.getSource(), rewriter);
1104
1105 // rank = ConstantOp srcRank
1106 auto rankVal = LLVM::ConstantOp::create(rewriter, loc, getIndexType(),
1107 rewriter.getIndexAttr(rank));
1108 // poison = PoisonOp
1109 UnrankedMemRefDescriptor memRefDesc =
1110 UnrankedMemRefDescriptor::poison(rewriter, loc, targetStructType);
1111 // d1 = InsertValueOp poison, rank, 0
1112 memRefDesc.setRank(rewriter, loc, rankVal);
1113 // d2 = InsertValueOp d1, ptr, 1
1114 memRefDesc.setMemRefDescPtr(rewriter, loc, ptr);
1115 rewriter.replaceOp(memRefCastOp, (Value)memRefDesc);
1116
1117 } else if (isa<UnrankedMemRefType>(srcType) && isa<MemRefType>(dstType)) {
1118 // Casting from unranked type to ranked.
1119 // The operation is assumed to be doing a correct cast. If the destination
1120 // type mismatches the unranked the type, it is undefined behavior.
1121 UnrankedMemRefDescriptor memRefDesc(adaptor.getSource());
1122 // ptr = ExtractValueOp src, 1
1123 auto ptr = memRefDesc.memRefDescPtr(rewriter, loc);
1124
1125 // struct = LoadOp ptr
1126 auto loadOp = LLVM::LoadOp::create(rewriter, loc, targetStructType, ptr);
1127 rewriter.replaceOp(memRefCastOp, loadOp.getResult());
1128 } else {
1129 llvm_unreachable("Unsupported unranked memref to unranked memref cast");
1130 }
1131
1132 return success();
1133 }
1134};
1135
1136/// Pattern to lower a `memref.copy` to llvm.
1137///
1138/// For memrefs with identity layouts, the copy is lowered to the llvm
1139/// `memcpy` intrinsic. For non-identity layouts, the copy is lowered to a call
1140/// to the generic `MemrefCopyFn`.
1141class MemRefCopyOpLowering : public ConvertOpToLLVMPattern<memref::CopyOp> {
1142 SymbolTableCollection *symbolTables = nullptr;
1143
1144public:
1145 explicit MemRefCopyOpLowering(const LLVMTypeConverter &typeConverter,
1146 SymbolTableCollection *symbolTables = nullptr,
1147 PatternBenefit benefit = 1)
1148 : ConvertOpToLLVMPattern<memref::CopyOp>(typeConverter, benefit),
1149 symbolTables(symbolTables) {}
1150
1151 LogicalResult
1152 lowerToMemCopyIntrinsic(memref::CopyOp op, OpAdaptor adaptor,
1153 ConversionPatternRewriter &rewriter) const {
1154 auto loc = op.getLoc();
1155 auto srcType = dyn_cast<MemRefType>(op.getSource().getType());
1156
1157 MemRefDescriptor srcDesc(adaptor.getSource());
1158
1159 // Compute number of elements.
1160 Value numElements = LLVM::ConstantOp::create(rewriter, loc, getIndexType(),
1161 rewriter.getIndexAttr(1));
1162 for (int pos = 0; pos < srcType.getRank(); ++pos) {
1163 auto size = srcDesc.size(rewriter, loc, pos);
1164 numElements = LLVM::MulOp::create(rewriter, loc, numElements, size);
1165 }
1166
1167 // Get element size.
1168 auto sizeInBytes = getSizeInBytes(loc, srcType.getElementType(), rewriter);
1169 // Compute total.
1170 Value totalSize =
1171 LLVM::MulOp::create(rewriter, loc, numElements, sizeInBytes);
1172
1173 Type elementType = typeConverter->convertType(srcType.getElementType());
1174
1175 Value srcBasePtr = srcDesc.alignedPtr(rewriter, loc);
1176 Value srcOffset = srcDesc.offset(rewriter, loc);
1177 Value srcPtr = LLVM::GEPOp::create(rewriter, loc, srcBasePtr.getType(),
1178 elementType, srcBasePtr, srcOffset);
1179 MemRefDescriptor targetDesc(adaptor.getTarget());
1180 Value targetBasePtr = targetDesc.alignedPtr(rewriter, loc);
1181 Value targetOffset = targetDesc.offset(rewriter, loc);
1182 Value targetPtr =
1183 LLVM::GEPOp::create(rewriter, loc, targetBasePtr.getType(), elementType,
1184 targetBasePtr, targetOffset);
1185 LLVM::MemcpyOp::create(rewriter, loc, targetPtr, srcPtr, totalSize,
1186 /*isVolatile=*/false);
1187 rewriter.eraseOp(op);
1188
1189 return success();
1190 }
1191
1192 LogicalResult
1193 lowerToMemCopyFunctionCall(memref::CopyOp op, OpAdaptor adaptor,
1194 ConversionPatternRewriter &rewriter) const {
1195 auto loc = op.getLoc();
1196 auto srcType = cast<BaseMemRefType>(op.getSource().getType());
1197 auto targetType = cast<BaseMemRefType>(op.getTarget().getType());
1198
1199 // First make sure we have an unranked memref descriptor representation.
1200 auto makeUnranked = [&, this](Value ranked, MemRefType type) {
1201 auto rank = LLVM::ConstantOp::create(rewriter, loc, getIndexType(),
1202 type.getRank());
1203 auto *typeConverter = getTypeConverter();
1204 auto ptr =
1205 typeConverter->promoteOneMemRefDescriptor(loc, ranked, rewriter);
1206
1207 auto unrankedType =
1208 UnrankedMemRefType::get(type.getElementType(), type.getMemorySpace());
1210 rewriter, loc, *typeConverter, unrankedType, ValueRange{rank, ptr});
1211 };
1212
1213 // Save stack position before promoting descriptors
1214 auto stackSaveOp = LLVM::StackSaveOp::create(rewriter, loc, getPtrType());
1215
1216 auto srcMemRefType = dyn_cast<MemRefType>(srcType);
1217 Value unrankedSource =
1218 srcMemRefType ? makeUnranked(adaptor.getSource(), srcMemRefType)
1219 : adaptor.getSource();
1220 auto targetMemRefType = dyn_cast<MemRefType>(targetType);
1221 Value unrankedTarget =
1222 targetMemRefType ? makeUnranked(adaptor.getTarget(), targetMemRefType)
1223 : adaptor.getTarget();
1224
1225 // Now promote the unranked descriptors to the stack.
1226 auto one = LLVM::ConstantOp::create(rewriter, loc, getIndexType(),
1227 rewriter.getIndexAttr(1));
1228 auto promote = [&](Value desc) {
1229 auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext());
1230 auto allocated =
1231 LLVM::AllocaOp::create(rewriter, loc, ptrType, desc.getType(), one);
1232 LLVM::StoreOp::create(rewriter, loc, desc, allocated);
1233 return allocated;
1234 };
1235
1236 auto sourcePtr = promote(unrankedSource);
1237 auto targetPtr = promote(unrankedTarget);
1238
1239 // Derive size from llvm.getelementptr which will account for any
1240 // potential alignment
1241 auto elemSize = getSizeInBytes(loc, srcType.getElementType(), rewriter);
1243 rewriter, op->getParentOfType<ModuleOp>(), getIndexType(),
1244 sourcePtr.getType(), symbolTables);
1245 if (failed(copyFn))
1246 return failure();
1247 LLVM::CallOp::create(rewriter, loc, copyFn.value(),
1248 ValueRange{elemSize, sourcePtr, targetPtr});
1249
1250 // Restore stack used for descriptors
1251 LLVM::StackRestoreOp::create(rewriter, loc, stackSaveOp);
1252
1253 rewriter.eraseOp(op);
1254
1255 return success();
1256 }
1257
1258 LogicalResult
1259 matchAndRewrite(memref::CopyOp op, OpAdaptor adaptor,
1260 ConversionPatternRewriter &rewriter) const override {
1261 auto srcType = cast<BaseMemRefType>(op.getSource().getType());
1262 auto targetType = cast<BaseMemRefType>(op.getTarget().getType());
1263
1264 auto isContiguousMemrefType = [&](BaseMemRefType type) {
1265 auto memrefType = dyn_cast<mlir::MemRefType>(type);
1266 // We can use memcpy for memrefs if they have an identity layout or are
1267 // contiguous with an arbitrary offset. Ignore empty memrefs, which is a
1268 // special case handled by memrefCopy.
1269 return memrefType &&
1270 (memrefType.getLayout().isIdentity() ||
1271 (memrefType.hasStaticShape() && memrefType.getNumElements() > 0 &&
1273 };
1274
1275 if (isContiguousMemrefType(srcType) && isContiguousMemrefType(targetType))
1276 return lowerToMemCopyIntrinsic(op, adaptor, rewriter);
1277
1278 return lowerToMemCopyFunctionCall(op, adaptor, rewriter);
1279 }
1280};
1281
1282struct MemorySpaceCastOpLowering
1283 : public ConvertOpToLLVMPattern<memref::MemorySpaceCastOp> {
1284 using ConvertOpToLLVMPattern<
1285 memref::MemorySpaceCastOp>::ConvertOpToLLVMPattern;
1286
1287 LogicalResult
1288 matchAndRewrite(memref::MemorySpaceCastOp op, OpAdaptor adaptor,
1289 ConversionPatternRewriter &rewriter) const override {
1290 Location loc = op.getLoc();
1291
1292 Type resultType = op.getDest().getType();
1293 if (auto resultTypeR = dyn_cast<MemRefType>(resultType)) {
1294 auto convertedType =
1295 typeConverter->convertType<LLVM::LLVMStructType>(resultTypeR);
1296 if (!convertedType)
1297 return rewriter.notifyMatchFailure(op, "memref type conversion failed");
1298 Type newPtrType = convertedType.getBody()[0];
1299
1300 SmallVector<Value> descVals;
1301 MemRefDescriptor::unpack(rewriter, loc, adaptor.getSource(), resultTypeR,
1302 descVals);
1303 descVals[0] =
1304 LLVM::AddrSpaceCastOp::create(rewriter, loc, newPtrType, descVals[0]);
1305 descVals[1] =
1306 LLVM::AddrSpaceCastOp::create(rewriter, loc, newPtrType, descVals[1]);
1307 Value result = MemRefDescriptor::pack(rewriter, loc, *getTypeConverter(),
1308 resultTypeR, descVals);
1309 rewriter.replaceOp(op, result);
1310 return success();
1311 }
1312 if (auto resultTypeU = dyn_cast<UnrankedMemRefType>(resultType)) {
1313 // Since the type converter won't be doing this for us, get the address
1314 // space.
1315 auto sourceType = cast<UnrankedMemRefType>(op.getSource().getType());
1316 FailureOr<unsigned> maybeSourceAddrSpace =
1317 getTypeConverter()->getMemRefAddressSpace(sourceType);
1318 if (failed(maybeSourceAddrSpace))
1319 return rewriter.notifyMatchFailure(loc,
1320 "non-integer source address space");
1321 unsigned sourceAddrSpace = *maybeSourceAddrSpace;
1322 FailureOr<unsigned> maybeResultAddrSpace =
1323 getTypeConverter()->getMemRefAddressSpace(resultTypeU);
1324 if (failed(maybeResultAddrSpace))
1325 return rewriter.notifyMatchFailure(loc,
1326 "non-integer result address space");
1327 unsigned resultAddrSpace = *maybeResultAddrSpace;
1328
1329 UnrankedMemRefDescriptor sourceDesc(adaptor.getSource());
1330 Value rank = sourceDesc.rank(rewriter, loc);
1331 Value sourceUnderlyingDesc = sourceDesc.memRefDescPtr(rewriter, loc);
1332
1333 // Create and allocate storage for new memref descriptor.
1335 rewriter, loc, typeConverter->convertType(resultTypeU));
1336 result.setRank(rewriter, loc, rank);
1337 Value resultUnderlyingSize = UnrankedMemRefDescriptor::computeSize(
1338 rewriter, loc, *getTypeConverter(), result, resultAddrSpace);
1339 Value resultUnderlyingDesc =
1340 LLVM::AllocaOp::create(rewriter, loc, getPtrType(),
1341 rewriter.getI8Type(), resultUnderlyingSize);
1342 result.setMemRefDescPtr(rewriter, loc, resultUnderlyingDesc);
1343
1344 // Copy pointers, performing address space casts.
1345 auto sourceElemPtrType =
1346 LLVM::LLVMPointerType::get(rewriter.getContext(), sourceAddrSpace);
1347 auto resultElemPtrType =
1348 LLVM::LLVMPointerType::get(rewriter.getContext(), resultAddrSpace);
1349
1350 Value allocatedPtr = sourceDesc.allocatedPtr(
1351 rewriter, loc, sourceUnderlyingDesc, sourceElemPtrType);
1352 Value alignedPtr =
1353 sourceDesc.alignedPtr(rewriter, loc, *getTypeConverter(),
1354 sourceUnderlyingDesc, sourceElemPtrType);
1355 allocatedPtr = LLVM::AddrSpaceCastOp::create(
1356 rewriter, loc, resultElemPtrType, allocatedPtr);
1357 alignedPtr = LLVM::AddrSpaceCastOp::create(rewriter, loc,
1358 resultElemPtrType, alignedPtr);
1359
1360 result.setAllocatedPtr(rewriter, loc, resultUnderlyingDesc,
1361 resultElemPtrType, allocatedPtr);
1362 result.setAlignedPtr(rewriter, loc, *getTypeConverter(),
1363 resultUnderlyingDesc, resultElemPtrType, alignedPtr);
1364
1365 // Copy all the index-valued operands.
1366 Value sourceIndexVals =
1367 sourceDesc.offsetBasePtr(rewriter, loc, *getTypeConverter(),
1368 sourceUnderlyingDesc, sourceElemPtrType);
1369 Value resultIndexVals =
1370 result.offsetBasePtr(rewriter, loc, *getTypeConverter(),
1371 resultUnderlyingDesc, resultElemPtrType);
1372
1373 int64_t bytesToSkip =
1374 2 * llvm::divideCeil(
1375 getTypeConverter()->getPointerBitwidth(resultAddrSpace), 8);
1376 Value bytesToSkipConst = LLVM::ConstantOp::create(
1377 rewriter, loc, getIndexType(), rewriter.getIndexAttr(bytesToSkip));
1378 Value copySize =
1379 LLVM::SubOp::create(rewriter, loc, getIndexType(),
1380 resultUnderlyingSize, bytesToSkipConst);
1381 LLVM::MemcpyOp::create(rewriter, loc, resultIndexVals, sourceIndexVals,
1382 copySize, /*isVolatile=*/false);
1383
1384 rewriter.replaceOp(op, ValueRange{result});
1385 return success();
1386 }
1387 return rewriter.notifyMatchFailure(loc, "unexpected memref type");
1388 }
1389};
1390
1391/// Extracts allocated, aligned pointers and offset from a ranked or unranked
1392/// memref type. In unranked case, the fields are extracted from the underlying
1393/// ranked descriptor.
1394static void extractPointersAndOffset(Location loc,
1395 ConversionPatternRewriter &rewriter,
1396 const LLVMTypeConverter &typeConverter,
1397 Value originalOperand,
1398 Value convertedOperand,
1399 Value *allocatedPtr, Value *alignedPtr,
1400 Value *offset = nullptr) {
1401 Type operandType = originalOperand.getType();
1402 if (isa<MemRefType>(operandType)) {
1403 MemRefDescriptor desc(convertedOperand);
1404 *allocatedPtr = desc.allocatedPtr(rewriter, loc);
1405 *alignedPtr = desc.alignedPtr(rewriter, loc);
1406 if (offset != nullptr)
1407 *offset = desc.offset(rewriter, loc);
1408 return;
1409 }
1410
1411 // These will all cause assert()s on unconvertible types.
1412 unsigned memorySpace = *typeConverter.getMemRefAddressSpace(
1413 cast<UnrankedMemRefType>(operandType));
1414 auto elementPtrType =
1415 LLVM::LLVMPointerType::get(rewriter.getContext(), memorySpace);
1416
1417 // Extract pointer to the underlying ranked memref descriptor and cast it to
1418 // ElemType**.
1419 UnrankedMemRefDescriptor unrankedDesc(convertedOperand);
1420 Value underlyingDescPtr = unrankedDesc.memRefDescPtr(rewriter, loc);
1421
1423 rewriter, loc, underlyingDescPtr, elementPtrType);
1425 rewriter, loc, typeConverter, underlyingDescPtr, elementPtrType);
1426 if (offset != nullptr) {
1428 rewriter, loc, typeConverter, underlyingDescPtr, elementPtrType);
1429 }
1430}
1431
1432struct MemRefReinterpretCastOpLowering
1433 : public ConvertOpToLLVMPattern<memref::ReinterpretCastOp> {
1434 using ConvertOpToLLVMPattern<
1435 memref::ReinterpretCastOp>::ConvertOpToLLVMPattern;
1436
1437 LogicalResult
1438 matchAndRewrite(memref::ReinterpretCastOp castOp, OpAdaptor adaptor,
1439 ConversionPatternRewriter &rewriter) const override {
1440 Type srcType = castOp.getSource().getType();
1441
1442 Value descriptor;
1443 if (failed(convertSourceMemRefToDescriptor(rewriter, srcType, castOp,
1444 adaptor, &descriptor)))
1445 return failure();
1446 rewriter.replaceOp(castOp, {descriptor});
1447 return success();
1448 }
1449
1450private:
1451 LogicalResult convertSourceMemRefToDescriptor(
1452 ConversionPatternRewriter &rewriter, Type srcType,
1453 memref::ReinterpretCastOp castOp,
1454 memref::ReinterpretCastOp::Adaptor adaptor, Value *descriptor) const {
1455 MemRefType targetMemRefType =
1456 cast<MemRefType>(castOp.getResult().getType());
1457 auto llvmTargetDescriptorTy =
1458 typeConverter->convertType<LLVM::LLVMStructType>(targetMemRefType);
1459 if (!llvmTargetDescriptorTy)
1460 return failure();
1461
1462 // Create descriptor.
1463 Location loc = castOp.getLoc();
1464 auto desc = MemRefDescriptor::poison(rewriter, loc, llvmTargetDescriptorTy);
1465
1466 // Set allocated and aligned pointers.
1467 Value allocatedPtr, alignedPtr;
1468 extractPointersAndOffset(loc, rewriter, *getTypeConverter(),
1469 castOp.getSource(), adaptor.getSource(),
1470 &allocatedPtr, &alignedPtr);
1471 desc.setAllocatedPtr(rewriter, loc, allocatedPtr);
1472 desc.setAlignedPtr(rewriter, loc, alignedPtr);
1473
1474 // Set offset.
1475 if (castOp.isDynamicOffset(0))
1476 desc.setOffset(rewriter, loc, adaptor.getOffsets()[0]);
1477 else
1478 desc.setConstantOffset(rewriter, loc, castOp.getStaticOffset(0));
1479
1480 // Set sizes and strides.
1481 unsigned dynSizeId = 0;
1482 unsigned dynStrideId = 0;
1483 for (unsigned i = 0, e = targetMemRefType.getRank(); i < e; ++i) {
1484 if (castOp.isDynamicSize(i))
1485 desc.setSize(rewriter, loc, i, adaptor.getSizes()[dynSizeId++]);
1486 else
1487 desc.setConstantSize(rewriter, loc, i, castOp.getStaticSize(i));
1488
1489 if (castOp.isDynamicStride(i))
1490 desc.setStride(rewriter, loc, i, adaptor.getStrides()[dynStrideId++]);
1491 else
1492 desc.setConstantStride(rewriter, loc, i, castOp.getStaticStride(i));
1493 }
1494 *descriptor = desc;
1495 return success();
1496 }
1497};
1498
1499struct MemRefReshapeOpLowering
1500 : public ConvertOpToLLVMPattern<memref::ReshapeOp> {
1501 using ConvertOpToLLVMPattern<memref::ReshapeOp>::ConvertOpToLLVMPattern;
1502
1503 LogicalResult
1504 matchAndRewrite(memref::ReshapeOp reshapeOp, OpAdaptor adaptor,
1505 ConversionPatternRewriter &rewriter) const override {
1506 Type srcType = reshapeOp.getSource().getType();
1507
1508 Value descriptor;
1509 if (failed(convertSourceMemRefToDescriptor(rewriter, srcType, reshapeOp,
1510 adaptor, &descriptor)))
1511 return failure();
1512 rewriter.replaceOp(reshapeOp, {descriptor});
1513 return success();
1514 }
1515
1516private:
1517 LogicalResult
1518 convertSourceMemRefToDescriptor(ConversionPatternRewriter &rewriter,
1519 Type srcType, memref::ReshapeOp reshapeOp,
1520 memref::ReshapeOp::Adaptor adaptor,
1521 Value *descriptor) const {
1522 auto shapeMemRefType = cast<MemRefType>(reshapeOp.getShape().getType());
1523 if (shapeMemRefType.hasStaticShape()) {
1524 MemRefType targetMemRefType =
1525 cast<MemRefType>(reshapeOp.getResult().getType());
1526 auto llvmTargetDescriptorTy =
1527 typeConverter->convertType<LLVM::LLVMStructType>(targetMemRefType);
1528 if (!llvmTargetDescriptorTy)
1529 return failure();
1530
1531 // Create descriptor.
1532 Location loc = reshapeOp.getLoc();
1533 auto desc =
1534 MemRefDescriptor::poison(rewriter, loc, llvmTargetDescriptorTy);
1535
1536 // Set allocated and aligned pointers.
1537 Value allocatedPtr, alignedPtr;
1538 extractPointersAndOffset(loc, rewriter, *getTypeConverter(),
1539 reshapeOp.getSource(), adaptor.getSource(),
1540 &allocatedPtr, &alignedPtr);
1541 desc.setAllocatedPtr(rewriter, loc, allocatedPtr);
1542 desc.setAlignedPtr(rewriter, loc, alignedPtr);
1543
1544 // Extract the offset and strides from the type.
1545 int64_t offset;
1546 SmallVector<int64_t> strides;
1547 if (failed(targetMemRefType.getStridesAndOffset(strides, offset)))
1548 return rewriter.notifyMatchFailure(
1549 reshapeOp, "failed to get stride and offset exprs");
1550
1551 if (!isStaticStrideOrOffset(offset))
1552 return rewriter.notifyMatchFailure(reshapeOp,
1553 "dynamic offset is unsupported");
1554
1555 desc.setConstantOffset(rewriter, loc, offset);
1556
1557 assert(targetMemRefType.getLayout().isIdentity() &&
1558 "Identity layout map is a precondition of a valid reshape op");
1559
1560 Type indexType = getIndexType();
1561 Value stride = nullptr;
1562 int64_t targetRank = targetMemRefType.getRank();
1563 for (auto i : llvm::reverse(llvm::seq<int64_t>(0, targetRank))) {
1564 if (ShapedType::isStatic(strides[i])) {
1565 // If the stride for this dimension is dynamic, then use the product
1566 // of the sizes of the inner dimensions.
1567 stride =
1568 createIndexAttrConstant(rewriter, loc, indexType, strides[i]);
1569 } else if (!stride) {
1570 // `stride` is null only in the first iteration of the loop. However,
1571 // since the target memref has an identity layout, we can safely set
1572 // the innermost stride to 1.
1573 stride = createIndexAttrConstant(rewriter, loc, indexType, 1);
1574 }
1575
1576 Value dimSize;
1577 // If the size of this dimension is dynamic, then load it at runtime
1578 // from the shape operand.
1579 if (!targetMemRefType.isDynamicDim(i)) {
1580 dimSize = createIndexAttrConstant(rewriter, loc, indexType,
1581 targetMemRefType.getDimSize(i));
1582 } else {
1583 Value shapeOp = reshapeOp.getShape();
1584 Value index = createIndexAttrConstant(rewriter, loc, indexType, i);
1585 dimSize = memref::LoadOp::create(rewriter, loc, shapeOp, index);
1586 Type indexType = getIndexType();
1587 if (dimSize.getType() != indexType)
1588 dimSize = typeConverter->materializeTargetConversion(
1589 rewriter, loc, indexType, dimSize);
1590 assert(dimSize && "Invalid memref element type");
1591 }
1592
1593 desc.setSize(rewriter, loc, i, dimSize);
1594 desc.setStride(rewriter, loc, i, stride);
1595
1596 // Prepare the stride value for the next dimension.
1597 stride = LLVM::MulOp::create(rewriter, loc, stride, dimSize);
1598 }
1599
1600 *descriptor = desc;
1601 return success();
1602 }
1603
1604 // The shape is a rank-1 tensor with unknown length.
1605 Location loc = reshapeOp.getLoc();
1606 MemRefDescriptor shapeDesc(adaptor.getShape());
1607 Value resultRank = shapeDesc.size(rewriter, loc, 0);
1608
1609 // Extract address space and element type.
1610 auto targetType = cast<UnrankedMemRefType>(reshapeOp.getResult().getType());
1611 unsigned addressSpace =
1612 *getTypeConverter()->getMemRefAddressSpace(targetType);
1613
1614 // Create the unranked memref descriptor that holds the ranked one. The
1615 // inner descriptor is allocated on stack.
1616 auto targetDesc = UnrankedMemRefDescriptor::poison(
1617 rewriter, loc, typeConverter->convertType(targetType));
1618 targetDesc.setRank(rewriter, loc, resultRank);
1619 Value allocationSize = UnrankedMemRefDescriptor::computeSize(
1620 rewriter, loc, *getTypeConverter(), targetDesc, addressSpace);
1621 Value underlyingDescPtr = LLVM::AllocaOp::create(
1622 rewriter, loc, getPtrType(), IntegerType::get(getContext(), 8),
1623 allocationSize);
1624 targetDesc.setMemRefDescPtr(rewriter, loc, underlyingDescPtr);
1625
1626 // Extract pointers and offset from the source memref.
1627 Value allocatedPtr, alignedPtr, offset;
1628 extractPointersAndOffset(loc, rewriter, *getTypeConverter(),
1629 reshapeOp.getSource(), adaptor.getSource(),
1630 &allocatedPtr, &alignedPtr, &offset);
1631
1632 // Set pointers and offset.
1633 auto elementPtrType =
1634 LLVM::LLVMPointerType::get(rewriter.getContext(), addressSpace);
1635
1636 UnrankedMemRefDescriptor::setAllocatedPtr(rewriter, loc, underlyingDescPtr,
1637 elementPtrType, allocatedPtr);
1638 UnrankedMemRefDescriptor::setAlignedPtr(rewriter, loc, *getTypeConverter(),
1639 underlyingDescPtr, elementPtrType,
1640 alignedPtr);
1641 UnrankedMemRefDescriptor::setOffset(rewriter, loc, *getTypeConverter(),
1642 underlyingDescPtr, elementPtrType,
1643 offset);
1644
1645 // Use the offset pointer as base for further addressing. Copy over the new
1646 // shape and compute strides. For this, we create a loop from rank-1 to 0.
1647 Value targetSizesBase = UnrankedMemRefDescriptor::sizeBasePtr(
1648 rewriter, loc, *getTypeConverter(), underlyingDescPtr, elementPtrType);
1649 Value targetStridesBase = UnrankedMemRefDescriptor::strideBasePtr(
1650 rewriter, loc, *getTypeConverter(), targetSizesBase, resultRank);
1651 Value shapeOperandPtr = shapeDesc.alignedPtr(rewriter, loc);
1652 Value oneIndex = createIndexAttrConstant(rewriter, loc, getIndexType(), 1);
1653 Value resultRankMinusOne =
1654 LLVM::SubOp::create(rewriter, loc, resultRank, oneIndex);
1655
1656 Block *initBlock = rewriter.getInsertionBlock();
1657 Type indexType = getTypeConverter()->getIndexType();
1658 Block::iterator remainingOpsIt = std::next(rewriter.getInsertionPoint());
1659
1660 Block *condBlock = rewriter.createBlock(initBlock->getParent(), {},
1661 {indexType, indexType}, {loc, loc});
1662
1663 // Move the remaining initBlock ops to condBlock.
1664 Block *remainingBlock = rewriter.splitBlock(initBlock, remainingOpsIt);
1665 rewriter.mergeBlocks(remainingBlock, condBlock, ValueRange());
1666
1667 rewriter.setInsertionPointToEnd(initBlock);
1668 LLVM::BrOp::create(rewriter, loc,
1669 ValueRange({resultRankMinusOne, oneIndex}), condBlock);
1670 rewriter.setInsertionPointToStart(condBlock);
1671 Value indexArg = condBlock->getArgument(0);
1672 Value strideArg = condBlock->getArgument(1);
1673
1674 Value zeroIndex = createIndexAttrConstant(rewriter, loc, indexType, 0);
1675 Value pred = LLVM::ICmpOp::create(
1676 rewriter, loc, IntegerType::get(rewriter.getContext(), 1),
1677 LLVM::ICmpPredicate::sge, indexArg, zeroIndex);
1678
1679 Block *bodyBlock =
1680 rewriter.splitBlock(condBlock, rewriter.getInsertionPoint());
1681 rewriter.setInsertionPointToStart(bodyBlock);
1682
1683 // Copy size from shape to descriptor.
1684 auto llvmIndexPtrType = LLVM::LLVMPointerType::get(rewriter.getContext());
1685 Value sizeLoadGep = LLVM::GEPOp::create(
1686 rewriter, loc, llvmIndexPtrType,
1687 typeConverter->convertType(shapeMemRefType.getElementType()),
1688 shapeOperandPtr, indexArg);
1689 Value size = LLVM::LoadOp::create(rewriter, loc, indexType, sizeLoadGep);
1690 UnrankedMemRefDescriptor::setSize(rewriter, loc, *getTypeConverter(),
1691 targetSizesBase, indexArg, size);
1692
1693 // Write stride value and compute next one.
1694 UnrankedMemRefDescriptor::setStride(rewriter, loc, *getTypeConverter(),
1695 targetStridesBase, indexArg, strideArg);
1696 Value nextStride = LLVM::MulOp::create(rewriter, loc, strideArg, size);
1697
1698 // Decrement loop counter and branch back.
1699 Value decrement = LLVM::SubOp::create(rewriter, loc, indexArg, oneIndex);
1700 LLVM::BrOp::create(rewriter, loc, ValueRange({decrement, nextStride}),
1701 condBlock);
1702
1703 Block *remainder =
1704 rewriter.splitBlock(bodyBlock, rewriter.getInsertionPoint());
1705
1706 // Hook up the cond exit to the remainder.
1707 rewriter.setInsertionPointToEnd(condBlock);
1708 LLVM::CondBrOp::create(rewriter, loc, pred, bodyBlock, ValueRange(),
1709 remainder, ValueRange());
1710
1711 // Reset position to beginning of new remainder block.
1712 rewriter.setInsertionPointToStart(remainder);
1713
1714 *descriptor = targetDesc;
1715 return success();
1716 }
1717};
1718
1719/// RessociatingReshapeOp must be expanded before we reach this stage.
1720/// Report that information.
1721template <typename ReshapeOp>
1722class ReassociatingReshapeOpConversion
1723 : public ConvertOpToLLVMPattern<ReshapeOp> {
1724public:
1725 using ConvertOpToLLVMPattern<ReshapeOp>::ConvertOpToLLVMPattern;
1726 using ReshapeOpAdaptor = typename ReshapeOp::Adaptor;
1727
1728 LogicalResult
1729 matchAndRewrite(ReshapeOp reshapeOp, typename ReshapeOp::Adaptor adaptor,
1730 ConversionPatternRewriter &rewriter) const override {
1731 return rewriter.notifyMatchFailure(
1732 reshapeOp,
1733 "reassociation operations should have been expanded beforehand");
1734 }
1735};
1736
1737/// Subviews must be expanded before we reach this stage.
1738/// Report that information.
1739struct SubViewOpLowering : public ConvertOpToLLVMPattern<memref::SubViewOp> {
1740 using ConvertOpToLLVMPattern<memref::SubViewOp>::ConvertOpToLLVMPattern;
1741
1742 LogicalResult
1743 matchAndRewrite(memref::SubViewOp subViewOp, OpAdaptor adaptor,
1744 ConversionPatternRewriter &rewriter) const override {
1745 return rewriter.notifyMatchFailure(
1746 subViewOp, "subview operations should have been expanded beforehand");
1747 }
1748};
1749
1750/// Conversion pattern that transforms a transpose op into:
1751/// 1. A function entry `alloca` operation to allocate a ViewDescriptor.
1752/// 2. A load of the ViewDescriptor from the pointer allocated in 1.
1753/// 3. Updates to the ViewDescriptor to introduce the data ptr, offset, size
1754/// and stride. Size and stride are permutations of the original values.
1755/// 4. A store of the resulting ViewDescriptor to the alloca'ed pointer.
1756/// The transpose op is replaced by the alloca'ed pointer.
1757class TransposeOpLowering : public ConvertOpToLLVMPattern<memref::TransposeOp> {
1758public:
1759 using ConvertOpToLLVMPattern<memref::TransposeOp>::ConvertOpToLLVMPattern;
1760
1761 LogicalResult
1762 matchAndRewrite(memref::TransposeOp transposeOp, OpAdaptor adaptor,
1763 ConversionPatternRewriter &rewriter) const override {
1764 auto loc = transposeOp.getLoc();
1765 MemRefDescriptor viewMemRef(adaptor.getIn());
1766
1767 // No permutation, early exit.
1768 if (transposeOp.getPermutation().isIdentity())
1769 return rewriter.replaceOp(transposeOp, {viewMemRef}), success();
1770
1771 auto targetMemRef = MemRefDescriptor::poison(
1772 rewriter, loc,
1773 typeConverter->convertType(transposeOp.getIn().getType()));
1774
1775 // Copy the base and aligned pointers from the old descriptor to the new
1776 // one.
1777 targetMemRef.setAllocatedPtr(rewriter, loc,
1778 viewMemRef.allocatedPtr(rewriter, loc));
1779 targetMemRef.setAlignedPtr(rewriter, loc,
1780 viewMemRef.alignedPtr(rewriter, loc));
1781
1782 // Copy the offset pointer from the old descriptor to the new one.
1783 targetMemRef.setOffset(rewriter, loc, viewMemRef.offset(rewriter, loc));
1784
1785 // Iterate over the dimensions and apply size/stride permutation:
1786 // When enumerating the results of the permutation map, the enumeration
1787 // index is the index into the target dimensions and the DimExpr points to
1788 // the dimension of the source memref.
1789 for (const auto &en :
1790 llvm::enumerate(transposeOp.getPermutation().getResults())) {
1791 int targetPos = en.index();
1792 int sourcePos = cast<AffineDimExpr>(en.value()).getPosition();
1793 targetMemRef.setSize(rewriter, loc, targetPos,
1794 viewMemRef.size(rewriter, loc, sourcePos));
1795 targetMemRef.setStride(rewriter, loc, targetPos,
1796 viewMemRef.stride(rewriter, loc, sourcePos));
1797 }
1798
1799 rewriter.replaceOp(transposeOp, {targetMemRef});
1800 return success();
1801 }
1802};
1803
1804/// Conversion pattern that transforms an op into:
1805/// 1. An `llvm.mlir.undef` operation to create a memref descriptor
1806/// 2. Updates to the descriptor to introduce the data ptr, offset, size
1807/// and stride.
1808/// The view op is replaced by the descriptor.
1809struct ViewOpLowering : public ConvertOpToLLVMPattern<memref::ViewOp> {
1810 using ConvertOpToLLVMPattern<memref::ViewOp>::ConvertOpToLLVMPattern;
1811
1812 // Build and return the value for the idx^th shape dimension, either by
1813 // returning the constant shape dimension or counting the proper dynamic size.
1814 Value getSize(ConversionPatternRewriter &rewriter, Location loc,
1815 ArrayRef<int64_t> shape, ValueRange dynamicSizes, unsigned idx,
1816 Type indexType) const {
1817 assert(idx < shape.size());
1818 if (ShapedType::isStatic(shape[idx]))
1819 return createIndexAttrConstant(rewriter, loc, indexType, shape[idx]);
1820 // Count the number of dynamic dims in range [0, idx]
1821 unsigned nDynamic =
1822 llvm::count_if(shape.take_front(idx), ShapedType::isDynamic);
1823 return dynamicSizes[nDynamic];
1824 }
1825
1826 // Build and return the idx^th stride, either by returning the constant stride
1827 // or by computing the dynamic stride from the current `runningStride` and
1828 // `nextSize`. The caller should keep a running stride and update it with the
1829 // result returned by this function.
1830 Value getStride(ConversionPatternRewriter &rewriter, Location loc,
1831 ArrayRef<int64_t> strides, Value nextSize,
1832 Value runningStride, unsigned idx, Type indexType) const {
1833 assert(idx < strides.size());
1834 if (ShapedType::isStatic(strides[idx]))
1835 return createIndexAttrConstant(rewriter, loc, indexType, strides[idx]);
1836 if (nextSize)
1837 return runningStride
1838 ? LLVM::MulOp::create(rewriter, loc, runningStride, nextSize)
1839 : nextSize;
1840 assert(!runningStride);
1841 return createIndexAttrConstant(rewriter, loc, indexType, 1);
1842 }
1843
1844 LogicalResult
1845 matchAndRewrite(memref::ViewOp viewOp, OpAdaptor adaptor,
1846 ConversionPatternRewriter &rewriter) const override {
1847 auto loc = viewOp.getLoc();
1848
1849 auto viewMemRefType = viewOp.getType();
1850 auto targetElementTy =
1851 typeConverter->convertType(viewMemRefType.getElementType());
1852 auto targetDescTy = typeConverter->convertType(viewMemRefType);
1853 if (!targetDescTy || !targetElementTy ||
1854 !LLVM::isCompatibleType(targetElementTy) ||
1855 !LLVM::isCompatibleType(targetDescTy))
1856 return viewOp.emitWarning("Target descriptor type not converted to LLVM"),
1857 failure();
1858
1859 int64_t offset;
1860 SmallVector<int64_t, 4> strides;
1861 auto successStrides = viewMemRefType.getStridesAndOffset(strides, offset);
1862 if (failed(successStrides))
1863 return viewOp.emitWarning("cannot cast to non-strided shape"), failure();
1864 assert(offset == 0 && "expected offset to be 0");
1865
1866 // Target memref must be contiguous in memory (innermost stride is 1), or
1867 // empty (special case when at least one of the memref dimensions is 0).
1868 if (!strides.empty() && (strides.back() != 1 && strides.back() != 0))
1869 return viewOp.emitWarning("cannot cast to non-contiguous shape"),
1870 failure();
1871
1872 // Create the descriptor.
1873 MemRefDescriptor sourceMemRef(adaptor.getSource());
1874 auto targetMemRef = MemRefDescriptor::poison(rewriter, loc, targetDescTy);
1875
1876 // Field 1: Copy the allocated pointer, used for malloc/free.
1877 Value allocatedPtr = sourceMemRef.allocatedPtr(rewriter, loc);
1878 auto srcMemRefType = cast<MemRefType>(viewOp.getSource().getType());
1879 targetMemRef.setAllocatedPtr(rewriter, loc, allocatedPtr);
1880
1881 // Field 2: Copy the actual aligned pointer to payload.
1882 Value alignedPtr = sourceMemRef.alignedPtr(rewriter, loc);
1883 alignedPtr = LLVM::GEPOp::create(
1884 rewriter, loc, alignedPtr.getType(),
1885 typeConverter->convertType(srcMemRefType.getElementType()), alignedPtr,
1886 adaptor.getByteShift());
1887
1888 targetMemRef.setAlignedPtr(rewriter, loc, alignedPtr);
1889
1890 Type indexType = getIndexType();
1891 // Field 3: The offset in the resulting type must be 0. This is
1892 // because of the type change: an offset on srcType* may not be
1893 // expressible as an offset on dstType*.
1894 targetMemRef.setOffset(
1895 rewriter, loc,
1896 createIndexAttrConstant(rewriter, loc, indexType, offset));
1897
1898 // Early exit for 0-D corner case.
1899 if (viewMemRefType.getRank() == 0)
1900 return rewriter.replaceOp(viewOp, {targetMemRef}), success();
1901
1902 // Fields 4 and 5: Update sizes and strides.
1903 Value stride = nullptr, nextSize = nullptr;
1904 for (int i = viewMemRefType.getRank() - 1; i >= 0; --i) {
1905 // Update size.
1906 Value size = getSize(rewriter, loc, viewMemRefType.getShape(),
1907 adaptor.getSizes(), i, indexType);
1908 targetMemRef.setSize(rewriter, loc, i, size);
1909 // Update stride.
1910 stride =
1911 getStride(rewriter, loc, strides, nextSize, stride, i, indexType);
1912 targetMemRef.setStride(rewriter, loc, i, stride);
1913 nextSize = size;
1914 }
1915
1916 rewriter.replaceOp(viewOp, {targetMemRef});
1917 return success();
1918 }
1919};
1920
1921//===----------------------------------------------------------------------===//
1922// AtomicRMWOpLowering
1923//===----------------------------------------------------------------------===//
1924
1925/// Try to match the kind of a memref.atomic_rmw to determine whether to use a
1926/// lowering to llvm.atomicrmw or fallback to llvm.cmpxchg.
1927static std::optional<LLVM::AtomicBinOp>
1928matchSimpleAtomicOp(memref::AtomicRMWOp atomicOp) {
1929 switch (atomicOp.getKind()) {
1930 case arith::AtomicRMWKind::addf:
1931 return LLVM::AtomicBinOp::fadd;
1932 case arith::AtomicRMWKind::addi:
1933 return LLVM::AtomicBinOp::add;
1934 case arith::AtomicRMWKind::assign:
1935 return LLVM::AtomicBinOp::xchg;
1936 case arith::AtomicRMWKind::maximumf:
1937 // TODO: remove this by end of 2025.
1938 LDBG() << "the lowering of memref.atomicrmw maximumf changed "
1939 "from fmax to fmaximum, expect more NaNs";
1940 return LLVM::AtomicBinOp::fmaximum;
1941 case arith::AtomicRMWKind::maxnumf:
1942 return LLVM::AtomicBinOp::fmax;
1943 case arith::AtomicRMWKind::maxs:
1944 return LLVM::AtomicBinOp::max;
1945 case arith::AtomicRMWKind::maxu:
1946 return LLVM::AtomicBinOp::umax;
1947 case arith::AtomicRMWKind::minimumf:
1948 // TODO: remove this by end of 2025.
1949 LDBG() << "the lowering of memref.atomicrmw minimum changed "
1950 "from fmin to fminimum, expect more NaNs";
1951 return LLVM::AtomicBinOp::fminimum;
1952 case arith::AtomicRMWKind::minnumf:
1953 return LLVM::AtomicBinOp::fmin;
1954 case arith::AtomicRMWKind::mins:
1955 return LLVM::AtomicBinOp::min;
1956 case arith::AtomicRMWKind::minu:
1957 return LLVM::AtomicBinOp::umin;
1958 case arith::AtomicRMWKind::ori:
1959 return LLVM::AtomicBinOp::_or;
1960 case arith::AtomicRMWKind::xori:
1961 return LLVM::AtomicBinOp::_xor;
1962 case arith::AtomicRMWKind::andi:
1963 return LLVM::AtomicBinOp::_and;
1964 default:
1965 return std::nullopt;
1966 }
1967 llvm_unreachable("Invalid AtomicRMWKind");
1968}
1969
1970struct AtomicRMWOpLowering : public LoadStoreOpLowering<memref::AtomicRMWOp> {
1971 using Base::Base;
1972
1973 LogicalResult
1974 matchAndRewrite(memref::AtomicRMWOp atomicOp, OpAdaptor adaptor,
1975 ConversionPatternRewriter &rewriter) const override {
1976 auto maybeKind = matchSimpleAtomicOp(atomicOp);
1977 if (!maybeKind)
1978 return failure();
1979 auto memRefType = atomicOp.getMemRefType();
1980 SmallVector<int64_t> strides;
1981 int64_t offset;
1982 if (failed(memRefType.getStridesAndOffset(strides, offset)))
1983 return failure();
1984 auto dataPtr =
1985 getStridedElementPtr(rewriter, atomicOp.getLoc(), memRefType,
1986 adaptor.getMemref(), adaptor.getIndices());
1987 rewriter.replaceOpWithNewOp<LLVM::AtomicRMWOp>(
1988 atomicOp, *maybeKind, dataPtr, adaptor.getValue(),
1989 LLVM::AtomicOrdering::acq_rel);
1990 return success();
1991 }
1992};
1993
1994/// Unpack the pointer returned by a memref.extract_aligned_pointer_as_index.
1995class ConvertExtractAlignedPointerAsIndex
1996 : public ConvertOpToLLVMPattern<memref::ExtractAlignedPointerAsIndexOp> {
1997public:
1998 using ConvertOpToLLVMPattern<
1999 memref::ExtractAlignedPointerAsIndexOp>::ConvertOpToLLVMPattern;
2000
2001 LogicalResult
2002 matchAndRewrite(memref::ExtractAlignedPointerAsIndexOp extractOp,
2003 OpAdaptor adaptor,
2004 ConversionPatternRewriter &rewriter) const override {
2005 BaseMemRefType sourceTy = extractOp.getSource().getType();
2006
2007 Value alignedPtr;
2008 if (sourceTy.hasRank()) {
2009 MemRefDescriptor desc(adaptor.getSource());
2010 alignedPtr = desc.alignedPtr(rewriter, extractOp->getLoc());
2011 } else {
2012 auto elementPtrTy = LLVM::LLVMPointerType::get(
2013 rewriter.getContext(), sourceTy.getMemorySpaceAsInt());
2014
2015 UnrankedMemRefDescriptor desc(adaptor.getSource());
2016 Value descPtr = desc.memRefDescPtr(rewriter, extractOp->getLoc());
2017
2019 rewriter, extractOp->getLoc(), *getTypeConverter(), descPtr,
2020 elementPtrTy);
2021 }
2022
2023 rewriter.replaceOpWithNewOp<LLVM::PtrToIntOp>(
2024 extractOp, getTypeConverter()->getIndexType(), alignedPtr);
2025 return success();
2026 }
2027};
2028
2029/// Materialize the MemRef descriptor represented by the results of
2030/// ExtractStridedMetadataOp.
2031class ExtractStridedMetadataOpLowering
2032 : public ConvertOpToLLVMPattern<memref::ExtractStridedMetadataOp> {
2033public:
2034 using ConvertOpToLLVMPattern<
2035 memref::ExtractStridedMetadataOp>::ConvertOpToLLVMPattern;
2036
2037 LogicalResult
2038 matchAndRewrite(memref::ExtractStridedMetadataOp extractStridedMetadataOp,
2039 OpAdaptor adaptor,
2040 ConversionPatternRewriter &rewriter) const override {
2041
2042 if (!LLVM::isCompatibleType(adaptor.getOperands().front().getType()))
2043 return failure();
2044
2045 // Create the descriptor.
2046 MemRefDescriptor sourceMemRef(adaptor.getSource());
2047 Location loc = extractStridedMetadataOp.getLoc();
2048 Value source = extractStridedMetadataOp.getSource();
2049
2050 auto sourceMemRefType = cast<MemRefType>(source.getType());
2051 int64_t rank = sourceMemRefType.getRank();
2052 SmallVector<Value> results;
2053 results.reserve(2 + rank * 2);
2054
2055 // Base buffer.
2056 Value baseBuffer = sourceMemRef.allocatedPtr(rewriter, loc);
2057 Value alignedBuffer = sourceMemRef.alignedPtr(rewriter, loc);
2058 MemRefDescriptor dstMemRef = MemRefDescriptor::fromStaticShape(
2059 rewriter, loc, *getTypeConverter(),
2060 cast<MemRefType>(extractStridedMetadataOp.getBaseBuffer().getType()),
2061 baseBuffer, alignedBuffer);
2062 results.push_back((Value)dstMemRef);
2063
2064 // Offset.
2065 results.push_back(sourceMemRef.offset(rewriter, loc));
2066
2067 // Sizes.
2068 for (unsigned i = 0; i < rank; ++i)
2069 results.push_back(sourceMemRef.size(rewriter, loc, i));
2070 // Strides.
2071 for (unsigned i = 0; i < rank; ++i)
2072 results.push_back(sourceMemRef.stride(rewriter, loc, i));
2073
2074 rewriter.replaceOp(extractStridedMetadataOp, results);
2075 return success();
2076 }
2077};
2078
2079} // namespace
2080
2082 const LLVMTypeConverter &converter, RewritePatternSet &patterns,
2083 SymbolTableCollection *symbolTables) {
2084 // clang-format off
2085 patterns.add<
2086 AllocaOpLowering,
2087 AllocaScopeOpLowering,
2088 AssumeAlignmentOpLowering,
2089 AtomicRMWOpLowering,
2090 ConvertExtractAlignedPointerAsIndex,
2091 DimOpLowering,
2092 DistinctObjectsOpLowering,
2093 ExtractStridedMetadataOpLowering,
2094 GenericAtomicRMWOpLowering,
2095 GetGlobalMemrefOpLowering,
2096 LoadOpLowering,
2097 MemRefCastOpLowering,
2098 MemRefReinterpretCastOpLowering,
2099 MemRefReshapeOpLowering,
2100 MemorySpaceCastOpLowering,
2101 PrefetchOpLowering,
2102 RankOpLowering,
2103 ReassociatingReshapeOpConversion<memref::CollapseShapeOp>,
2104 ReassociatingReshapeOpConversion<memref::ExpandShapeOp>,
2105 StoreOpLowering,
2106 SubViewOpLowering,
2108 ViewOpLowering>(converter);
2109 // clang-format on
2110 patterns.add<GlobalMemrefOpLowering, MemRefCopyOpLowering>(converter,
2111 symbolTables);
2112 auto allocLowering = converter.getOptions().allocLowering;
2114 patterns.add<AlignedAllocOpLowering, DeallocOpLowering>(converter,
2115 symbolTables);
2116 else if (allocLowering == LowerToLLVMOptions::AllocLowering::Malloc)
2117 patterns.add<AllocOpLowering, DeallocOpLowering>(converter, symbolTables);
2118}
2119
2120namespace {
2121struct FinalizeMemRefToLLVMConversionPass
2123 FinalizeMemRefToLLVMConversionPass> {
2124 using FinalizeMemRefToLLVMConversionPassBase::
2125 FinalizeMemRefToLLVMConversionPassBase;
2126
2127 void runOnOperation() override {
2128 Operation *op = getOperation();
2129 const auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
2131 dataLayoutAnalysis.getAtOrAbove(op));
2132 options.allocLowering =
2135
2136 options.useGenericFunctions = useGenericFunctions;
2137
2138 if (indexBitwidth != kDeriveIndexBitwidthFromDataLayout)
2139 options.overrideIndexBitwidth(indexBitwidth);
2140
2141 LLVMTypeConverter typeConverter(&getContext(), options,
2142 &dataLayoutAnalysis);
2143 RewritePatternSet patterns(&getContext());
2144 SymbolTableCollection symbolTables;
2145 populateFinalizeMemRefToLLVMConversionPatterns(typeConverter, patterns,
2146 &symbolTables);
2148 target.addLegalOp<func::FuncOp>();
2149 if (failed(applyPartialConversion(op, target, std::move(patterns))))
2150 signalPassFailure();
2151 }
2152};
2153
2154/// Implement the interface to convert MemRef to LLVM.
2155struct MemRefToLLVMDialectInterface : public ConvertToLLVMPatternInterface {
2156 MemRefToLLVMDialectInterface(Dialect *dialect)
2157 : ConvertToLLVMPatternInterface(dialect) {}
2158
2159 void loadDependentDialects(MLIRContext *context) const final {
2160 context->loadDialect<LLVM::LLVMDialect>();
2161 }
2162
2163 /// Hook for derived dialect interface to provide conversion patterns
2164 /// and mark dialect legal for the conversion target.
2165 void populateConvertToLLVMConversionPatterns(
2166 ConversionTarget &target, LLVMTypeConverter &typeConverter,
2167 RewritePatternSet &patterns) const final {
2168 populateFinalizeMemRefToLLVMConversionPatterns(typeConverter, patterns);
2169 }
2170};
2171
2172} // namespace
2173
2175 registry.addExtension(+[](MLIRContext *ctx, memref::MemRefDialect *dialect) {
2176 dialect->addInterfaces<MemRefToLLVMDialectInterface>();
2177 });
2178}
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
b getContext())
static Value createIndexAttrConstant(OpBuilder &builder, Location loc, Type resultType, int64_t value)
static LLVM::GEPNoWrapFlags getLoadStoreNoWrapFlags(MemRefType type)
Returns GEP no-wrap flags for a memref load/store.
static llvm::Value * getSizeInBytes(DataLayout &dl, const mlir::Type &type, Operation *clauseOp, llvm::Value *basePointer, llvm::Type *baseType, llvm::IRBuilderBase &builder, LLVM::ModuleTranslation &moduleTranslation)
static llvm::ManagedStatic< PassManagerOptions > options
Rewrite AVX2-specific vector.transpose, for the supported cases and depending on the TransposeLowerin...
LogicalResult matchAndRewrite(vector::TransposeOp op, PatternRewriter &rewriter) const override
unsigned getMemorySpaceAsInt() const
[deprecated] Returns the memory space in old raw integer representation.
bool hasRank() const
Returns if this type is ranked, i.e. it has a known number of dimensions.
OpListType::iterator iterator
Definition Block.h:164
BlockArgument getArgument(unsigned i)
Definition Block.h:153
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgListType getArguments()
Definition Block.h:111
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
Definition Block.h:236
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition Pattern.h:227
ConvertOpToLLVMPattern(const LLVMTypeConverter &typeConverter, PatternBenefit benefit=1)
Definition Pattern.h:233
Stores data layout objects for each operation that specifies the data layout above and below the give...
The main mechanism for performing data layout queries.
llvm::TypeSize getTypeSize(Type t) const
Returns the size of the given type in the current scope.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
auto lookupOrNull(T from) const
Lookup a mapped value within the map.
Definition IRMapping.h:58
Derived class that automatically populates legalization information for different LLVM ops.
Conversion from types to the LLVM IR dialect.
unsigned getUnrankedMemRefDescriptorSize(UnrankedMemRefType type, const DataLayout &layout) const
Returns the size of the unranked memref descriptor object in bytes.
Value promoteOneMemRefDescriptor(Location loc, Value operand, OpBuilder &builder) const
Promote the LLVM struct representation of one MemRef descriptor to stack and use pointer to struct to...
const LowerToLLVMOptions & getOptions() const
FailureOr< unsigned > getMemRefAddressSpace(BaseMemRefType type) const
Return the LLVM address space corresponding to the memory space of the memref type type or failure if...
const DataLayoutAnalysis * getDataLayoutAnalysis() const
Returns the data layout analysis to query during conversion.
unsigned getMemRefDescriptorSize(MemRefType type, const DataLayout &layout) const
Returns the size of the memref descriptor object in bytes.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
Options to control the LLVM lowering.
@ Malloc
Use malloc for heap allocations.
@ AlignedAlloc
Use aligned_alloc for heap allocations.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
Helper class to produce LLVM dialect operations extracting or inserting elements of a MemRef descript...
This class helps build Operations.
Definition Builders.h:209
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Value getOperand(unsigned idx)
Definition Operation.h:375
result_range getResults()
Definition Operation.h:440
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class represents a collection of SymbolTables.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
static void setOffset(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, Value memRefDescPtr, LLVM::LLVMPointerType elemPtrType, Value offset)
Builds IR inserting the offset into the descriptor.
static Value allocatedPtr(OpBuilder &builder, Location loc, Value memRefDescPtr, LLVM::LLVMPointerType elemPtrType)
TODO: The following accessors don't take alignment rules between elements of the descriptor struct in...
static Value computeSize(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, UnrankedMemRefDescriptor desc, unsigned addressSpace)
Builds and returns IR computing the size in bytes (suitable for opaque allocation).
void setRank(OpBuilder &builder, Location loc, Value value)
Builds IR setting the rank in the descriptor.
Value memRefDescPtr(OpBuilder &builder, Location loc) const
Builds IR extracting ranked memref descriptor ptr.
static void setAllocatedPtr(OpBuilder &builder, Location loc, Value memRefDescPtr, LLVM::LLVMPointerType elemPtrType, Value allocatedPtr)
Builds IR inserting the allocated pointer into the descriptor.
static void setSize(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, Value sizeBasePtr, Value index, Value size)
Builds IR inserting the size[index] into the descriptor.
static Value pack(OpBuilder &builder, Location loc, const LLVMTypeConverter &converter, UnrankedMemRefType type, ValueRange values)
Builds IR populating an unranked MemRef descriptor structure from a list of individual constituent va...
static UnrankedMemRefDescriptor poison(OpBuilder &builder, Location loc, Type descriptorType)
Builds IR creating an undef value of the descriptor type.
static void setAlignedPtr(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, Value memRefDescPtr, LLVM::LLVMPointerType elemPtrType, Value alignedPtr)
Builds IR inserting the aligned pointer into the descriptor.
static Value offset(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, Value memRefDescPtr, LLVM::LLVMPointerType elemPtrType)
Builds IR extracting the offset from the descriptor.
static Value strideBasePtr(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, Value sizeBasePtr, Value rank)
Builds IR extracting the pointer to the first element of the stride array.
void setMemRefDescPtr(OpBuilder &builder, Location loc, Value value)
Builds IR setting ranked memref descriptor ptr.
static void setStride(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, Value strideBasePtr, Value index, Value stride)
Builds IR inserting the stride[index] into the descriptor.
static Value sizeBasePtr(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, Value memRefDescPtr, LLVM::LLVMPointerType elemPtrType)
Builds IR extracting the pointer to the first element of the size array.
static Value alignedPtr(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, Value memRefDescPtr, LLVM::LLVMPointerType elemPtrType)
Builds IR extracting the aligned pointer from the descriptor.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
FailureOr< LLVM::LLVMFuncOp > lookupOrCreateFreeFn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreateMemRefCopyFn(OpBuilder &b, Operation *moduleOp, Type indexType, Type unrankedDescriptorType, SymbolTableCollection *symbolTables=nullptr)
Value getStridedElementPtr(OpBuilder &builder, Location loc, const LLVMTypeConverter &converter, MemRefType type, Value memRefDesc, ValueRange indices, LLVM::GEPNoWrapFlags noWrapFlags=LLVM::GEPNoWrapFlags::none)
Performs the index computation to get to the element at indices of the memory pointed to by memRefDes...
Definition Pattern.cpp:603
FailureOr< LLVM::LLVMFuncOp > lookupOrCreateGenericAlignedAllocFn(OpBuilder &b, Operation *moduleOp, Type indexType, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreateMallocFn(OpBuilder &b, Operation *moduleOp, Type indexType, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreateGenericAllocFn(OpBuilder &b, Operation *moduleOp, Type indexType, SymbolTableCollection *symbolTables=nullptr)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreateAlignedAllocFn(OpBuilder &b, Operation *moduleOp, Type indexType, SymbolTableCollection *symbolTables=nullptr)
bool isCompatibleType(Type type)
Returns true if the given type is compatible with the LLVM dialect.
FailureOr< LLVM::LLVMFuncOp > lookupOrCreateGenericFreeFn(OpBuilder &b, Operation *moduleOp, SymbolTableCollection *symbolTables=nullptr)
bool isStaticShapeAndContiguousRowMajor(MemRefType type)
Returns true, if the memref type has static shapes and represents a contiguous chunk of memory.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
detail::InFlightRemark analysis(Location loc, RemarkOpts opts)
Report an optimization analysis remark.
Definition Remarks.h:723
void promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
Definition SCF.cpp:748
Include the generated interface declarations.
void registerConvertMemRefToLLVMInterface(DialectRegistry &registry)
static constexpr unsigned kDeriveIndexBitwidthFromDataLayout
Value to pass as bitwidth for the index type when the converter is expected to derive the bitwidth fr...
void populateFinalizeMemRefToLLVMConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, SymbolTableCollection *symbolTables=nullptr)
Collect a set of patterns to convert memory-related operations from the MemRef dialect to the LLVM di...
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)