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