300 lines · cpp
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-exception6//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 we18 // can start deleting them.19 dropAllReferences();20}21 22/// Return the context this region is inserted in. The region must have a valid23/// 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 the30/// 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 to64 // figure out what number this is.65 return this - &getParentOp()->getRegions()[0];66}67 68/// Clone the internal blocks from this region into `dest`. Any69/// 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, 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 the86 // sake of multi threading. That essentially means not adding any uses to any87 // of the blocks or operation results contained within this region as that88 // would lead to a write in their use-def list. This is unavoidable for89 // 'Value's from outside the region however, in which case it is not read90 // only. Using the IRMapper it is possible to remap such 'Value's91 // to ones owned by the calling thread however, making it read only once92 // again.93 94 // First clone all the blocks and block arguments and map them, but don't yet95 // clone the operations, as they may otherwise add a use to a block that has96 // not yet been mapped97 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 the102 // block by specifying them in the mapper. If so, we don't add the103 // 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 their115 // regions, nor set their operands. Setting the successors is safe as all have116 // already been mapped. We are essentially just creating the operation results117 // to be able to map them.118 // Cloning the operands and region as well would lead to uses of operations119 // 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 operands131 // 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 the152/// ancestor of 'block' that lies in this region. Returns nullptr if the latter153/// fails.154Block *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 the166/// ancestor of 'op' that lies in this region. Returns nullptr if the167/// 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 a201/// 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 block208/// 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 parent212 // 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::OpIterator224//===----------------------------------------------------------------------===//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 next234 // 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 of249 // next block (sentinel value used for end).250 if (block == region->end())251 operation = {};252 else253 operation = block->begin();254}255 256llvm::raw_ostream &mlir::operator<<(llvm::raw_ostream &os, Region ®ion) {257 if (!region.getParentOp()) {258 os << "Region has no parent op";259 } else {260 os << "Region #" << region.getRegionNumber() << " in operation "261 << region.getParentOp()->getName();262 }263 for (auto it : llvm::enumerate(region.getBlocks())) {264 os << "\n Block #" << it.index() << ":";265 for (Operation &op : it.value().getOperations())266 os << "\n " << OpWithFlags(&op, OpPrintingFlags().skipRegions());267 }268 return os;269}270 271//===----------------------------------------------------------------------===//272// RegionRange273//===----------------------------------------------------------------------===//274 275RegionRange::RegionRange(MutableArrayRef<Region> regions)276 : RegionRange(regions.data(), regions.size()) {}277RegionRange::RegionRange(ArrayRef<std::unique_ptr<Region>> regions)278 : RegionRange(regions.data(), regions.size()) {}279RegionRange::RegionRange(ArrayRef<Region *> regions)280 : RegionRange(const_cast<Region **>(regions.data()), regions.size()) {}281 282/// See `llvm::detail::indexed_accessor_range_base` for details.283RegionRange::OwnerT RegionRange::offset_base(const OwnerT &owner,284 ptrdiff_t index) {285 if (auto *region = llvm::dyn_cast_if_present<const std::unique_ptr<Region> *>(owner))286 return region + index;287 if (auto **region = llvm::dyn_cast_if_present<Region **>(owner))288 return region + index;289 return &cast<Region *>(owner)[index];290}291/// See `llvm::detail::indexed_accessor_range_base` for details.292Region *RegionRange::dereference_iterator(const OwnerT &owner,293 ptrdiff_t index) {294 if (auto *region = llvm::dyn_cast_if_present<const std::unique_ptr<Region> *>(owner))295 return region[index].get();296 if (auto **region = llvm::dyn_cast_if_present<Region **>(owner))297 return region[index];298 return &cast<Region *>(owner)[index];299}300