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 = █ 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