MLIR 24.0.0git
AttrTypeSubElements.cpp
Go to the documentation of this file.
1//===- AttrTypeSubElements.cpp - Attr and Type SubElement Interfaces ------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "mlir/IR/Operation.h"
10#include <optional>
11
12using namespace mlir;
13
14//===----------------------------------------------------------------------===//
15// AttrTypeWalker
16//===----------------------------------------------------------------------===//
17
18WalkResult AttrTypeWalker::walkImpl(Attribute attr, WalkOrder order) {
19 return walkImpl(attr, attrWalkFns, order);
20}
21WalkResult AttrTypeWalker::walkImpl(Type type, WalkOrder order) {
22 return walkImpl(type, typeWalkFns, order);
23}
24
25template <typename T, typename WalkFns>
26WalkResult AttrTypeWalker::walkImpl(T element, WalkFns &walkFns,
27 WalkOrder order) {
28 // Check if we've already walk this element before.
29 auto key = std::make_pair(element.getAsOpaquePointer(), (int)order);
30 auto [it, inserted] =
31 visitedAttrTypes.try_emplace(key, WalkResult::advance());
32 if (!inserted)
33 return it->second;
34
35 // If we are walking in post order, walk the sub elements first.
36 if (order == WalkOrder::PostOrder) {
37 if (walkSubElements(element, order).wasInterrupted())
38 return visitedAttrTypes[key] = WalkResult::interrupt();
39 }
40
41 // Walk this element, bailing if skipped or interrupted.
42 for (auto &walkFn : llvm::reverse(walkFns)) {
43 WalkResult walkResult = walkFn(element);
44 if (walkResult.wasInterrupted())
45 return visitedAttrTypes[key] = WalkResult::interrupt();
46 if (walkResult.wasSkipped())
47 return WalkResult::advance();
48 }
49
50 // If we are walking in pre-order, walk the sub elements last.
51 if (order == WalkOrder::PreOrder) {
52 if (walkSubElements(element, order).wasInterrupted())
53 return WalkResult::interrupt();
54 }
55 return WalkResult::advance();
56}
57
58template <typename T>
59WalkResult AttrTypeWalker::walkSubElements(T interface, WalkOrder order) {
60 WalkResult result = WalkResult::advance();
61 auto walkFn = [&](auto element) {
62 if (element && !result.wasInterrupted())
63 result = walkImpl(element, order);
64 };
65 interface.walkImmediateSubElements(walkFn, walkFn);
66 return result.wasInterrupted() ? result : WalkResult::advance();
67}
68
69//===----------------------------------------------------------------------===//
70/// AttrTypeReplacerBase
71//===----------------------------------------------------------------------===//
72
73template <typename Concrete>
76 attrReplacementFns.emplace_back(std::move(fn));
77}
78
79template <typename Concrete>
81 ReplaceFn<Type> fn) {
82 typeReplacementFns.push_back(std::move(fn));
83}
84
85template <typename Concrete>
87 Operation *op, bool replaceAttrs, bool replaceLocs, bool replaceTypes) {
88 // Functor that replaces the given element if the new value is different,
89 // otherwise returns nullptr.
90 auto replaceIfDifferent = [&](auto element) {
91 auto replacement = static_cast<Concrete *>(this)->replace(element);
92 return (replacement && replacement != element) ? replacement : nullptr;
93 };
94
95 // Update the attribute dictionary.
96 if (replaceAttrs) {
97 if (auto newAttrs = replaceIfDifferent(op->getRawDictionaryAttrs()))
98 op->setDiscardableAttrs(cast<DictionaryAttr>(newAttrs));
99
100 if (op->getPropertiesStorageSize()) {
101 op->getName().walkInherentAttrs(op, [&](StringRef, Attribute &attr) {
102 if (Attribute replacement = replaceIfDifferent(attr))
103 attr = replacement;
104 });
105 }
106 }
107
108 // If we aren't updating locations or types, we're done.
109 if (!replaceTypes && !replaceLocs)
110 return;
111
112 // Update the location.
113 if (replaceLocs) {
114 if (Attribute newLoc = replaceIfDifferent(op->getLoc()))
115 op->setLoc(cast<LocationAttr>(newLoc));
116 }
117
118 // Update the result types.
119 if (replaceTypes) {
120 for (OpResult result : op->getResults())
121 if (Type newType = replaceIfDifferent(result.getType()))
122 result.setType(newType);
123 }
124
125 // Update any nested block arguments.
126 for (Region &region : op->getRegions()) {
127 for (Block &block : region) {
128 for (BlockArgument &arg : block.getArguments()) {
129 if (replaceLocs) {
130 if (Attribute newLoc = replaceIfDifferent(arg.getLoc()))
131 arg.setLoc(cast<LocationAttr>(newLoc));
132 }
133
134 if (replaceTypes) {
135 if (Type newType = replaceIfDifferent(arg.getType()))
136 arg.setType(newType);
137 }
138 }
139 }
140 }
141}
142
143template <typename Concrete>
145 Operation *op, bool replaceAttrs, bool replaceLocs, bool replaceTypes) {
146 op->walk([&](Operation *nestedOp) {
147 replaceElementsIn(nestedOp, replaceAttrs, replaceLocs, replaceTypes);
148 });
149}
150
151template <typename T, typename Replacer>
152static void updateSubElementImpl(T element, Replacer &replacer,
153 SmallVectorImpl<T> &newElements,
154 FailureOr<bool> &changed) {
155 // Bail early if we failed at any point.
156 if (failed(changed))
157 return;
158
159 // Guard against potentially null inputs. We always map null to null.
160 if (!element) {
161 newElements.push_back(nullptr);
162 return;
163 }
164
165 // Replace the element.
166 if (T result = replacer.replace(element)) {
167 newElements.push_back(result);
168 if (result != element)
169 changed = true;
170 } else {
171 changed = failure();
172 }
173}
174
175template <typename T, typename Replacer>
176static T replaceSubElements(T interface, Replacer &replacer) {
177 // Walk the current sub-elements, replacing them as necessary.
179 SmallVector<Type, 16> newTypes;
180 FailureOr<bool> changed = false;
181 interface.walkImmediateSubElements(
182 [&](Attribute element) {
183 updateSubElementImpl(element, replacer, newAttrs, changed);
184 },
185 [&](Type element) {
186 updateSubElementImpl(element, replacer, newTypes, changed);
187 });
188 if (failed(changed))
189 return nullptr;
190
191 // If any sub-elements changed, use the new elements during the replacement.
192 T result = interface;
193 if (*changed)
194 result = interface.replaceImmediateSubElements(newAttrs, newTypes);
195 return result;
196}
197
198/// Shared implementation of replacing a given attribute or type element.
199template <typename T, typename ReplaceFns, typename Replacer>
200static T replaceElementImpl(T element, ReplaceFns &replaceFns,
201 Replacer &replacer) {
202 T result = element;
203 WalkResult walkResult = WalkResult::advance();
204 for (auto &replaceFn : llvm::reverse(replaceFns)) {
205 if (std::optional<std::pair<T, WalkResult>> newRes = replaceFn(element)) {
206 std::tie(result, walkResult) = *newRes;
207 break;
208 }
209 }
210
211 // If an error occurred, return nullptr to indicate failure.
212 if (walkResult.wasInterrupted() || !result) {
213 return nullptr;
214 }
215
216 // Handle replacing sub-elements if this element is also a container.
217 if (!walkResult.wasSkipped()) {
218 // Replace the sub elements of this element, bailing if we fail.
219 if (!(result = replaceSubElements(result, replacer))) {
220 return nullptr;
222 }
223
224 return result;
225}
226
227template <typename Concrete>
229 return replaceElementImpl(attr, attrReplacementFns,
230 *static_cast<Concrete *>(this));
231}
232
233template <typename Concrete>
235 return replaceElementImpl(type, typeReplacementFns,
236 *static_cast<Concrete *>(this));
237}
238
239//===----------------------------------------------------------------------===//
240/// AttrTypeReplacer
241//===----------------------------------------------------------------------===//
242
244
245template <typename T>
246T AttrTypeReplacer::cachedReplaceImpl(T element) {
247 const void *opaqueElement = element.getAsOpaquePointer();
248 auto [it, inserted] = cache.try_emplace(opaqueElement, opaqueElement);
249 if (!inserted)
250 return T::getFromOpaquePointer(it->second);
251
252 T result = replaceBase(element);
253
254 cache[opaqueElement] = result.getAsOpaquePointer();
255 return result;
256}
257
259 return cachedReplaceImpl(attr);
260}
261
262Type AttrTypeReplacer::replace(Type type) { return cachedReplaceImpl(type); }
263
264//===----------------------------------------------------------------------===//
265/// CyclicAttrTypeReplacer
266//===----------------------------------------------------------------------===//
267
269
271 : cache([&](void *attr) { return breakCycleImpl(attr); }) {}
272
274 attrCycleBreakerFns.emplace_back(std::move(fn));
275}
276
278 typeCycleBreakerFns.emplace_back(std::move(fn));
279}
280
281template <typename T>
282T CyclicAttrTypeReplacer::cachedReplaceImpl(T element) {
283 void *opaqueTaggedElement = AttrOrType(element).getOpaqueValue();
285 cache.lookupOrInit(opaqueTaggedElement);
286 if (auto resultOpt = cacheEntry.get())
287 return T::getFromOpaquePointer(*resultOpt);
288
289 T result = replaceBase(element);
290
291 cacheEntry.resolve(result.getAsOpaquePointer());
292 return result;
293}
294
296 return cachedReplaceImpl(attr);
297}
298
300 return cachedReplaceImpl(type);
301}
302
303std::optional<const void *>
304CyclicAttrTypeReplacer::breakCycleImpl(void *element) {
305 AttrOrType attrType = AttrOrType::getFromOpaqueValue(element);
306 if (auto attr = dyn_cast<Attribute>(attrType)) {
307 for (auto &cyclicReplaceFn : llvm::reverse(attrCycleBreakerFns)) {
308 if (std::optional<Attribute> newRes = cyclicReplaceFn(attr)) {
309 return newRes->getAsOpaquePointer();
310 }
311 }
312 } else {
313 auto type = dyn_cast<Type>(attrType);
314 for (auto &cyclicReplaceFn : llvm::reverse(typeCycleBreakerFns)) {
315 if (std::optional<Type> newRes = cyclicReplaceFn(type)) {
316 return newRes->getAsOpaquePointer();
317 }
318 }
319 }
320 return std::nullopt;
321}
322
323//===----------------------------------------------------------------------===//
324// AttrTypeImmediateSubElementWalker
325//===----------------------------------------------------------------------===//
326
328 if (element)
329 walkAttrsFn(element);
330}
331
333 if (element)
334 walkTypesFn(element);
335}
static void updateSubElementImpl(T element, Replacer &replacer, SmallVectorImpl< T > &newElements, FailureOr< bool > &changed)
static T replaceElementImpl(T element, ReplaceFns &replaceFns, Replacer &replacer)
Shared implementation of replacing a given attribute or type element.
static T replaceSubElements(T interface, Replacer &replacer)
*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 inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
*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`
void walk(Attribute element)
Walk an attribute.
Attribute replace(Attribute attr)
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class represents an argument of a Block.
Definition Value.h:306
Block represents an ordered list of Operations.
Definition Block.h:33
void addCycleBreaker(CycleBreakerFn< Attribute > fn)
Register a cycle-breaking function.
Attribute replace(Attribute attr)
std::function< std::optional< T >(T)> CycleBreakerFn
A cycle-breaking function.
CacheEntry lookupOrInit(InT element)
Lookup the cache for a pre-calculated replacement for element.
This is a value defined by a result of an operation.
Definition Value.h:454
void walkInherentAttrs(Operation *op, InherentAttrVisitor visitor) const
Visit the inherent attributes stored in the properties of op.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
void setLoc(Location loc)
Set the source location the operation was defined or derived from.
Definition Operation.h:243
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
DictionaryAttr getRawDictionaryAttrs()
Return all attributes that are not stored as properties.
Definition Operation.h:561
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:729
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:849
result_range getResults()
Definition Operation.h:440
int getPropertiesStorageSize() const
Returns the properties storage size.
Definition Operation.h:948
void setDiscardableAttrs(DictionaryAttr newAttrs)
Set the discardable attribute dictionary on this operation.
Definition Operation.h:575
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
A utility result that is used to signal how to proceed with an ongoing walk:
Definition WalkResult.h:29
bool wasSkipped() const
Returns true if the walk was skipped.
Definition WalkResult.h:54
static WalkResult advance()
Definition WalkResult.h:47
bool wasInterrupted() const
Returns true if the walk was interrupted.
Definition WalkResult.h:51
static WalkResult interrupt()
Definition WalkResult.h:46
This class provides a base utility for replacing attributes/types, and their sub elements.
void recursivelyReplaceElementsIn(Operation *op, bool replaceAttrs=true, bool replaceLocs=false, bool replaceTypes=false)
Replace the elements within the given operation, and all nested operations.
Attribute replaceBase(Attribute attr)
Invokes the registered replacement functions from most recently registered to least recently register...
std::function< ReplaceFnResult< T >(T)> ReplaceFn
void replaceElementsIn(Operation *op, bool replaceAttrs=true, bool replaceLocs=false, bool replaceTypes=false)
Replace the elements within the given operation.
void addReplacement(ReplaceFn< Attribute > fn)
Register a replacement function for mapping a given attribute or type.
Include the generated interface declarations.
WalkOrder
Traversal order for region, block and operation walk utilities.
Definition Visitors.h:28
A possibly unresolved cache entry.
void resolve(OutT result)
Resolve an unresolved cache entry by providing the result to be stored in the cache.
const std::optional< OutT > & get() const
Get the resolved result if one exists.