| 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, destPos: 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 IRMapper 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. |
| 154 | Block *Region::findAncestorBlockInRegion(Block &block) { |
| 155 | Block *currBlock = █ |
| 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> *>(Val: owner)) |
| 271 | return region + index; |
| 272 | if (auto **region = llvm::dyn_cast_if_present<Region **>(Val: owner)) |
| 273 | return region + index; |
| 274 | return &cast<Region *>(Val: 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> *>(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 &cast<Region *>(Val: owner)[index]; |
| 284 | } |
| 285 | |