xref: /llvm-project/mlir/lib/IR/Region.cpp (revision fcb1591b46f12b8908a8cdb252611708820102f8)
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, 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(&block, 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(arg))
106         mapper.map(arg, newBlock->addArgument(arg.getType(), arg.getLoc()));
107 
108     dest->getBlocks().insert(destPos, newBlock);
109   }
110 
111   auto newBlocksRange =
112       llvm::make_range(Region::iterator(mapper.lookup(&front())), 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(false).cloneOperands(false);
122   for (auto zippedBlocks : llvm::zip(*this, newBlocksRange)) {
123     Block &sourceBlock = std::get<0>(zippedBlocks);
124     Block &clonedBlock = std::get<1>(zippedBlocks);
125     // Clone and remap the operations within this block.
126     for (Operation &op : sourceBlock)
127       clonedBlock.push_back(op.clone(mapper, 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(*this, newBlocksRange)) {
134     for (auto ops :
135          llvm::zip(std::get<0>(zippedBlocks), std::get<1>(zippedBlocks))) {
136       Operation &source = std::get<0>(ops);
137       Operation &clone = std::get<1>(ops);
138 
139       operands.resize(source.getNumOperands());
140       llvm::transform(
141           source.getOperands(), operands.begin(),
142           [&](Value operand) { return mapper.lookupOrDefault(operand); });
143       clone.setOperands(operands);
144 
145       for (auto regions : llvm::zip(source.getRegions(), clone.getRegions()))
146         std::get<0>(regions).cloneInto(&std::get<1>(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 = &block;
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> *>(owner))
271     return region + index;
272   if (auto **region = llvm::dyn_cast_if_present<Region **>(owner))
273     return region + index;
274   return &cast<Region *>(owner)[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> *>(owner))
280     return region[index].get();
281   if (auto **region = llvm::dyn_cast_if_present<Region **>(owner))
282     return region[index];
283   return &cast<Region *>(owner)[index];
284 }
285