MLIR 24.0.0git
MemRefTransformOps.cpp
Go to the documentation of this file.
1//===- MemRefTransformOps.cpp - Implementation of Memref transform ops ----===//
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
27#include "llvm/Support/Debug.h"
28
29using namespace mlir;
30
31#define DEBUG_TYPE "memref-transforms"
32#define DBGS() (llvm::dbgs() << '[' << DEBUG_TYPE << "] ")
33
34//===----------------------------------------------------------------------===//
35// Apply...ConversionPatternsOp
36//===----------------------------------------------------------------------===//
37
38std::unique_ptr<TypeConverter>
39transform::MemrefToLLVMTypeConverterOp::getTypeConverter() {
41 options.allocLowering =
44 options.useGenericFunctions = getUseGenericFunctions();
45
47 options.overrideIndexBitwidth(getIndexBitwidth());
48
49 // TODO: the following two options don't really make sense for
50 // memref_to_llvm_type_converter specifically but we should have a single
51 // to_llvm_type_converter.
52 if (getDataLayout().has_value())
53 options.dataLayout = llvm::DataLayout(getDataLayout().value());
54 options.useBarePtrCallConv = getUseBarePtrCallConv();
55
56 return std::make_unique<LLVMTypeConverter>(getContext(), options);
57}
58
59StringRef transform::MemrefToLLVMTypeConverterOp::getTypeConverterType() {
60 return "LLVMTypeConverter";
61}
62
63//===----------------------------------------------------------------------===//
64// Apply...PatternsOp
65//===----------------------------------------------------------------------===//
66
67namespace {
68class AllocToAllocaPattern : public OpRewritePattern<memref::AllocOp> {
69public:
70 explicit AllocToAllocaPattern(Operation *analysisRoot, int64_t maxSize = 0)
71 : OpRewritePattern<memref::AllocOp>(analysisRoot->getContext()),
72 dataLayoutAnalysis(analysisRoot), maxSize(maxSize) {}
73
74 LogicalResult matchAndRewrite(memref::AllocOp op,
75 PatternRewriter &rewriter) const override {
77 rewriter, op, [this](memref::AllocOp alloc, memref::DeallocOp dealloc) {
78 MemRefType type = alloc.getMemref().getType();
79 if (!type.hasStaticShape())
80 return false;
81
82 const DataLayout &dataLayout = dataLayoutAnalysis.getAtOrAbove(alloc);
83 int64_t elementSize = dataLayout.getTypeSize(type.getElementType());
84 return maxSize == 0 || type.getNumElements() * elementSize < maxSize;
85 }));
86 }
87
88private:
89 DataLayoutAnalysis dataLayoutAnalysis;
90 int64_t maxSize;
91};
92} // namespace
93
94void transform::ApplyAllocToAllocaOp::populatePatterns(
95 RewritePatternSet &patterns) {}
96
97void transform::ApplyAllocToAllocaOp::populatePatternsWithState(
99 patterns.insert<AllocToAllocaPattern>(
100 state.getTopLevel(), static_cast<int64_t>(getSizeLimit().value_or(0)));
101}
102
103void transform::ApplyExpandOpsPatternsOp::populatePatterns(
104 RewritePatternSet &patterns) {
106}
107
108void transform::ApplyExpandStridedMetadataPatternsOp::populatePatterns(
109 RewritePatternSet &patterns) {
111}
112
113void transform::ApplyExtractAddressComputationsPatternsOp::populatePatterns(
114 RewritePatternSet &patterns) {
116}
117
118void transform::ApplyFoldMemrefAliasOpsPatternsOp::populatePatterns(
119 RewritePatternSet &patterns) {
121}
122
123void transform::ApplyResolveRankedShapedTypeResultDimsPatternsOp::
124 populatePatterns(RewritePatternSet &patterns) {
126}
127
128//===----------------------------------------------------------------------===//
129// Alloc and alloca to global utilities
130//===----------------------------------------------------------------------===//
131
132/// Checks whether an allocation operation can be converted to a
133/// `memref.global`.
134template <typename AllocLikeOp>
136checkAllocToGlobalPreconditions(AllocLikeOp allocLikeOp) {
137 MemRefType memrefType = allocLikeOp.getType();
138 if (!memrefType.hasStaticShape()) {
139 return emitSilenceableFailure(allocLikeOp)
140 << "conversion to a global op requires statically shaped memrefs, "
141 "but got "
142 << memrefType;
143 }
144
145 if (!allocLikeOp.getSymbolOperands().empty()) {
146 return emitSilenceableFailure(allocLikeOp)
147 << "conversion to a global op does not support symbol operands, but "
148 "got "
149 << memrefType;
150 }
151
152 int64_t offset;
154 if (failed(memrefType.getStridesAndOffset(strides, offset))) {
155 return emitSilenceableFailure(allocLikeOp)
156 << "conversion to a global op requires strided layout, but got "
157 << memrefType;
158 }
159 if (!ShapedType::isStatic(offset) || !ShapedType::isStaticShape(strides)) {
160 return emitSilenceableFailure(allocLikeOp)
161 << "conversion to a global op does not support dynamic offset or "
162 "strides, but got "
163 << memrefType;
164 }
165
167}
168
169/// Converts an allocation operation (`memref.alloca` or `memref.alloc`) to a
170/// `memref.global` operation in the nearest symbol table, and replaces the
171/// allocation with a `memref.get_global` operation. Any `memref.dealloc`
172/// operations referencing the allocation are erased.
173template <typename AllocLikeOp>
176 AllocLikeOp allocLikeOp, StringRef globalName,
177 memref::GlobalOp &globalOp,
178 memref::GetGlobalOp &getGlobalOp) {
179 if (DiagnosedSilenceableFailure failure =
181 !failure.succeeded())
182 return failure;
183
184 MLIRContext *ctx = rewriter.getContext();
185 Location loc = allocLikeOp->getLoc();
186
187 // Find nearest symbol table.
188 Operation *symbolTableOp = SymbolTable::getNearestSymbolTable(allocLikeOp);
189 assert(symbolTableOp && "expected payload to be in symbol table");
190 SymbolTable symbolTable(symbolTableOp);
191
192 // Insert a `memref.global` into the symbol table.
193 Type resultType = allocLikeOp.getResult().getType();
194 OpBuilder builder(rewriter.getContext());
195 // TODO: Add a better builder for this.
196 globalOp = memref::GlobalOp::create(
197 builder, loc, StringAttr::get(ctx, globalName),
198 StringAttr::get(ctx, "private"), TypeAttr::get(resultType), Attribute{},
199 UnitAttr{}, allocLikeOp.getAlignmentAttr());
200 symbolTable.insert(globalOp);
201
202 // Remove any `memref.dealloc` operations referencing this allocation.
203 // We assume that the allocation does not escape the current container
204 // (e.g., via return or interprocedural function calls) and is not passed
205 // through control-flow or alias operations (e.g., `scf.if`, `cf.cond_br`,
206 // `select`, `memref.subview`), so any deallocation is a direct user of the
207 // allocation. Indirect deallocations are not removed and must be handled
208 // separately.
209 for (Operation *user : llvm::make_early_inc_range(allocLikeOp->getUsers())) {
210 if (auto dealloc = dyn_cast<memref::DeallocOp>(user))
211 rewriter.eraseOp(dealloc);
212 }
213
214 // Replace the allocation with a `memref.get_global` accessing the
215 // global symbol inserted above.
216 rewriter.setInsertionPoint(allocLikeOp);
217 getGlobalOp = rewriter.replaceOpWithNewOp<memref::GetGlobalOp>(
218 allocLikeOp, globalOp.getType(), globalOp.getName());
219
221}
222
223//===----------------------------------------------------------------------===//
224// AllocaToGlobalOp
225//===----------------------------------------------------------------------===//
226
228transform::MemRefAllocaToGlobalOp::apply(transform::TransformRewriter &rewriter,
231 auto allocaOps = state.getPayloadOps(getAlloca());
232
235
236 // Transform `memref.alloca`s.
237 for (auto *op : allocaOps) {
238 auto alloca = cast<memref::AllocaOp>(op);
239 memref::GlobalOp globalOp;
240 memref::GetGlobalOp getGlobalOp;
242 allocLikeToGlobal(rewriter, alloca, "alloca", globalOp, getGlobalOp);
243 if (!diag.succeeded())
244 return diag;
245
246 globalOps.push_back(globalOp);
247 getGlobalOps.push_back(getGlobalOp);
248 }
249
250 // Assemble results.
251 results.set(cast<OpResult>(getGlobal()), globalOps);
252 results.set(cast<OpResult>(getGetGlobal()), getGlobalOps);
253
255}
256
257void transform::MemRefAllocaToGlobalOp::getEffects(
259 producesHandle(getOperation()->getOpResults(), effects);
260 consumesHandle(getAllocaMutable(), effects);
261 modifiesPayload(effects);
262}
263
264//===----------------------------------------------------------------------===//
265// AllocToGlobalOp
266//===----------------------------------------------------------------------===//
267
269transform::MemRefAllocToGlobalOp::apply(transform::TransformRewriter &rewriter,
272 auto allocOps = state.getPayloadOps(getAlloc());
273
276
277 // Transform `memref.alloc`s.
278 for (auto *op : allocOps) {
279 auto alloc = cast<memref::AllocOp>(op);
280 memref::GlobalOp globalOp;
281 memref::GetGlobalOp getGlobalOp;
283 allocLikeToGlobal(rewriter, alloc, "alloc", globalOp, getGlobalOp);
284 if (!diag.succeeded())
285 return diag;
286
287 globalOps.push_back(globalOp);
288 getGlobalOps.push_back(getGlobalOp);
289 }
290
291 // Assemble results.
292 results.set(cast<OpResult>(getGlobal()), globalOps);
293 results.set(cast<OpResult>(getGetGlobal()), getGlobalOps);
294
296}
297
298void transform::MemRefAllocToGlobalOp::getEffects(
300 producesHandle(getOperation()->getOpResults(), effects);
301 consumesHandle(getAllocMutable(), effects);
302 modifiesPayload(effects);
303}
304
305//===----------------------------------------------------------------------===//
306// MemRefMultiBufferOp
307//===----------------------------------------------------------------------===//
308
309DiagnosedSilenceableFailure transform::MemRefMultiBufferOp::apply(
311 transform::TransformResults &transformResults,
314 for (Operation *op : state.getPayloadOps(getTarget())) {
315 bool canApplyMultiBuffer = true;
316 auto target = cast<memref::AllocOp>(op);
317 LLVM_DEBUG(DBGS() << "Start multibuffer transform op: " << target << "\n";);
318 // Skip allocations not used in a loop.
319 for (Operation *user : target->getUsers()) {
320 if (isa<memref::DeallocOp>(user))
321 continue;
322 auto loop = user->getParentOfType<LoopLikeOpInterface>();
323 if (!loop) {
324 LLVM_DEBUG(DBGS() << "--allocation not used in a loop\n";
325 DBGS() << "----due to user: " << *user;);
326 canApplyMultiBuffer = false;
327 break;
328 }
329 }
330 if (!canApplyMultiBuffer) {
331 LLVM_DEBUG(DBGS() << "--cannot apply multibuffering -> Skip\n";);
332 continue;
333 }
334
335 auto newBuffer =
336 memref::multiBuffer(rewriter, target, getFactor(), getSkipAnalysis());
337
338 if (failed(newBuffer)) {
339 LLVM_DEBUG(DBGS() << "--op failed to multibuffer\n";);
340 return emitSilenceableFailure(target->getLoc())
341 << "op failed to multibuffer";
342 }
343
344 results.push_back(*newBuffer);
345 }
346 transformResults.set(cast<OpResult>(getResult()), results);
348}
349
350//===----------------------------------------------------------------------===//
351// MemRefEraseDeadAllocAndStoresOp
352//===----------------------------------------------------------------------===//
353
355transform::MemRefEraseDeadAllocAndStoresOp::applyToOne(
359 // Apply store to load forwarding and dead store elimination.
360 vector::transferOpflowOpt(rewriter, target);
363}
364
365void transform::MemRefEraseDeadAllocAndStoresOp::getEffects(
367 transform::onlyReadsHandle(getTargetMutable(), effects);
369}
370void transform::MemRefEraseDeadAllocAndStoresOp::build(OpBuilder &builder,
372 Value target) {
373 result.addOperands(target);
374}
375
376//===----------------------------------------------------------------------===//
377// MemRefMakeLoopIndependentOp
378//===----------------------------------------------------------------------===//
379
380DiagnosedSilenceableFailure transform::MemRefMakeLoopIndependentOp::applyToOne(
384 // Gather IVs.
386 Operation *nextOp = target;
387 for (uint64_t i = 0, e = getNumLoops(); i < e; ++i) {
388 nextOp = nextOp->getParentOfType<scf::ForOp>();
389 if (!nextOp) {
390 DiagnosedSilenceableFailure diag = emitSilenceableError()
391 << "could not find " << i
392 << "-th enclosing loop";
393 diag.attachNote(target->getLoc()) << "target op";
394 return diag;
395 }
396 ivs.push_back(cast<scf::ForOp>(nextOp).getInductionVar());
397 }
398
399 // Rewrite IR.
400 FailureOr<Value> replacement = failure();
401 if (auto allocaOp = dyn_cast<memref::AllocaOp>(target)) {
402 replacement = memref::replaceWithIndependentOp(rewriter, allocaOp, ivs);
403 } else {
404 DiagnosedSilenceableFailure diag = emitSilenceableError()
405 << "unsupported target op";
406 diag.attachNote(target->getLoc()) << "target op";
407 return diag;
408 }
409 if (failed(replacement)) {
411 emitSilenceableError() << "could not make target op loop-independent";
412 diag.attachNote(target->getLoc()) << "target op";
413 return diag;
414 }
415 results.push_back(replacement->getDefiningOp());
417}
418
419//===----------------------------------------------------------------------===//
420// Transform op registration
421//===----------------------------------------------------------------------===//
422
423namespace {
424class MemRefTransformDialectExtension
426 MemRefTransformDialectExtension> {
427public:
428 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(MemRefTransformDialectExtension)
429
430 using Base::Base;
431
432 void init() {
433 declareGeneratedDialect<affine::AffineDialect>();
434 declareGeneratedDialect<arith::ArithDialect>();
435 declareGeneratedDialect<memref::MemRefDialect>();
436 declareGeneratedDialect<nvgpu::NVGPUDialect>();
437 declareGeneratedDialect<vector::VectorDialect>();
438
439 registerTransformOps<
440#define GET_OP_LIST
441#include "mlir/Dialect/MemRef/TransformOps/MemRefTransformOps.cpp.inc"
442 >();
443 }
444};
445} // namespace
446
447#define GET_OP_CLASSES
448#include "mlir/Dialect/MemRef/TransformOps/MemRefTransformOps.cpp.inc"
449
451 DialectRegistry &registry) {
452 registry.addExtensions<MemRefTransformDialectExtension>();
453}
return success()
static uint64_t getIndexBitwidth(DataLayoutEntryListRef params)
Returns the bitwidth of the index type if specified in the param list.
b getContext())
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static DiagnosedSilenceableFailure checkAllocToGlobalPreconditions(AllocLikeOp allocLikeOp)
Checks whether an allocation operation can be converted to a memref.global.
static DiagnosedSilenceableFailure allocLikeToGlobal(transform::TransformRewriter &rewriter, AllocLikeOp allocLikeOp, StringRef globalName, memref::GlobalOp &globalOp, memref::GetGlobalOp &getGlobalOp)
Converts an allocation operation (memref.alloca or memref.alloc) to a memref.global operation in the ...
#define DBGS()
static std::string diag(const llvm::Value &value)
static llvm::ManagedStatic< PassManagerOptions > options
#define MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CLASS_NAME)
Definition TypeID.h:331
Attributes are known-constant values of operations.
Definition Attributes.h:25
MLIRContext * getContext() const
Definition Builders.h:56
llvm::TypeSize getTypeSize(Type t) const
Returns the size of the given type in the current scope.
The result of a transform IR operation application.
static DiagnosedSilenceableFailure success()
Constructs a DiagnosedSilenceableFailure in the success state.
bool succeeded() const
Returns true if this is a success.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
void addExtensions()
Add the given extensions to the registry.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
Options to control the LLVM lowering.
@ Malloc
Use malloc for heap allocations.
@ AlignedAlloc
Use aligned_alloc for heap allocations.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class helps build Operations.
Definition Builders.h:210
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
RewritePatternSet & insert(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition SymbolTable.h:24
StringAttr insert(Operation *symbol, Block::iterator insertPt={})
Insert a new symbol into the table, and rename it as necessary to avoid collisions.
static Operation * getNearestSymbolTable(Operation *from)
Returns the nearest symbol table from a given operation from.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
A list of results of applying a transform op with ApplyEachOpTrait to a single payload operation,...
void push_back(Operation *op)
Appends an element to the list.
Base class for extensions of the Transform dialect that supports injecting operations into the Transf...
Local mapping between values defined by a specific op implementing the TransformOpInterface and the p...
void set(OpResult value, Range &&ops)
Indicates that the result of the transform IR op at the given position corresponds to the given list ...
This is a special rewriter to be used in transform op implementations, providing additional helper fu...
The state maintained across applications of various ops implementing the TransformOpInterface.
auto getPayloadOps(Value value) const
Returns an iterator that enumerates all ops that the given transform IR value corresponds to.
Operation * getTopLevel() const
Returns the op at which the transformation state is rooted.
void populateFoldMemRefAliasOpPatterns(RewritePatternSet &patterns)
Appends patterns for folding memref aliasing ops into consumer load/store ops into patterns.
void populateResolveRankedShapedTypeResultDimsPatterns(RewritePatternSet &patterns)
Appends patterns that resolve memref.dim operations with values that are defined by operations that i...
FailureOr< Value > replaceWithIndependentOp(RewriterBase &rewriter, memref::AllocaOp allocaOp, ValueRange independencies)
Build a new memref::AllocaOp whose dynamic sizes are independent of all given independencies.
void eraseDeadAllocAndStores(RewriterBase &rewriter, Operation *parentOp)
Track temporary allocations that are never read from.
FailureOr< memref::AllocOp > multiBuffer(RewriterBase &rewriter, memref::AllocOp allocOp, unsigned multiplier, bool skipOverrideAnalysis=false)
Transformation to do multi-buffering/array expansion to remove dependencies on the temporary allocati...
void populateExpandOpsPatterns(RewritePatternSet &patterns)
Collects a set of patterns to rewrite ops within the memref dialect.
void populateExtractAddressComputationsPatterns(RewritePatternSet &patterns)
Appends patterns for extracting address computations from memory access operations such that these ac...
memref::AllocaOp allocToAlloca(RewriterBase &rewriter, memref::AllocOp alloc, function_ref< bool(memref::AllocOp, memref::DeallocOp)> filter=nullptr)
Replaces the given alloc with the corresponding alloca and returns it if the following conditions are...
void populateExpandStridedMetadataPatterns(RewritePatternSet &patterns)
Appends patterns for expanding memref operations that modify the metadata (sizes, offset,...
void registerTransformDialectExtension(DialectRegistry &registry)
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
void producesHandle(ResultRange handles, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
void consumesHandle(MutableArrayRef< OpOperand > handles, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
Populates effects with the memory effects indicating the operation on the given handle value:
void onlyReadsHandle(MutableArrayRef< OpOperand > handles, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
void modifiesPayload(SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
Populates effects with the memory effects indicating the access to payload IR resource.
Include the generated interface declarations.
static constexpr unsigned kDeriveIndexBitwidthFromDataLayout
Value to pass as bitwidth for the index type when the converter is expected to derive the bitwidth fr...
DiagnosedSilenceableFailure emitSilenceableFailure(Location loc, const Twine &message={})
Emits a silenceable failure with the given message.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.