1 | //===- Region.cpp - MLIR Region Class -------------------------------------===// |
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/Region.h" |
10 | #include "mlir/IR/IRMapping.h" |
11 | #include "mlir/IR/Operation.h" |
12 | using namespace mlir; |
13 | |
14 | Region::Region(Operation *container) : container(container) {} |
15 | |
16 | Region::~Region() { |
17 | // Operations may have cyclic references, which need to be dropped before we |
18 | // can start deleting them. |
19 | dropAllReferences(); |
20 | } |
21 | |
22 | /// Return the context this region is inserted in. The region must have a valid |
23 | /// parent container. |
24 | MLIRContext *Region::getContext() { |
25 | assert(container && "region is not attached to a container" ); |
26 | return container->getContext(); |
27 | } |
28 | |
29 | /// Return a location for this region. This is the location attached to the |
30 | /// parent container. The region must have a valid parent container. |
31 | Location Region::getLoc() { |
32 | assert(container && "region is not attached to a container" ); |
33 | return container->getLoc(); |
34 | } |
35 | |
36 | auto Region::getArgumentTypes() -> ValueTypeRange<BlockArgListType> { |
37 | return ValueTypeRange<BlockArgListType>(getArguments()); |
38 | } |
39 | |
40 | iterator_range<Region::args_iterator> |
41 | Region::addArguments(TypeRange types, ArrayRef<Location> locs) { |
42 | return front().addArguments(types, locs); |
43 | } |
44 | |
45 | Region *Region::getParentRegion() { |
46 | assert(container && "region is not attached to a container" ); |
47 | return container->getParentRegion(); |
48 | } |
49 | |
50 | bool Region::isProperAncestor(Region *other) { |
51 | if (this == other) |
52 | return false; |
53 | |
54 | while ((other = other->getParentRegion())) { |
55 | if (this == other) |
56 | return true; |
57 | } |
58 | return false; |
59 | } |
60 | |
61 | /// Return the number of this region in the parent operation. |
62 | unsigned Region::getRegionNumber() { |
63 | // Regions are always stored consecutively, so use pointer subtraction to |
64 | // figure out what number this is. |
65 | return this - &getParentOp()->getRegions()[0]; |
66 | } |
67 | |
68 | /// Clone the internal blocks from this region into `dest`. Any |
69 | /// cloned blocks are appended to the back of dest. |
70 | void Region::cloneInto(Region *dest, IRMapping &mapper) { |
71 | assert(dest && "expected valid region to clone into" ); |
72 | cloneInto(dest, destPos: dest->end(), mapper); |
73 | } |
74 | |
75 | /// Clone this region into 'dest' before the given position in 'dest'. |
76 | void Region::cloneInto(Region *dest, Region::iterator destPos, |
77 | IRMapping &mapper) { |
78 | assert(dest && "expected valid region to clone into" ); |
79 | assert(this != dest && "cannot clone region into itself" ); |
80 | |
81 | // If the list is empty there is nothing to clone. |
82 | if (empty()) |
83 | return; |
84 | |
85 | // The below clone implementation takes special care to be read only for the |
86 | // sake of multi threading. That essentially means not adding any uses to any |
87 | // of the blocks or operation results contained within this region as that |
88 | // would lead to a write in their use-def list. This is unavoidable for |
89 | // 'Value's from outside the region however, in which case it is not read |
90 | // only. Using the BlockAndValueMapper it is possible to remap such 'Value's |
91 | // to ones owned by the calling thread however, making it read only once |
92 | // again. |
93 | |
94 | // First clone all the blocks and block arguments and map them, but don't yet |
95 | // clone the operations, as they may otherwise add a use to a block that has |
96 | // not yet been mapped |
97 | for (Block &block : *this) { |
98 | Block *newBlock = new Block(); |
99 | mapper.map(from: &block, to: newBlock); |
100 | |
101 | // Clone the block arguments. The user might be deleting arguments to the |
102 | // block by specifying them in the mapper. If so, we don't add the |
103 | // argument to the cloned block. |
104 | for (auto arg : block.getArguments()) |
105 | if (!mapper.contains(from: arg)) |
106 | mapper.map(from: arg, to: newBlock->addArgument(type: arg.getType(), loc: arg.getLoc())); |
107 | |
108 | dest->getBlocks().insert(where: destPos, New: newBlock); |
109 | } |
110 | |
111 | auto newBlocksRange = |
112 | llvm::make_range(x: Region::iterator(mapper.lookup(from: &front())), y: destPos); |
113 | |
114 | // Now follow up with creating the operations, but don't yet clone their |
115 | // regions, nor set their operands. Setting the successors is safe as all have |
116 | // already been mapped. We are essentially just creating the operation results |
117 | // to be able to map them. |
118 | // Cloning the operands and region as well would lead to uses of operations |
119 | // not yet mapped. |
120 | auto cloneOptions = |
121 | Operation::CloneOptions::all().cloneRegions(enable: false).cloneOperands(enable: false); |
122 | for (auto zippedBlocks : llvm::zip(t&: *this, u&: newBlocksRange)) { |
123 | Block &sourceBlock = std::get<0>(t&: zippedBlocks); |
124 | Block &clonedBlock = std::get<1>(t&: zippedBlocks); |
125 | // Clone and remap the operations within this block. |
126 | for (Operation &op : sourceBlock) |
127 | clonedBlock.push_back(op: op.clone(mapper, options: cloneOptions)); |
128 | } |
129 | |
130 | // Finally now that all operation results have been mapped, set the operands |
131 | // and clone the regions. |
132 | SmallVector<Value> operands; |
133 | for (auto zippedBlocks : llvm::zip(t&: *this, u&: newBlocksRange)) { |
134 | for (auto ops : |
135 | llvm::zip(t&: std::get<0>(t&: zippedBlocks), u&: std::get<1>(t&: zippedBlocks))) { |
136 | Operation &source = std::get<0>(t&: ops); |
137 | Operation &clone = std::get<1>(t&: ops); |
138 | |
139 | operands.resize(N: source.getNumOperands()); |
140 | llvm::transform( |
141 | Range: source.getOperands(), d_first: operands.begin(), |
142 | F: [&](Value operand) { return mapper.lookupOrDefault(from: operand); }); |
143 | clone.setOperands(operands); |
144 | |
145 | for (auto regions : llvm::zip(t: source.getRegions(), u: clone.getRegions())) |
146 | std::get<0>(t&: regions).cloneInto(dest: &std::get<1>(t&: regions), mapper); |
147 | } |
148 | } |
149 | } |
150 | |
151 | /// Returns 'block' if 'block' lies in this region, or otherwise finds the |
152 | /// ancestor of 'block' that lies in this region. Returns nullptr if the latter |
153 | /// fails. |
154 | Block *Region::findAncestorBlockInRegion(Block &block) { |
155 | Block *currBlock = █ |
156 | while (currBlock->getParent() != this) { |
157 | Operation *parentOp = currBlock->getParentOp(); |
158 | if (!parentOp || !parentOp->getBlock()) |
159 | return nullptr; |
160 | currBlock = parentOp->getBlock(); |
161 | } |
162 | return currBlock; |
163 | } |
164 | |
165 | /// Returns 'op' if 'op' lies in this region, or otherwise finds the |
166 | /// ancestor of 'op' that lies in this region. Returns nullptr if the |
167 | /// latter fails. |
168 | Operation *Region::findAncestorOpInRegion(Operation &op) { |
169 | Operation *curOp = &op; |
170 | while (Region *opRegion = curOp->getParentRegion()) { |
171 | if (opRegion == this) |
172 | return curOp; |
173 | |
174 | curOp = opRegion->getParentOp(); |
175 | if (!curOp) |
176 | return nullptr; |
177 | } |
178 | return nullptr; |
179 | } |
180 | |
181 | void Region::dropAllReferences() { |
182 | for (Block &b : *this) |
183 | b.dropAllReferences(); |
184 | } |
185 | |
186 | Region *llvm::ilist_traits<::mlir::Block>::getParentRegion() { |
187 | size_t offset( |
188 | size_t(&((Region *)nullptr->*Region::getSublistAccess(nullptr)))); |
189 | iplist<Block> *anchor(static_cast<iplist<Block> *>(this)); |
190 | return reinterpret_cast<Region *>(reinterpret_cast<char *>(anchor) - offset); |
191 | } |
192 | |
193 | /// This is a trait method invoked when a basic block is added to a region. |
194 | /// We keep the region pointer up to date. |
195 | void llvm::ilist_traits<::mlir::Block>::addNodeToList(Block *block) { |
196 | assert(!block->getParent() && "already in a region!" ); |
197 | block->parentValidOpOrderPair.setPointer(getParentRegion()); |
198 | } |
199 | |
200 | /// This is a trait method invoked when an operation is removed from a |
201 | /// region. We keep the region pointer up to date. |
202 | void llvm::ilist_traits<::mlir::Block>::removeNodeFromList(Block *block) { |
203 | assert(block->getParent() && "not already in a region!" ); |
204 | block->parentValidOpOrderPair.setPointer(nullptr); |
205 | } |
206 | |
207 | /// This is a trait method invoked when an operation is moved from one block |
208 | /// to another. We keep the block pointer up to date. |
209 | void llvm::ilist_traits<::mlir::Block>::transferNodesFromList( |
210 | ilist_traits<Block> &otherList, block_iterator first, block_iterator last) { |
211 | // If we are transferring operations within the same function, the parent |
212 | // pointer doesn't need to be updated. |
213 | auto *curParent = getParentRegion(); |
214 | if (curParent == otherList.getParentRegion()) |
215 | return; |
216 | |
217 | // Update the 'parent' member of each Block. |
218 | for (; first != last; ++first) |
219 | first->parentValidOpOrderPair.setPointer(curParent); |
220 | } |
221 | |
222 | //===----------------------------------------------------------------------===// |
223 | // Region::OpIterator |
224 | //===----------------------------------------------------------------------===// |
225 | |
226 | Region::OpIterator::OpIterator(Region *region, bool end) |
227 | : region(region), block(end ? region->end() : region->begin()) { |
228 | if (!region->empty()) |
229 | skipOverBlocksWithNoOps(); |
230 | } |
231 | |
232 | Region::OpIterator &Region::OpIterator::operator++() { |
233 | // We increment over operations, if we reach the last use then move to next |
234 | // block. |
235 | if (operation != block->end()) |
236 | ++operation; |
237 | if (operation == block->end()) { |
238 | ++block; |
239 | skipOverBlocksWithNoOps(); |
240 | } |
241 | return *this; |
242 | } |
243 | |
244 | void Region::OpIterator::skipOverBlocksWithNoOps() { |
245 | while (block != region->end() && block->empty()) |
246 | ++block; |
247 | |
248 | // If we are at the last block, then set the operation to first operation of |
249 | // next block (sentinel value used for end). |
250 | if (block == region->end()) |
251 | operation = {}; |
252 | else |
253 | operation = block->begin(); |
254 | } |
255 | |
256 | //===----------------------------------------------------------------------===// |
257 | // RegionRange |
258 | //===----------------------------------------------------------------------===// |
259 | |
260 | RegionRange::RegionRange(MutableArrayRef<Region> regions) |
261 | : RegionRange(regions.data(), regions.size()) {} |
262 | RegionRange::RegionRange(ArrayRef<std::unique_ptr<Region>> regions) |
263 | : RegionRange(regions.data(), regions.size()) {} |
264 | RegionRange::RegionRange(ArrayRef<Region *> regions) |
265 | : RegionRange(const_cast<Region **>(regions.data()), regions.size()) {} |
266 | |
267 | /// See `llvm::detail::indexed_accessor_range_base` for details. |
268 | RegionRange::OwnerT RegionRange::offset_base(const OwnerT &owner, |
269 | ptrdiff_t index) { |
270 | if (auto *region = llvm::dyn_cast_if_present<const std::unique_ptr<Region> *>(Val: owner)) |
271 | return region + index; |
272 | if (auto **region = llvm::dyn_cast_if_present<Region **>(Val: owner)) |
273 | return region + index; |
274 | return &owner.get<Region *>()[index]; |
275 | } |
276 | /// See `llvm::detail::indexed_accessor_range_base` for details. |
277 | Region *RegionRange::dereference_iterator(const OwnerT &owner, |
278 | ptrdiff_t index) { |
279 | if (auto *region = llvm::dyn_cast_if_present<const std::unique_ptr<Region> *>(Val: owner)) |
280 | return region[index].get(); |
281 | if (auto **region = llvm::dyn_cast_if_present<Region **>(Val: owner)) |
282 | return region[index]; |
283 | return &owner.get<Region *>()[index]; |
284 | } |
285 | |