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"
12using namespace mlir;
13
14Region::Region(Operation *container) : container(container) {}
15
16Region::~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.
24MLIRContext *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.
31Location Region::getLoc() {
32 assert(container && "region is not attached to a container");
33 return container->getLoc();
34}
35
36auto Region::getArgumentTypes() -> ValueTypeRange<BlockArgListType> {
37 return ValueTypeRange<BlockArgListType>(getArguments());
38}
39
40iterator_range<Region::args_iterator>
41Region::addArguments(TypeRange types, ArrayRef<Location> locs) {
42 return front().addArguments(types, locs);
43}
44
45Region *Region::getParentRegion() {
46 assert(container && "region is not attached to a container");
47 return container->getParentRegion();
48}
49
50bool 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.
62unsigned 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.
70void Region::cloneInto(Region *dest, IRMapping &mapper) {
71 assert(dest && "expected valid region to clone into");
72 cloneInto(dest, destPos: dest->end(), mapper);
73}
74
75/// Clone this region into 'dest' before the given position in 'dest'.
76void 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(from: &block, to: 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(from: arg))
106 mapper.map(from: arg, to: newBlock->addArgument(type: arg.getType(), loc: arg.getLoc()));
107
108 dest->getBlocks().insert(where: destPos, New: newBlock);
109 }
110
111 auto newBlocksRange =
112 llvm::make_range(x: Region::iterator(mapper.lookup(from: &front())), y: 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(enable: false).cloneOperands(enable: false);
122 for (auto zippedBlocks : llvm::zip(t&: *this, u&: newBlocksRange)) {
123 Block &sourceBlock = std::get<0>(t&: zippedBlocks);
124 Block &clonedBlock = std::get<1>(t&: zippedBlocks);
125 // Clone and remap the operations within this block.
126 for (Operation &op : sourceBlock)
127 clonedBlock.push_back(op: op.clone(mapper, options: 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(t&: *this, u&: newBlocksRange)) {
134 for (auto ops :
135 llvm::zip(t&: std::get<0>(t&: zippedBlocks), u&: std::get<1>(t&: zippedBlocks))) {
136 Operation &source = std::get<0>(t&: ops);
137 Operation &clone = std::get<1>(t&: ops);
138
139 operands.resize(N: source.getNumOperands());
140 llvm::transform(
141 Range: source.getOperands(), d_first: operands.begin(),
142 F: [&](Value operand) { return mapper.lookupOrDefault(from: operand); });
143 clone.setOperands(operands);
144
145 for (auto regions : llvm::zip(t: source.getRegions(), u: clone.getRegions()))
146 std::get<0>(t&: regions).cloneInto(dest: &std::get<1>(t&: 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.
154Block *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.
168Operation *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
181void Region::dropAllReferences() {
182 for (Block &b : *this)
183 b.dropAllReferences();
184}
185
186Region *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.
195void 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.
202void 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.
209void 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
226Region::OpIterator::OpIterator(Region *region, bool end)
227 : region(region), block(end ? region->end() : region->begin()) {
228 if (!region->empty())
229 skipOverBlocksWithNoOps();
230}
231
232Region::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
244void 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
260RegionRange::RegionRange(MutableArrayRef<Region> regions)
261 : RegionRange(regions.data(), regions.size()) {}
262RegionRange::RegionRange(ArrayRef<std::unique_ptr<Region>> regions)
263 : RegionRange(regions.data(), regions.size()) {}
264RegionRange::RegionRange(ArrayRef<Region *> regions)
265 : RegionRange(const_cast<Region **>(regions.data()), regions.size()) {}
266
267/// See `llvm::detail::indexed_accessor_range_base` for details.
268RegionRange::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> *>(Val: owner))
271 return region + index;
272 if (auto **region = llvm::dyn_cast_if_present<Region **>(Val: owner))
273 return region + index;
274 return &owner.get<Region *>()[index];
275}
276/// See `llvm::detail::indexed_accessor_range_base` for details.
277Region *RegionRange::dereference_iterator(const OwnerT &owner,
278 ptrdiff_t index) {
279 if (auto *region = llvm::dyn_cast_if_present<const std::unique_ptr<Region> *>(Val: owner))
280 return region[index].get();
281 if (auto **region = llvm::dyn_cast_if_present<Region **>(Val: owner))
282 return region[index];
283 return &owner.get<Region *>()[index];
284}
285

source code of mlir/lib/IR/Region.cpp