xref: /llvm-project-15.0.7/mlir/lib/IR/Region.cpp (revision 8f5beb4c)
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 Region *Region::getParentRegion() {
37   assert(container && "region is not attached to a container");
38   return container->getParentRegion();
39 }
40 
41 Operation *Region::getParentOp() { return container; }
42 
43 bool Region::isProperAncestor(Region *other) {
44   if (this == other)
45     return false;
46 
47   while ((other = other->getParentRegion())) {
48     if (this == other)
49       return true;
50   }
51   return false;
52 }
53 
54 /// Return the number of this region in the parent operation.
55 unsigned Region::getRegionNumber() {
56   // Regions are always stored consecutively, so use pointer subtraction to
57   // figure out what number this is.
58   return this - &getParentOp()->getRegions()[0];
59 }
60 
61 /// Clone the internal blocks from this region into `dest`. Any
62 /// cloned blocks are appended to the back of dest.
63 void Region::cloneInto(Region *dest, BlockAndValueMapping &mapper) {
64   assert(dest && "expected valid region to clone into");
65   cloneInto(dest, dest->end(), mapper);
66 }
67 
68 /// Clone this region into 'dest' before the given position in 'dest'.
69 void Region::cloneInto(Region *dest, Region::iterator destPos,
70                        BlockAndValueMapping &mapper) {
71   assert(dest && "expected valid region to clone into");
72   assert(this != dest && "cannot clone region into itself");
73 
74   // If the list is empty there is nothing to clone.
75   if (empty())
76     return;
77 
78   for (Block &block : *this) {
79     Block *newBlock = new Block();
80     mapper.map(&block, newBlock);
81 
82     // Clone the block arguments. The user might be deleting arguments to the
83     // block by specifying them in the mapper. If so, we don't add the
84     // argument to the cloned block.
85     for (auto arg : block.getArguments())
86       if (!mapper.contains(arg))
87         mapper.map(arg, newBlock->addArgument(arg.getType()));
88 
89     // Clone and remap the operations within this block.
90     for (auto &op : block)
91       newBlock->push_back(op.clone(mapper));
92 
93     dest->getBlocks().insert(destPos, newBlock);
94   }
95 
96   // Now that each of the blocks have been cloned, go through and remap the
97   // operands of each of the operations.
98   auto remapOperands = [&](Operation *op) {
99     for (auto &operand : op->getOpOperands())
100       if (auto mappedOp = mapper.lookupOrNull(operand.get()))
101         operand.set(mappedOp);
102     for (auto &succOp : op->getBlockOperands())
103       if (auto *mappedOp = mapper.lookupOrNull(succOp.get()))
104         succOp.set(mappedOp);
105   };
106 
107   for (iterator it(mapper.lookup(&front())); it != destPos; ++it)
108     it->walk(remapOperands);
109 }
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 Block *Region::findAncestorBlockInRegion(Block &block) {
115   auto currBlock = █
116   while (currBlock->getParent() != this) {
117     Operation *parentOp = currBlock->getParentOp();
118     if (!parentOp || !parentOp->getBlock())
119       return nullptr;
120     currBlock = parentOp->getBlock();
121   }
122   return currBlock;
123 }
124 
125 void Region::dropAllReferences() {
126   for (Block &b : *this)
127     b.dropAllReferences();
128 }
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 &region, Region &limit,
137                             Optional<Location> noteLoc) {
138   assert(limit.isAncestor(&region) &&
139          "expected isolation limit to be an ancestor of the given region");
140 
141   // List of regions to analyze.  Each region is processed independently, with
142   // respect to the common `limit` region, so we can look at them in any order.
143   // Therefore, use a simple vector and push/pop back the current region.
144   SmallVector<Region *, 8> pendingRegions;
145   pendingRegions.push_back(&region);
146 
147   // Traverse all operations in the region.
148   while (!pendingRegions.empty()) {
149     for (Block &block : *pendingRegions.pop_back_val()) {
150       for (Operation &op : block) {
151         for (Value operand : op.getOperands()) {
152           // operand should be non-null here if the IR is well-formed. But
153           // we don't assert here as this function is called from the verifier
154           // and so could be called on invalid IR.
155           if (!operand) {
156             if (noteLoc)
157               op.emitOpError("block's operand not defined").attachNote(noteLoc);
158             return false;
159           }
160 
161           // Check that any value that is used by an operation is defined in the
162           // same region as either an operation result or a block argument.
163           if (operand.getParentRegion()->isProperAncestor(&limit)) {
164             if (noteLoc) {
165               op.emitOpError("using value defined outside the region")
166                       .attachNote(noteLoc)
167                   << "required by region isolation constraints";
168             }
169             return false;
170           }
171         }
172         // Schedule any regions the operations contain for further checking.
173         pendingRegions.reserve(pendingRegions.size() + op.getNumRegions());
174         for (Region &subRegion : op.getRegions())
175           pendingRegions.push_back(&subRegion);
176       }
177     }
178   }
179   return true;
180 }
181 
182 bool Region::isIsolatedFromAbove(Optional<Location> noteLoc) {
183   return isIsolatedAbove(*this, *this, noteLoc);
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 // RegionRange
224 //===----------------------------------------------------------------------===//
225 
226 RegionRange::RegionRange(MutableArrayRef<Region> regions)
227     : RegionRange(regions.data(), regions.size()) {}
228 RegionRange::RegionRange(ArrayRef<std::unique_ptr<Region>> regions)
229     : RegionRange(regions.data(), regions.size()) {}
230 
231 /// See `llvm::detail::indexed_accessor_range_base` for details.
232 RegionRange::OwnerT RegionRange::offset_base(const OwnerT &owner,
233                                              ptrdiff_t index) {
234   if (auto *operand = owner.dyn_cast<const std::unique_ptr<Region> *>())
235     return operand + index;
236   return &owner.get<Region *>()[index];
237 }
238 /// See `llvm::detail::indexed_accessor_range_base` for details.
239 Region *RegionRange::dereference_iterator(const OwnerT &owner,
240                                           ptrdiff_t index) {
241   if (auto *operand = owner.dyn_cast<const std::unique_ptr<Region> *>())
242     return operand[index].get();
243   return &owner.get<Region *>()[index];
244 }
245