/home/arjun/llvm-project/mlir/lib/IR/Region.cpp
Line | Count | Source (jump to first uncovered line) |
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/BlockAndValueMapping.h" |
11 | | #include "mlir/IR/Operation.h" |
12 | | using namespace mlir; |
13 | | |
14 | 0 | Region::Region(Operation *container) : container(container) {} |
15 | | |
16 | 0 | Region::~Region() { |
17 | 0 | // Operations may have cyclic references, which need to be dropped before we |
18 | 0 | // can start deleting them. |
19 | 0 | dropAllReferences(); |
20 | 0 | } |
21 | | |
22 | | /// Return the context this region is inserted in. The region must have a valid |
23 | | /// parent container. |
24 | 0 | MLIRContext *Region::getContext() { |
25 | 0 | assert(container && "region is not attached to a container"); |
26 | 0 | return container->getContext(); |
27 | 0 | } |
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 | 0 | Location Region::getLoc() { |
32 | 0 | assert(container && "region is not attached to a container"); |
33 | 0 | return container->getLoc(); |
34 | 0 | } |
35 | | |
36 | 0 | Region *Region::getParentRegion() { |
37 | 0 | assert(container && "region is not attached to a container"); |
38 | 0 | return container->getParentRegion(); |
39 | 0 | } |
40 | | |
41 | 0 | Operation *Region::getParentOp() { return container; } |
42 | | |
43 | 0 | bool Region::isProperAncestor(Region *other) { |
44 | 0 | if (this == other) |
45 | 0 | return false; |
46 | 0 | |
47 | 0 | while ((other = other->getParentRegion())) { |
48 | 0 | if (this == other) |
49 | 0 | return true; |
50 | 0 | } |
51 | 0 | return false; |
52 | 0 | } |
53 | | |
54 | | /// Return the number of this region in the parent operation. |
55 | 0 | unsigned Region::getRegionNumber() { |
56 | 0 | // Regions are always stored consecutively, so use pointer subtraction to |
57 | 0 | // figure out what number this is. |
58 | 0 | return this - &getParentOp()->getRegions()[0]; |
59 | 0 | } |
60 | | |
61 | | /// Clone the internal blocks from this region into `dest`. Any |
62 | | /// cloned blocks are appended to the back of dest. |
63 | 0 | void Region::cloneInto(Region *dest, BlockAndValueMapping &mapper) { |
64 | 0 | assert(dest && "expected valid region to clone into"); |
65 | 0 | cloneInto(dest, dest->end(), mapper); |
66 | 0 | } |
67 | | |
68 | | /// Clone this region into 'dest' before the given position in 'dest'. |
69 | | void Region::cloneInto(Region *dest, Region::iterator destPos, |
70 | 0 | BlockAndValueMapping &mapper) { |
71 | 0 | assert(dest && "expected valid region to clone into"); |
72 | 0 | assert(this != dest && "cannot clone region into itself"); |
73 | 0 |
|
74 | 0 | // If the list is empty there is nothing to clone. |
75 | 0 | if (empty()) |
76 | 0 | return; |
77 | 0 | |
78 | 0 | for (Block &block : *this) { |
79 | 0 | Block *newBlock = new Block(); |
80 | 0 | mapper.map(&block, newBlock); |
81 | 0 |
|
82 | 0 | // Clone the block arguments. The user might be deleting arguments to the |
83 | 0 | // block by specifying them in the mapper. If so, we don't add the |
84 | 0 | // argument to the cloned block. |
85 | 0 | for (auto arg : block.getArguments()) |
86 | 0 | if (!mapper.contains(arg)) |
87 | 0 | mapper.map(arg, newBlock->addArgument(arg.getType())); |
88 | 0 |
|
89 | 0 | // Clone and remap the operations within this block. |
90 | 0 | for (auto &op : block) |
91 | 0 | newBlock->push_back(op.clone(mapper)); |
92 | 0 |
|
93 | 0 | dest->getBlocks().insert(destPos, newBlock); |
94 | 0 | } |
95 | 0 |
|
96 | 0 | // Now that each of the blocks have been cloned, go through and remap the |
97 | 0 | // operands of each of the operations. |
98 | 0 | auto remapOperands = [&](Operation *op) { |
99 | 0 | for (auto &operand : op->getOpOperands()) |
100 | 0 | if (auto mappedOp = mapper.lookupOrNull(operand.get())) |
101 | 0 | operand.set(mappedOp); |
102 | 0 | for (auto &succOp : op->getBlockOperands()) |
103 | 0 | if (auto *mappedOp = mapper.lookupOrNull(succOp.get())) |
104 | 0 | succOp.set(mappedOp); |
105 | 0 | }; |
106 | 0 |
|
107 | 0 | for (iterator it(mapper.lookup(&front())); it != destPos; ++it) |
108 | 0 | it->walk(remapOperands); |
109 | 0 | } |
110 | | |
111 | | /// Returns 'block' if 'block' lies in this region, or otherwise finds the |
112 | | /// ancestor of 'block' that lies in this region. Returns nullptr if the latter |
113 | | /// fails. |
114 | 0 | Block *Region::findAncestorBlockInRegion(Block &block) { |
115 | 0 | auto currBlock = █ |
116 | 0 | while (currBlock->getParent() != this) { |
117 | 0 | Operation *parentOp = currBlock->getParentOp(); |
118 | 0 | if (!parentOp || !parentOp->getBlock()) |
119 | 0 | return nullptr; |
120 | 0 | currBlock = parentOp->getBlock(); |
121 | 0 | } |
122 | 0 | return currBlock; |
123 | 0 | } |
124 | | |
125 | 0 | void Region::dropAllReferences() { |
126 | 0 | for (Block &b : *this) |
127 | 0 | b.dropAllReferences(); |
128 | 0 | } |
129 | | |
130 | | /// Check if there are any values used by operations in `region` defined |
131 | | /// outside its ancestor region `limit`. That is, given `A{B{C{}}}` with region |
132 | | /// `C` and limit `B`, the values defined in `B` can be used but the values |
133 | | /// defined in `A` cannot. Emit errors if `noteLoc` is provided; this location |
134 | | /// is used to point to the operation containing the region, the actual error is |
135 | | /// reported at the operation with an offending use. |
136 | | static bool isIsolatedAbove(Region ®ion, Region &limit, |
137 | 0 | Optional<Location> noteLoc) { |
138 | 0 | assert(limit.isAncestor(®ion) && |
139 | 0 | "expected isolation limit to be an ancestor of the given region"); |
140 | 0 |
|
141 | 0 | // List of regions to analyze. Each region is processed independently, with |
142 | 0 | // respect to the common `limit` region, so we can look at them in any order. |
143 | 0 | // Therefore, use a simple vector and push/pop back the current region. |
144 | 0 | SmallVector<Region *, 8> pendingRegions; |
145 | 0 | pendingRegions.push_back(®ion); |
146 | 0 |
|
147 | 0 | // Traverse all operations in the region. |
148 | 0 | while (!pendingRegions.empty()) { |
149 | 0 | for (Operation &op : pendingRegions.pop_back_val()->getOps()) { |
150 | 0 | for (Value operand : op.getOperands()) { |
151 | 0 | // operand should be non-null here if the IR is well-formed. But |
152 | 0 | // we don't assert here as this function is called from the verifier |
153 | 0 | // and so could be called on invalid IR. |
154 | 0 | if (!operand) { |
155 | 0 | if (noteLoc) |
156 | 0 | op.emitOpError("block's operand not defined").attachNote(noteLoc); |
157 | 0 | return false; |
158 | 0 | } |
159 | 0 |
|
160 | 0 | // Check that any value that is used by an operation is defined in the |
161 | 0 | // same region as either an operation result or a block argument. |
162 | 0 | if (operand.getParentRegion()->isProperAncestor(&limit)) { |
163 | 0 | if (noteLoc) { |
164 | 0 | op.emitOpError("using value defined outside the region") |
165 | 0 | .attachNote(noteLoc) |
166 | 0 | << "required by region isolation constraints"; |
167 | 0 | } |
168 | 0 | return false; |
169 | 0 | } |
170 | 0 | } |
171 | 0 | // Schedule any regions the operations contain for further checking. |
172 | 0 | pendingRegions.reserve(pendingRegions.size() + op.getNumRegions()); |
173 | 0 | for (Region &subRegion : op.getRegions()) |
174 | 0 | pendingRegions.push_back(&subRegion); |
175 | 0 | } |
176 | 0 | } |
177 | 0 | return true; |
178 | 0 | } |
179 | | |
180 | 0 | bool Region::isIsolatedFromAbove(Optional<Location> noteLoc) { |
181 | 0 | return isIsolatedAbove(*this, *this, noteLoc); |
182 | 0 | } |
183 | | |
184 | 0 | Region *llvm::ilist_traits<::mlir::Block>::getParentRegion() { |
185 | 0 | size_t Offset( |
186 | 0 | size_t(&((Region *)nullptr->*Region::getSublistAccess(nullptr)))); |
187 | 0 | iplist<Block> *Anchor(static_cast<iplist<Block> *>(this)); |
188 | 0 | return reinterpret_cast<Region *>(reinterpret_cast<char *>(Anchor) - Offset); |
189 | 0 | } |
190 | | |
191 | | /// This is a trait method invoked when a basic block is added to a region. |
192 | | /// We keep the region pointer up to date. |
193 | 0 | void llvm::ilist_traits<::mlir::Block>::addNodeToList(Block *block) { |
194 | 0 | assert(!block->getParent() && "already in a region!"); |
195 | 0 | block->parentValidOpOrderPair.setPointer(getParentRegion()); |
196 | 0 | } |
197 | | |
198 | | /// This is a trait method invoked when an operation is removed from a |
199 | | /// region. We keep the region pointer up to date. |
200 | 0 | void llvm::ilist_traits<::mlir::Block>::removeNodeFromList(Block *block) { |
201 | 0 | assert(block->getParent() && "not already in a region!"); |
202 | 0 | block->parentValidOpOrderPair.setPointer(nullptr); |
203 | 0 | } |
204 | | |
205 | | /// This is a trait method invoked when an operation is moved from one block |
206 | | /// to another. We keep the block pointer up to date. |
207 | | void llvm::ilist_traits<::mlir::Block>::transferNodesFromList( |
208 | 0 | ilist_traits<Block> &otherList, block_iterator first, block_iterator last) { |
209 | 0 | // If we are transferring operations within the same function, the parent |
210 | 0 | // pointer doesn't need to be updated. |
211 | 0 | auto *curParent = getParentRegion(); |
212 | 0 | if (curParent == otherList.getParentRegion()) |
213 | 0 | return; |
214 | 0 | |
215 | 0 | // Update the 'parent' member of each Block. |
216 | 0 | for (; first != last; ++first) |
217 | 0 | first->parentValidOpOrderPair.setPointer(curParent); |
218 | 0 | } |
219 | | |
220 | | //===----------------------------------------------------------------------===// |
221 | | // Region::OpIterator |
222 | | //===----------------------------------------------------------------------===// |
223 | | |
224 | | Region::OpIterator::OpIterator(Region *region, bool end) |
225 | 0 | : region(region), block(end ? region->end() : region->begin()) { |
226 | 0 | if (!region->empty()) |
227 | 0 | skipOverBlocksWithNoOps(); |
228 | 0 | } |
229 | | |
230 | 0 | Region::OpIterator &Region::OpIterator::operator++() { |
231 | 0 | // We increment over operations, if we reach the last use then move to next |
232 | 0 | // block. |
233 | 0 | if (operation != block->end()) |
234 | 0 | ++operation; |
235 | 0 | if (operation == block->end()) { |
236 | 0 | ++block; |
237 | 0 | skipOverBlocksWithNoOps(); |
238 | 0 | } |
239 | 0 | return *this; |
240 | 0 | } |
241 | | |
242 | 0 | void Region::OpIterator::skipOverBlocksWithNoOps() { |
243 | 0 | while (block != region->end() && block->empty()) |
244 | 0 | ++block; |
245 | 0 |
|
246 | 0 | // If we are at the last block, then set the operation to first operation of |
247 | 0 | // next block (sentinel value used for end). |
248 | 0 | if (block == region->end()) |
249 | 0 | operation = {}; |
250 | 0 | else |
251 | 0 | operation = block->begin(); |
252 | 0 | } |
253 | | |
254 | | //===----------------------------------------------------------------------===// |
255 | | // RegionRange |
256 | | //===----------------------------------------------------------------------===// |
257 | | |
258 | | RegionRange::RegionRange(MutableArrayRef<Region> regions) |
259 | 0 | : RegionRange(regions.data(), regions.size()) {} |
260 | | RegionRange::RegionRange(ArrayRef<std::unique_ptr<Region>> regions) |
261 | 0 | : RegionRange(regions.data(), regions.size()) {} |
262 | | |
263 | | /// See `llvm::detail::indexed_accessor_range_base` for details. |
264 | | RegionRange::OwnerT RegionRange::offset_base(const OwnerT &owner, |
265 | 0 | ptrdiff_t index) { |
266 | 0 | if (auto *operand = owner.dyn_cast<const std::unique_ptr<Region> *>()) |
267 | 0 | return operand + index; |
268 | 0 | return &owner.get<Region *>()[index]; |
269 | 0 | } |
270 | | /// See `llvm::detail::indexed_accessor_range_base` for details. |
271 | | Region *RegionRange::dereference_iterator(const OwnerT &owner, |
272 | 0 | ptrdiff_t index) { |
273 | 0 | if (auto *operand = owner.dyn_cast<const std::unique_ptr<Region> *>()) |
274 | 0 | return operand[index].get(); |
275 | 0 | return &owner.get<Region *>()[index]; |
276 | 0 | } |