blob: 4558ce436a9d1a12f3edc9d206f5a9d9725d4d19 [file] [log] [blame]
//===- Region.h - MLIR Region Class -----------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file defines the Region class.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_IR_REGION_H
#define MLIR_IR_REGION_H
#include "mlir/IR/Block.h"
namespace mlir {
class TypeRange;
template <typename ValueRangeT>
class ValueTypeRange;
class BlockAndValueMapping;
/// This class contains a list of basic blocks and a link to the parent
/// operation it is attached to.
class Region {
public:
Region() = default;
explicit Region(Operation *container);
~Region();
/// Return the context this region is inserted in. The region must have a
/// valid parent container.
MLIRContext *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 getLoc();
//===--------------------------------------------------------------------===//
// Block list management
//===--------------------------------------------------------------------===//
using BlockListType = llvm::iplist<Block>;
BlockListType &getBlocks() { return blocks; }
Block &emplaceBlock() {
push_back(new Block);
return back();
}
// Iteration over the blocks in the region.
using iterator = BlockListType::iterator;
using reverse_iterator = BlockListType::reverse_iterator;
iterator begin() { return blocks.begin(); }
iterator end() { return blocks.end(); }
reverse_iterator rbegin() { return blocks.rbegin(); }
reverse_iterator rend() { return blocks.rend(); }
bool empty() { return blocks.empty(); }
void push_back(Block *block) { blocks.push_back(block); }
void push_front(Block *block) { blocks.push_front(block); }
Block &back() { return blocks.back(); }
Block &front() { return blocks.front(); }
/// Return true if this region has exactly one block.
bool hasOneBlock() { return !empty() && std::next(begin()) == end(); }
/// getSublistAccess() - Returns pointer to member of region.
static BlockListType Region::*getSublistAccess(Block *) {
return &Region::blocks;
}
//===--------------------------------------------------------------------===//
// Argument Handling
//===--------------------------------------------------------------------===//
// This is the list of arguments to the block.
using BlockArgListType = MutableArrayRef<BlockArgument>;
BlockArgListType getArguments() {
return empty() ? BlockArgListType() : front().getArguments();
}
ValueTypeRange<BlockArgListType> getArgumentTypes();
using args_iterator = BlockArgListType::iterator;
using reverse_args_iterator = BlockArgListType::reverse_iterator;
args_iterator args_begin() { return getArguments().begin(); }
args_iterator args_end() { return getArguments().end(); }
reverse_args_iterator args_rbegin() { return getArguments().rbegin(); }
reverse_args_iterator args_rend() { return getArguments().rend(); }
bool args_empty() { return getArguments().empty(); }
/// Add one value to the argument list.
BlockArgument addArgument(Type type) { return front().addArgument(type); }
/// Insert one value to the position in the argument list indicated by the
/// given iterator. The existing arguments are shifted. The block is expected
/// not to have predecessors.
BlockArgument insertArgument(args_iterator it, Type type) {
return front().insertArgument(it, type);
}
/// Add one argument to the argument list for each type specified in the list.
iterator_range<args_iterator> addArguments(TypeRange types);
/// Add one value to the argument list at the specified position.
BlockArgument insertArgument(unsigned index, Type type) {
return front().insertArgument(index, type);
}
/// Erase the argument at 'index' and remove it from the argument list.
void eraseArgument(unsigned index) { front().eraseArgument(index); }
unsigned getNumArguments() { return getArguments().size(); }
BlockArgument getArgument(unsigned i) { return getArguments()[i]; }
//===--------------------------------------------------------------------===//
// Operation list utilities
//===--------------------------------------------------------------------===//
/// This class provides iteration over the held operations of blocks directly
/// within a region.
class OpIterator final
: public llvm::iterator_facade_base<OpIterator, std::forward_iterator_tag,
Operation> {
public:
/// Initialize OpIterator for a region, specify `end` to return the iterator
/// to last operation.
explicit OpIterator(Region *region, bool end = false);
using llvm::iterator_facade_base<OpIterator, std::forward_iterator_tag,
Operation>::operator++;
OpIterator &operator++();
Operation *operator->() const { return &*operation; }
Operation &operator*() const { return *operation; }
/// Compare this iterator with another.
bool operator==(const OpIterator &rhs) const {
return operation == rhs.operation;
}
bool operator!=(const OpIterator &rhs) const { return !(*this == rhs); }
private:
void skipOverBlocksWithNoOps();
/// The region whose operations are being iterated over.
Region *region;
/// The block of 'region' whose operations are being iterated over.
Region::iterator block;
/// The current operation within 'block'.
Block::iterator operation;
};
/// This class provides iteration over the held operations of a region for a
/// specific operation type.
template <typename OpT>
using op_iterator = detail::op_iterator<OpT, OpIterator>;
/// Return iterators that walk the operations nested directly within this
/// region.
OpIterator op_begin() { return OpIterator(this); }
OpIterator op_end() { return OpIterator(this, /*end=*/true); }
iterator_range<OpIterator> getOps() { return {op_begin(), op_end()}; }
/// Return iterators that walk operations of type 'T' nested directly within
/// this region.
template <typename OpT>
op_iterator<OpT> op_begin() {
return detail::op_filter_iterator<OpT, OpIterator>(op_begin(), op_end());
}
template <typename OpT>
op_iterator<OpT> op_end() {
return detail::op_filter_iterator<OpT, OpIterator>(op_end(), op_end());
}
template <typename OpT>
iterator_range<op_iterator<OpT>> getOps() {
auto endIt = op_end();
return {detail::op_filter_iterator<OpT, OpIterator>(op_begin(), endIt),
detail::op_filter_iterator<OpT, OpIterator>(endIt, endIt)};
}
//===--------------------------------------------------------------------===//
// Misc. utilities
//===--------------------------------------------------------------------===//
/// Return the region containing this region or nullptr if the region is
/// attached to a top-level operation.
Region *getParentRegion();
/// Return the parent operation this region is attached to.
Operation *getParentOp() { return container; }
/// Find the first parent operation of the given type, or nullptr if there is
/// no ancestor operation.
template <typename ParentT>
ParentT getParentOfType() {
auto *region = this;
do {
if (auto parent = dyn_cast_or_null<ParentT>(region->container))
return parent;
} while ((region = region->getParentRegion()));
return ParentT();
}
/// Return the number of this region in the parent operation.
unsigned getRegionNumber();
/// Return true if this region is a proper ancestor of the `other` region.
bool isProperAncestor(Region *other);
/// Return true if this region is ancestor of the `other` region. A region
/// is considered as its own ancestor, use `isProperAncestor` to avoid this.
bool isAncestor(Region *other) {
return this == other || isProperAncestor(other);
}
/// Clone the internal blocks from this region into dest. Any
/// cloned blocks are appended to the back of dest. If the mapper
/// contains entries for block arguments, these arguments are not included
/// in the respective cloned block.
void cloneInto(Region *dest, BlockAndValueMapping &mapper);
/// Clone this region into 'dest' before the given position in 'dest'.
void cloneInto(Region *dest, Region::iterator destPos,
BlockAndValueMapping &mapper);
/// Takes body of another region (that region will have no body after this
/// operation completes). The current body of this region is cleared.
void takeBody(Region &other) {
blocks.clear();
blocks.splice(blocks.end(), other.getBlocks());
}
/// 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 *findAncestorBlockInRegion(Block &block);
/// Returns 'op' if 'op' lies in this region, or otherwise finds the
/// ancestor of 'op' that lies in this region. Returns nullptr if the
/// latter fails.
Operation *findAncestorOpInRegion(Operation &op);
/// Drop all operand uses from operations within this region, which is
/// an essential step in breaking cyclic dependences between references when
/// they are to be deleted.
void dropAllReferences();
//===--------------------------------------------------------------------===//
// Operation Walkers
//===--------------------------------------------------------------------===//
/// Walk the operations in this region. The callback method is called for each
/// nested region, block or operation, depending on the callback provided.
/// Regions, blocks and operations at the same nesting level are visited in
/// lexicographical order. The walk order for enclosing regions, blocks and
/// operations with respect to their nested ones is specified by 'Order'
/// (post-order by default). This method is invoked for void-returning
/// callbacks. A callback on a block or operation is allowed to erase that
/// block or operation only if the walk is in post-order. See non-void method
/// for pre-order erasure. See Operation::walk for more details.
template <WalkOrder Order = WalkOrder::PostOrder, typename FnT,
typename RetT = detail::walkResultType<FnT>>
typename std::enable_if<std::is_same<RetT, void>::value, RetT>::type
walk(FnT &&callback) {
for (auto &block : *this)
block.walk<Order>(callback);
}
/// Walk the operations in this region. The callback method is called for each
/// nested region, block or operation, depending on the callback provided.
/// Regions, blocks and operations at the same nesting level are visited in
/// lexicographical order. The walk order for enclosing regions, blocks and
/// operations with respect to their nested ones is specified by 'Order'
/// (post-order by default). This method is invoked for skippable or
/// interruptible callbacks. A callback on a block or operation is allowed to
/// erase that block or operation if either:
/// * the walk is in post-order,
/// * or the walk is in pre-order and the walk is skipped after the erasure.
/// See Operation::walk for more details.
template <WalkOrder Order = WalkOrder::PostOrder, typename FnT,
typename RetT = detail::walkResultType<FnT>>
typename std::enable_if<std::is_same<RetT, WalkResult>::value, RetT>::type
walk(FnT &&callback) {
for (auto &block : *this)
if (block.walk<Order>(callback).wasInterrupted())
return WalkResult::interrupt();
return WalkResult::advance();
}
//===--------------------------------------------------------------------===//
// CFG view utilities
//===--------------------------------------------------------------------===//
/// Displays the CFG in a window. This is for use from the debugger and
/// depends on Graphviz to generate the graph.
/// This function is defined in ViewOpGraph.cpp and only works with that
/// target linked.
void viewGraph(const Twine &regionName);
void viewGraph();
private:
BlockListType blocks;
/// This is the object we are part of.
Operation *container = nullptr;
};
/// This class provides an abstraction over the different types of ranges over
/// Regions. In many cases, this prevents the need to explicitly materialize a
/// SmallVector/std::vector. This class should be used in places that are not
/// suitable for a more derived type (e.g. ArrayRef) or a template range
/// parameter.
class RegionRange
: public llvm::detail::indexed_accessor_range_base<
RegionRange, PointerUnion<Region *, const std::unique_ptr<Region> *>,
Region *, Region *, Region *> {
/// The type representing the owner of this range. This is either a list of
/// values, operands, or results.
using OwnerT = PointerUnion<Region *, const std::unique_ptr<Region> *>;
public:
using RangeBaseT::RangeBaseT;
RegionRange(MutableArrayRef<Region> regions = llvm::None);
template <typename Arg,
typename = typename std::enable_if_t<std::is_constructible<
ArrayRef<std::unique_ptr<Region>>, Arg>::value>>
RegionRange(Arg &&arg)
: RegionRange(ArrayRef<std::unique_ptr<Region>>(std::forward<Arg>(arg))) {
}
RegionRange(ArrayRef<std::unique_ptr<Region>> regions);
private:
/// See `llvm::detail::indexed_accessor_range_base` for details.
static OwnerT offset_base(const OwnerT &owner, ptrdiff_t index);
/// See `llvm::detail::indexed_accessor_range_base` for details.
static Region *dereference_iterator(const OwnerT &owner, ptrdiff_t index);
/// Allow access to `offset_base` and `dereference_iterator`.
friend RangeBaseT;
};
} // end namespace mlir
#endif // MLIR_IR_REGION_H