//===- Region.cpp - MLIR Region Class -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "mlir/IR/Region.h" #include "mlir/IR/BlockAndValueMapping.h" #include "mlir/IR/Operation.h" using namespace mlir; Region::Region(Operation *container) : container(container) {} Region::~Region() { // Operations may have cyclic references, which need to be dropped before we // can start deleting them. dropAllReferences(); } /// Return the context this region is inserted in. The region must have a valid /// parent container. MLIRContext *Region::getContext() { assert(container && "region is not attached to a container"); return container->getContext(); } /// Return a location for this region. This is the location attached to the /// parent container. The region must have a valid parent container. Location Region::getLoc() { assert(container && "region is not attached to a container"); return container->getLoc(); } /// Return a range containing the types of the arguments for this region. auto Region::getArgumentTypes() -> ValueTypeRange { return ValueTypeRange(getArguments()); } /// Add one argument to the argument list for each type specified in the list. iterator_range Region::addArguments(TypeRange types) { return front().addArguments(types); } Region *Region::getParentRegion() { assert(container && "region is not attached to a container"); return container->getParentRegion(); } Operation *Region::getParentOp() { return container; } bool Region::isProperAncestor(Region *other) { if (this == other) return false; while ((other = other->getParentRegion())) { if (this == other) return true; } return false; } /// Return the number of this region in the parent operation. unsigned Region::getRegionNumber() { // Regions are always stored consecutively, so use pointer subtraction to // figure out what number this is. return this - &getParentOp()->getRegions()[0]; } /// Clone the internal blocks from this region into `dest`. Any /// cloned blocks are appended to the back of dest. void Region::cloneInto(Region *dest, BlockAndValueMapping &mapper) { assert(dest && "expected valid region to clone into"); cloneInto(dest, dest->end(), mapper); } /// Clone this region into 'dest' before the given position in 'dest'. void Region::cloneInto(Region *dest, Region::iterator destPos, BlockAndValueMapping &mapper) { assert(dest && "expected valid region to clone into"); assert(this != dest && "cannot clone region into itself"); // If the list is empty there is nothing to clone. if (empty()) return; for (Block &block : *this) { Block *newBlock = new Block(); mapper.map(&block, newBlock); // Clone the block arguments. The user might be deleting arguments to the // block by specifying them in the mapper. If so, we don't add the // argument to the cloned block. for (auto arg : block.getArguments()) if (!mapper.contains(arg)) mapper.map(arg, newBlock->addArgument(arg.getType())); // Clone and remap the operations within this block. for (auto &op : block) newBlock->push_back(op.clone(mapper)); dest->getBlocks().insert(destPos, newBlock); } // Now that each of the blocks have been cloned, go through and remap the // operands of each of the operations. auto remapOperands = [&](Operation *op) { for (auto &operand : op->getOpOperands()) if (auto mappedOp = mapper.lookupOrNull(operand.get())) operand.set(mappedOp); for (auto &succOp : op->getBlockOperands()) if (auto *mappedOp = mapper.lookupOrNull(succOp.get())) succOp.set(mappedOp); }; for (iterator it(mapper.lookup(&front())); it != destPos; ++it) it->walk(remapOperands); } /// Returns 'block' if 'block' lies in this region, or otherwise finds the /// ancestor of 'block' that lies in this region. Returns nullptr if the latter /// fails. Block *Region::findAncestorBlockInRegion(Block &block) { auto currBlock = █ while (currBlock->getParent() != this) { Operation *parentOp = currBlock->getParentOp(); if (!parentOp || !parentOp->getBlock()) return nullptr; currBlock = parentOp->getBlock(); } return currBlock; } void Region::dropAllReferences() { for (Block &b : *this) b.dropAllReferences(); } /// Check if there are any values used by operations in `region` defined /// outside its ancestor region `limit`. That is, given `A{B{C{}}}` with region /// `C` and limit `B`, the values defined in `B` can be used but the values /// defined in `A` cannot. Emit errors if `noteLoc` is provided; this location /// is used to point to the operation containing the region, the actual error is /// reported at the operation with an offending use. static bool isIsolatedAbove(Region ®ion, Region &limit, Optional noteLoc) { assert(limit.isAncestor(®ion) && "expected isolation limit to be an ancestor of the given region"); // List of regions to analyze. Each region is processed independently, with // respect to the common `limit` region, so we can look at them in any order. // Therefore, use a simple vector and push/pop back the current region. SmallVector pendingRegions; pendingRegions.push_back(®ion); // Traverse all operations in the region. while (!pendingRegions.empty()) { for (Operation &op : pendingRegions.pop_back_val()->getOps()) { for (Value operand : op.getOperands()) { // operand should be non-null here if the IR is well-formed. But // we don't assert here as this function is called from the verifier // and so could be called on invalid IR. if (!operand) { if (noteLoc) op.emitOpError("block's operand not defined").attachNote(noteLoc); return false; } // Check that any value that is used by an operation is defined in the // same region as either an operation result or a block argument. if (operand.getParentRegion()->isProperAncestor(&limit)) { if (noteLoc) { op.emitOpError("using value defined outside the region") .attachNote(noteLoc) << "required by region isolation constraints"; } return false; } } // Schedule any regions the operations contain for further checking. pendingRegions.reserve(pendingRegions.size() + op.getNumRegions()); for (Region &subRegion : op.getRegions()) pendingRegions.push_back(&subRegion); } } return true; } bool Region::isIsolatedFromAbove(Optional noteLoc) { return isIsolatedAbove(*this, *this, noteLoc); } Region *llvm::ilist_traits<::mlir::Block>::getParentRegion() { size_t Offset( size_t(&((Region *)nullptr->*Region::getSublistAccess(nullptr)))); iplist *Anchor(static_cast *>(this)); return reinterpret_cast(reinterpret_cast(Anchor) - Offset); } /// This is a trait method invoked when a basic block is added to a region. /// We keep the region pointer up to date. void llvm::ilist_traits<::mlir::Block>::addNodeToList(Block *block) { assert(!block->getParent() && "already in a region!"); block->parentValidOpOrderPair.setPointer(getParentRegion()); } /// This is a trait method invoked when an operation is removed from a /// region. We keep the region pointer up to date. void llvm::ilist_traits<::mlir::Block>::removeNodeFromList(Block *block) { assert(block->getParent() && "not already in a region!"); block->parentValidOpOrderPair.setPointer(nullptr); } /// This is a trait method invoked when an operation is moved from one block /// to another. We keep the block pointer up to date. void llvm::ilist_traits<::mlir::Block>::transferNodesFromList( ilist_traits &otherList, block_iterator first, block_iterator last) { // If we are transferring operations within the same function, the parent // pointer doesn't need to be updated. auto *curParent = getParentRegion(); if (curParent == otherList.getParentRegion()) return; // Update the 'parent' member of each Block. for (; first != last; ++first) first->parentValidOpOrderPair.setPointer(curParent); } //===----------------------------------------------------------------------===// // Region::OpIterator //===----------------------------------------------------------------------===// Region::OpIterator::OpIterator(Region *region, bool end) : region(region), block(end ? region->end() : region->begin()) { if (!region->empty()) skipOverBlocksWithNoOps(); } Region::OpIterator &Region::OpIterator::operator++() { // We increment over operations, if we reach the last use then move to next // block. if (operation != block->end()) ++operation; if (operation == block->end()) { ++block; skipOverBlocksWithNoOps(); } return *this; } void Region::OpIterator::skipOverBlocksWithNoOps() { while (block != region->end() && block->empty()) ++block; // If we are at the last block, then set the operation to first operation of // next block (sentinel value used for end). if (block == region->end()) operation = {}; else operation = block->begin(); } //===----------------------------------------------------------------------===// // RegionRange //===----------------------------------------------------------------------===// RegionRange::RegionRange(MutableArrayRef regions) : RegionRange(regions.data(), regions.size()) {} RegionRange::RegionRange(ArrayRef> regions) : RegionRange(regions.data(), regions.size()) {} /// See `llvm::detail::indexed_accessor_range_base` for details. RegionRange::OwnerT RegionRange::offset_base(const OwnerT &owner, ptrdiff_t index) { if (auto *operand = owner.dyn_cast *>()) return operand + index; return &owner.get()[index]; } /// See `llvm::detail::indexed_accessor_range_base` for details. Region *RegionRange::dereference_iterator(const OwnerT &owner, ptrdiff_t index) { if (auto *operand = owner.dyn_cast *>()) return operand[index].get(); return &owner.get()[index]; }