blob: c14e9aad8f6d1e2e10b6cf14a4baa5821d94f021 [file] [log] [blame]
//===- Block.h - MLIR Block 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 Block class.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_IR_BLOCK_H
#define MLIR_IR_BLOCK_H
#include "mlir/IR/BlockSupport.h"
#include "mlir/IR/Visitors.h"
namespace llvm {
class BitVector;
} // namespace llvm
namespace mlir {
class TypeRange;
template <typename ValueRangeT>
class ValueTypeRange;
/// `Block` represents an ordered list of `Operation`s.
class Block : public IRObjectWithUseList<BlockOperand>,
public llvm::ilist_node_with_parent<Block, Region> {
public:
explicit Block() = default;
~Block();
void clear() {
// Drop all references from within this block.
dropAllReferences();
// Clear operations in the reverse order so that uses are destroyed
// before their defs.
while (!empty())
operations.pop_back();
}
/// Provide a 'getParent' method for ilist_node_with_parent methods.
/// We mark it as a const function because ilist_node_with_parent specifically
/// requires a 'getParent() const' method. Once ilist_node removes this
/// constraint, we should drop the const to fit the rest of the MLIR const
/// model.
Region *getParent() const;
/// Returns the closest surrounding operation that contains this block.
Operation *getParentOp();
/// Return if this block is the entry block in the parent region.
bool isEntryBlock();
/// Insert this block (which must not already be in a region) right before
/// the specified block.
void insertBefore(Block *block);
/// Insert this block (which must not already be in a region) right after
/// the specified block.
void insertAfter(Block *block);
/// Unlink this block from its current region and insert it right before the
/// specific block.
void moveBefore(Block *block);
/// Unlink this block from its current region and insert it right before the
/// block that the given iterator points to in the region region.
void moveBefore(Region *region, llvm::iplist<Block>::iterator iterator);
/// Unlink this Block from its parent region and delete it.
void erase();
//===--------------------------------------------------------------------===//
// Block argument management
//===--------------------------------------------------------------------===//
// This is the list of arguments to the block.
using BlockArgListType = MutableArrayRef<BlockArgument>;
BlockArgListType getArguments() { return arguments; }
/// Return a range containing the types of the arguments for this block.
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 arguments.empty(); }
/// Add one value to the argument list.
BlockArgument addArgument(Type type, Location loc);
/// 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, Location loc);
/// Add one argument to the argument list for each type specified in the list.
/// `locs` is required to have the same number of elements as `types`.
iterator_range<args_iterator> addArguments(TypeRange types,
ArrayRef<Location> locs);
/// Add one value to the argument list at the specified position.
BlockArgument insertArgument(unsigned index, Type type, Location loc);
/// Erase the argument at 'index' and remove it from the argument list.
void eraseArgument(unsigned index);
/// Erases 'num' arguments from the index 'start'.
void eraseArguments(unsigned start, unsigned num);
/// Erases the arguments that have their corresponding bit set in
/// `eraseIndices` and removes them from the argument list.
void eraseArguments(const BitVector &eraseIndices);
/// Erases arguments using the given predicate. If the predicate returns true,
/// that argument is erased.
void eraseArguments(function_ref<bool(BlockArgument)> shouldEraseFn);
unsigned getNumArguments() { return arguments.size(); }
BlockArgument getArgument(unsigned i) { return arguments[i]; }
//===--------------------------------------------------------------------===//
// Operation list management
//===--------------------------------------------------------------------===//
/// This is the list of operations in the block.
using OpListType = llvm::iplist<Operation>;
OpListType &getOperations() { return operations; }
// Iteration over the operations in the block.
using iterator = OpListType::iterator;
using reverse_iterator = OpListType::reverse_iterator;
iterator begin() { return operations.begin(); }
iterator end() { return operations.end(); }
reverse_iterator rbegin() { return operations.rbegin(); }
reverse_iterator rend() { return operations.rend(); }
bool empty() { return operations.empty(); }
void push_back(Operation *op) { operations.push_back(op); }
void push_front(Operation *op) { operations.push_front(op); }
Operation &back() { return operations.back(); }
Operation &front() { return operations.front(); }
/// Returns 'op' if 'op' lies in this block, or otherwise finds the
/// ancestor operation of 'op' that lies in this block. Returns nullptr if
/// the latter fails.
/// TODO: This is very specific functionality that should live somewhere else,
/// probably in Dominance.cpp.
Operation *findAncestorOpInBlock(Operation &op);
/// This drops all operand uses from operations within this block, which is
/// an essential step in breaking cyclic dependences between references when
/// they are to be deleted.
void dropAllReferences();
/// This drops all uses of values defined in this block or in the blocks of
/// nested regions wherever the uses are located.
void dropAllDefinedValueUses();
/// Returns true if the ordering of the child operations is valid, false
/// otherwise.
bool isOpOrderValid();
/// Invalidates the current ordering of operations.
void invalidateOpOrder();
/// Verifies the current ordering of child operations matches the
/// validOpOrder flag. Returns false if the order is valid, true otherwise.
bool verifyOpOrder();
/// Recomputes the ordering of child operations within the block.
void recomputeOpOrder();
/// This class provides iteration over the held operations of a block for a
/// specific operation type.
template <typename OpT>
using op_iterator = detail::op_iterator<OpT, iterator>;
/// Return an iterator range over the operations within this block that are of
/// 'OpT'.
template <typename OpT>
iterator_range<op_iterator<OpT>> getOps() {
auto endIt = end();
return {detail::op_filter_iterator<OpT, iterator>(begin(), endIt),
detail::op_filter_iterator<OpT, iterator>(endIt, endIt)};
}
template <typename OpT>
op_iterator<OpT> op_begin() {
return detail::op_filter_iterator<OpT, iterator>(begin(), end());
}
template <typename OpT>
op_iterator<OpT> op_end() {
return detail::op_filter_iterator<OpT, iterator>(end(), end());
}
/// Return an iterator range over the operation within this block excluding
/// the terminator operation at the end.
iterator_range<iterator> without_terminator() {
if (begin() == end())
return {begin(), end()};
auto endIt = --end();
return {begin(), endIt};
}
//===--------------------------------------------------------------------===//
// Terminator management
//===--------------------------------------------------------------------===//
/// Get the terminator operation of this block. This function asserts that
/// the block might have a valid terminator operation.
Operation *getTerminator();
/// Check whether this block might have a terminator.
bool mightHaveTerminator();
//===--------------------------------------------------------------------===//
// Predecessors and successors.
//===--------------------------------------------------------------------===//
// Predecessor iteration.
using pred_iterator = PredecessorIterator;
pred_iterator pred_begin() {
return pred_iterator((BlockOperand *)getFirstUse());
}
pred_iterator pred_end() { return pred_iterator(nullptr); }
iterator_range<pred_iterator> getPredecessors() {
return {pred_begin(), pred_end()};
}
/// Return true if this block has no predecessors.
bool hasNoPredecessors() { return pred_begin() == pred_end(); }
/// Returns true if this blocks has no successors.
bool hasNoSuccessors() { return succ_begin() == succ_end(); }
/// If this block has exactly one predecessor, return it. Otherwise, return
/// null.
///
/// Note that if a block has duplicate predecessors from a single block (e.g.
/// if you have a conditional branch with the same block as the true/false
/// destinations) is not considered to be a single predecessor.
Block *getSinglePredecessor();
/// If this block has a unique predecessor, i.e., all incoming edges originate
/// from one block, return it. Otherwise, return null.
Block *getUniquePredecessor();
// Indexed successor access.
unsigned getNumSuccessors();
Block *getSuccessor(unsigned i);
// Successor iteration.
using succ_iterator = SuccessorRange::iterator;
succ_iterator succ_begin() { return getSuccessors().begin(); }
succ_iterator succ_end() { return getSuccessors().end(); }
SuccessorRange getSuccessors() { return SuccessorRange(this); }
//===--------------------------------------------------------------------===//
// Walkers
//===--------------------------------------------------------------------===//
/// Walk all nested operations, blocks (including this block) or regions,
/// depending on the type of callback.
///
/// The order in which operations, blocks or regions at the same nesting
/// level are visited (e.g., lexicographical or reverse lexicographical order)
/// is determined by `Iterator`. The walk order for enclosing operations,
/// blocks or regions with respect to their nested ones is specified by
/// `Order` (post-order by default).
///
/// A callback on a operation or block is allowed to erase that operation or
/// block 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 Iterator = ForwardIterator, typename FnT,
typename ArgT = detail::first_argument<FnT>,
typename RetT = detail::walkResultType<FnT>>
RetT walk(FnT &&callback) {
if constexpr (std::is_same<ArgT, Block *>::value &&
Order == WalkOrder::PreOrder) {
// Pre-order walk on blocks: invoke the callback on this block.
if constexpr (std::is_same<RetT, void>::value) {
callback(this);
} else {
RetT result = callback(this);
if (result.wasSkipped())
return WalkResult::advance();
if (result.wasInterrupted())
return WalkResult::interrupt();
}
}
// Walk nested operations, blocks or regions.
if constexpr (std::is_same<RetT, void>::value) {
walk<Order, Iterator>(begin(), end(), std::forward<FnT>(callback));
} else {
if (walk<Order, Iterator>(begin(), end(), std::forward<FnT>(callback))
.wasInterrupted())
return WalkResult::interrupt();
}
if constexpr (std::is_same<ArgT, Block *>::value &&
Order == WalkOrder::PostOrder) {
// Post-order walk on blocks: invoke the callback on this block.
return callback(this);
}
if constexpr (!std::is_same<RetT, void>::value)
return WalkResult::advance();
}
/// Walk all nested operations, blocks (excluding this block) or regions,
/// depending on the type of callback, in the specified [begin, end) range of
/// this block.
///
/// The order in which operations, blocks or regions at the same nesting
/// level are visited (e.g., lexicographical or reverse lexicographical order)
/// is determined by `Iterator`. The walk order for enclosing operations,
/// blocks or regions with respect to their nested ones is specified by
/// `Order` (post-order by default).
///
/// A callback on a operation or block is allowed to erase that operation or
/// block 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 Iterator = ForwardIterator, typename FnT,
typename RetT = detail::walkResultType<FnT>>
RetT walk(Block::iterator begin, Block::iterator end, FnT &&callback) {
for (auto &op : llvm::make_early_inc_range(llvm::make_range(begin, end))) {
if constexpr (std::is_same<RetT, WalkResult>::value) {
if (detail::walk<Order, Iterator>(&op, callback).wasInterrupted())
return WalkResult::interrupt();
} else {
detail::walk<Order, Iterator>(&op, callback);
}
}
if constexpr (std::is_same<RetT, WalkResult>::value)
return WalkResult::advance();
}
//===--------------------------------------------------------------------===//
// Other
//===--------------------------------------------------------------------===//
/// Split the block into two blocks before the specified operation or
/// iterator.
///
/// Note that all operations BEFORE the specified iterator stay as part of
/// the original basic block, and the rest of the operations in the original
/// block are moved to the new block, including the old terminator. The
/// original block is left without a terminator.
///
/// The newly formed Block is returned, and the specified iterator is
/// invalidated.
Block *splitBlock(iterator splitBefore);
Block *splitBlock(Operation *splitBeforeOp) {
return splitBlock(iterator(splitBeforeOp));
}
/// Returns pointer to member of operation list.
static OpListType Block::*getSublistAccess(Operation *) {
return &Block::operations;
}
void print(raw_ostream &os);
void print(raw_ostream &os, AsmState &state);
void dump();
/// Print out the name of the block without printing its body.
/// NOTE: The printType argument is ignored. We keep it for compatibility
/// with LLVM dominator machinery that expects it to exist.
void printAsOperand(raw_ostream &os, bool printType = true);
void printAsOperand(raw_ostream &os, AsmState &state);
private:
/// Pair of the parent object that owns this block and a bit that signifies if
/// the operations within this block have a valid ordering.
llvm::PointerIntPair<Region *, /*IntBits=*/1, bool> parentValidOpOrderPair;
/// This is the list of operations in the block.
OpListType operations;
/// This is the list of arguments to the block.
std::vector<BlockArgument> arguments;
Block(Block &) = delete;
void operator=(Block &) = delete;
friend struct llvm::ilist_traits<Block>;
};
} // namespace mlir
#endif // MLIR_IR_BLOCK_H