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