xref: /llvm-project-15.0.7/mlir/lib/IR/Region.cpp (revision 2ea3c8a5)
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 /// Return a range containing the types of the arguments for this region.
37 auto Region::getArgumentTypes() -> ValueTypeRange<BlockArgListType> {
38   return ValueTypeRange<BlockArgListType>(getArguments());
39 }
40 
41 /// Add one argument to the argument list for each type specified in the list.
42 iterator_range<Region::args_iterator> Region::addArguments(TypeRange types) {
43   return front().addArguments(types);
44 }
45 
46 Region *Region::getParentRegion() {
47   assert(container && "region is not attached to a container");
48   return container->getParentRegion();
49 }
50 
51 bool Region::isProperAncestor(Region *other) {
52   if (this == other)
53     return false;
54 
55   while ((other = other->getParentRegion())) {
56     if (this == other)
57       return true;
58   }
59   return false;
60 }
61 
62 /// Return the number of this region in the parent operation.
63 unsigned Region::getRegionNumber() {
64   // Regions are always stored consecutively, so use pointer subtraction to
65   // figure out what number this is.
66   return this - &getParentOp()->getRegions()[0];
67 }
68 
69 /// Clone the internal blocks from this region into `dest`. Any
70 /// cloned blocks are appended to the back of dest.
71 void Region::cloneInto(Region *dest, BlockAndValueMapping &mapper) {
72   assert(dest && "expected valid region to clone into");
73   cloneInto(dest, dest->end(), mapper);
74 }
75 
76 /// Clone this region into 'dest' before the given position in 'dest'.
77 void Region::cloneInto(Region *dest, Region::iterator destPos,
78                        BlockAndValueMapping &mapper) {
79   assert(dest && "expected valid region to clone into");
80   assert(this != dest && "cannot clone region into itself");
81 
82   // If the list is empty there is nothing to clone.
83   if (empty())
84     return;
85 
86   for (Block &block : *this) {
87     Block *newBlock = new Block();
88     mapper.map(&block, newBlock);
89 
90     // Clone the block arguments. The user might be deleting arguments to the
91     // block by specifying them in the mapper. If so, we don't add the
92     // argument to the cloned block.
93     for (auto arg : block.getArguments())
94       if (!mapper.contains(arg))
95         mapper.map(arg, newBlock->addArgument(arg.getType()));
96 
97     // Clone and remap the operations within this block.
98     for (auto &op : block)
99       newBlock->push_back(op.clone(mapper));
100 
101     dest->getBlocks().insert(destPos, newBlock);
102   }
103 
104   // Now that each of the blocks have been cloned, go through and remap the
105   // operands of each of the operations.
106   auto remapOperands = [&](Operation *op) {
107     for (auto &operand : op->getOpOperands())
108       if (auto mappedOp = mapper.lookupOrNull(operand.get()))
109         operand.set(mappedOp);
110     for (auto &succOp : op->getBlockOperands())
111       if (auto *mappedOp = mapper.lookupOrNull(succOp.get()))
112         succOp.set(mappedOp);
113   };
114 
115   for (iterator it(mapper.lookup(&front())); it != destPos; ++it)
116     it->walk(remapOperands);
117 }
118 
119 /// Returns 'block' if 'block' lies in this region, or otherwise finds the
120 /// ancestor of 'block' that lies in this region. Returns nullptr if the latter
121 /// fails.
122 Block *Region::findAncestorBlockInRegion(Block &block) {
123   Block *currBlock = &block;
124   while (currBlock->getParent() != this) {
125     Operation *parentOp = currBlock->getParentOp();
126     if (!parentOp || !parentOp->getBlock())
127       return nullptr;
128     currBlock = parentOp->getBlock();
129   }
130   return currBlock;
131 }
132 
133 /// Returns 'op' if 'op' lies in this region, or otherwise finds the
134 /// ancestor of 'op' that lies in this region. Returns nullptr if the
135 /// latter fails.
136 Operation *Region::findAncestorOpInRegion(Operation &op) {
137   Operation *curOp = &op;
138   while (Region *opRegion = curOp->getParentRegion()) {
139     if (opRegion == this)
140       return curOp;
141 
142     curOp = opRegion->getParentOp();
143     if (!curOp)
144       return nullptr;
145   }
146   return nullptr;
147 }
148 
149 void Region::dropAllReferences() {
150   for (Block &b : *this)
151     b.dropAllReferences();
152 }
153 
154 Region *llvm::ilist_traits<::mlir::Block>::getParentRegion() {
155   size_t Offset(
156       size_t(&((Region *)nullptr->*Region::getSublistAccess(nullptr))));
157   iplist<Block> *Anchor(static_cast<iplist<Block> *>(this));
158   return reinterpret_cast<Region *>(reinterpret_cast<char *>(Anchor) - Offset);
159 }
160 
161 /// This is a trait method invoked when a basic block is added to a region.
162 /// We keep the region pointer up to date.
163 void llvm::ilist_traits<::mlir::Block>::addNodeToList(Block *block) {
164   assert(!block->getParent() && "already in a region!");
165   block->parentValidOpOrderPair.setPointer(getParentRegion());
166 }
167 
168 /// This is a trait method invoked when an operation is removed from a
169 /// region.  We keep the region pointer up to date.
170 void llvm::ilist_traits<::mlir::Block>::removeNodeFromList(Block *block) {
171   assert(block->getParent() && "not already in a region!");
172   block->parentValidOpOrderPair.setPointer(nullptr);
173 }
174 
175 /// This is a trait method invoked when an operation is moved from one block
176 /// to another.  We keep the block pointer up to date.
177 void llvm::ilist_traits<::mlir::Block>::transferNodesFromList(
178     ilist_traits<Block> &otherList, block_iterator first, block_iterator last) {
179   // If we are transferring operations within the same function, the parent
180   // pointer doesn't need to be updated.
181   auto *curParent = getParentRegion();
182   if (curParent == otherList.getParentRegion())
183     return;
184 
185   // Update the 'parent' member of each Block.
186   for (; first != last; ++first)
187     first->parentValidOpOrderPair.setPointer(curParent);
188 }
189 
190 //===----------------------------------------------------------------------===//
191 // Region::OpIterator
192 //===----------------------------------------------------------------------===//
193 
194 Region::OpIterator::OpIterator(Region *region, bool end)
195     : region(region), block(end ? region->end() : region->begin()) {
196   if (!region->empty())
197     skipOverBlocksWithNoOps();
198 }
199 
200 Region::OpIterator &Region::OpIterator::operator++() {
201   // We increment over operations, if we reach the last use then move to next
202   // block.
203   if (operation != block->end())
204     ++operation;
205   if (operation == block->end()) {
206     ++block;
207     skipOverBlocksWithNoOps();
208   }
209   return *this;
210 }
211 
212 void Region::OpIterator::skipOverBlocksWithNoOps() {
213   while (block != region->end() && block->empty())
214     ++block;
215 
216   // If we are at the last block, then set the operation to first operation of
217   // next block (sentinel value used for end).
218   if (block == region->end())
219     operation = {};
220   else
221     operation = block->begin();
222 }
223 
224 //===----------------------------------------------------------------------===//
225 // RegionRange
226 //===----------------------------------------------------------------------===//
227 
228 RegionRange::RegionRange(MutableArrayRef<Region> regions)
229     : RegionRange(regions.data(), regions.size()) {}
230 RegionRange::RegionRange(ArrayRef<std::unique_ptr<Region>> regions)
231     : RegionRange(regions.data(), regions.size()) {}
232 
233 /// See `llvm::detail::indexed_accessor_range_base` for details.
234 RegionRange::OwnerT RegionRange::offset_base(const OwnerT &owner,
235                                              ptrdiff_t index) {
236   if (auto *operand = owner.dyn_cast<const std::unique_ptr<Region> *>())
237     return operand + index;
238   return &owner.get<Region *>()[index];
239 }
240 /// See `llvm::detail::indexed_accessor_range_base` for details.
241 Region *RegionRange::dereference_iterator(const OwnerT &owner,
242                                           ptrdiff_t index) {
243   if (auto *operand = owner.dyn_cast<const std::unique_ptr<Region> *>())
244     return operand[index].get();
245   return &owner.get<Region *>()[index];
246 }
247