1 //===- BlockSupport.h -------------------------------------------*- C++ -*-===//
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 // This file defines a number of support types for the Block class.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef MLIR_IR_BLOCKSUPPORT_H
14 #define MLIR_IR_BLOCKSUPPORT_H
15 
16 #include "mlir/IR/Value.h"
17 #include "llvm/ADT/PointerUnion.h"
18 #include "llvm/ADT/ilist.h"
19 #include "llvm/ADT/ilist_node.h"
20 
21 namespace mlir {
22 class Block;
23 
24 //===----------------------------------------------------------------------===//
25 // BlockOperand
26 //===----------------------------------------------------------------------===//
27 
28 /// A block operand represents an operand that holds a reference to a Block,
29 /// e.g. for terminator operations.
30 class BlockOperand : public IROperand<BlockOperand, Block *> {
31 public:
32   using IROperand<BlockOperand, Block *>::IROperand;
33 
34   /// Provide the use list that is attached to the given block.
35   static IRObjectWithUseList<BlockOperand> *getUseList(Block *value);
36 
37   /// Return which operand this is in the BlockOperand list of the Operation.
38   unsigned getOperandNumber();
39 };
40 
41 //===----------------------------------------------------------------------===//
42 // Predecessors
43 //===----------------------------------------------------------------------===//
44 
45 /// Implement a predecessor iterator for blocks. This works by walking the use
46 /// lists of the blocks. The entries on this list are the BlockOperands that
47 /// are embedded into terminator operations. From the operand, we can get the
48 /// terminator that contains it, and its parent block is the predecessor.
49 class PredecessorIterator final
50     : public llvm::mapped_iterator<ValueUseIterator<BlockOperand>,
51                                    Block *(*)(BlockOperand &)> {
52   static Block *unwrap(BlockOperand &value);
53 
54 public:
55   /// Initializes the operand type iterator to the specified operand iterator.
PredecessorIterator(ValueUseIterator<BlockOperand> it)56   PredecessorIterator(ValueUseIterator<BlockOperand> it)
57       : llvm::mapped_iterator<ValueUseIterator<BlockOperand>,
58                               Block *(*)(BlockOperand &)>(it, &unwrap) {}
PredecessorIterator(BlockOperand * operand)59   explicit PredecessorIterator(BlockOperand *operand)
60       : PredecessorIterator(ValueUseIterator<BlockOperand>(operand)) {}
61 
62   /// Get the successor number in the predecessor terminator.
63   unsigned getSuccessorIndex() const;
64 };
65 
66 //===----------------------------------------------------------------------===//
67 // Successors
68 //===----------------------------------------------------------------------===//
69 
70 /// This class implements the successor iterators for Block.
71 class SuccessorRange final
72     : public llvm::detail::indexed_accessor_range_base<
73           SuccessorRange, BlockOperand *, Block *, Block *, Block *> {
74 public:
75   using RangeBaseT::RangeBaseT;
76   SuccessorRange();
77   SuccessorRange(Block *block);
78   SuccessorRange(Operation *term);
79 
80 private:
81   /// See `llvm::detail::indexed_accessor_range_base` for details.
offset_base(BlockOperand * object,ptrdiff_t index)82   static BlockOperand *offset_base(BlockOperand *object, ptrdiff_t index) {
83     return object + index;
84   }
85   /// See `llvm::detail::indexed_accessor_range_base` for details.
dereference_iterator(BlockOperand * object,ptrdiff_t index)86   static Block *dereference_iterator(BlockOperand *object, ptrdiff_t index) {
87     return object[index].get();
88   }
89 
90   /// Allow access to `offset_base` and `dereference_iterator`.
91   friend RangeBaseT;
92 };
93 
94 //===----------------------------------------------------------------------===//
95 // BlockRange
96 //===----------------------------------------------------------------------===//
97 
98 /// This class provides an abstraction over the different types of ranges over
99 /// Blocks. In many cases, this prevents the need to explicitly materialize a
100 /// SmallVector/std::vector. This class should be used in places that are not
101 /// suitable for a more derived type (e.g. ArrayRef) or a template range
102 /// parameter.
103 class BlockRange final
104     : public llvm::detail::indexed_accessor_range_base<
105           BlockRange, llvm::PointerUnion<BlockOperand *, Block *const *>,
106           Block *, Block *, Block *> {
107 public:
108   using RangeBaseT::RangeBaseT;
109   BlockRange(ArrayRef<Block *> blocks = llvm::None);
110   BlockRange(SuccessorRange successors);
111   template <typename Arg,
112             typename = typename std::enable_if_t<
113                 std::is_constructible<ArrayRef<Block *>, Arg>::value>>
BlockRange(Arg && arg)114   BlockRange(Arg &&arg)
115       : BlockRange(ArrayRef<Block *>(std::forward<Arg>(arg))) {}
BlockRange(std::initializer_list<Block * > blocks)116   BlockRange(std::initializer_list<Block *> blocks)
117       : BlockRange(ArrayRef<Block *>(blocks)) {}
118 
119 private:
120   /// The owner of the range is either:
121   /// * A pointer to the first element of an array of block operands.
122   /// * A pointer to the first element of an array of Block *.
123   using OwnerT = llvm::PointerUnion<BlockOperand *, Block *const *>;
124 
125   /// See `llvm::detail::indexed_accessor_range_base` for details.
126   static OwnerT offset_base(OwnerT object, ptrdiff_t index);
127 
128   /// See `llvm::detail::indexed_accessor_range_base` for details.
129   static Block *dereference_iterator(OwnerT object, ptrdiff_t index);
130 
131   /// Allow access to `offset_base` and `dereference_iterator`.
132   friend RangeBaseT;
133 };
134 
135 //===----------------------------------------------------------------------===//
136 // Operation Iterators
137 //===----------------------------------------------------------------------===//
138 
139 namespace detail {
140 /// A utility iterator that filters out operations that are not 'OpT'.
141 template <typename OpT, typename IteratorT>
142 class op_filter_iterator
143     : public llvm::filter_iterator<IteratorT, bool (*)(Operation &)> {
filter(Operation & op)144   static bool filter(Operation &op) { return llvm::isa<OpT>(op); }
145 
146 public:
op_filter_iterator(IteratorT it,IteratorT end)147   op_filter_iterator(IteratorT it, IteratorT end)
148       : llvm::filter_iterator<IteratorT, bool (*)(Operation &)>(it, end,
149                                                                 &filter) {}
150 
151   /// Allow implicit conversion to the underlying iterator.
152   operator const IteratorT &() const { return this->wrapped(); }
153 };
154 
155 /// This class provides iteration over the held operations of a block for a
156 /// specific operation type.
157 template <typename OpT, typename IteratorT>
158 class op_iterator
159     : public llvm::mapped_iterator<op_filter_iterator<OpT, IteratorT>,
160                                    OpT (*)(Operation &)> {
unwrap(Operation & op)161   static OpT unwrap(Operation &op) { return cast<OpT>(op); }
162 
163 public:
164   /// Initializes the iterator to the specified filter iterator.
op_iterator(op_filter_iterator<OpT,IteratorT> it)165   op_iterator(op_filter_iterator<OpT, IteratorT> it)
166       : llvm::mapped_iterator<op_filter_iterator<OpT, IteratorT>,
167                               OpT (*)(Operation &)>(it, &unwrap) {}
168 
169   /// Allow implicit conversion to the underlying block iterator.
170   operator const IteratorT &() const { return this->wrapped(); }
171 };
172 } // namespace detail
173 } // namespace mlir
174 
175 namespace llvm {
176 
177 /// Provide support for hashing successor ranges.
178 template <>
179 struct DenseMapInfo<mlir::SuccessorRange> {
180   static mlir::SuccessorRange getEmptyKey() {
181     auto *pointer = llvm::DenseMapInfo<mlir::BlockOperand *>::getEmptyKey();
182     return mlir::SuccessorRange(pointer, 0);
183   }
184   static mlir::SuccessorRange getTombstoneKey() {
185     auto *pointer = llvm::DenseMapInfo<mlir::BlockOperand *>::getTombstoneKey();
186     return mlir::SuccessorRange(pointer, 0);
187   }
188   static unsigned getHashValue(mlir::SuccessorRange value) {
189     return llvm::hash_combine_range(value.begin(), value.end());
190   }
191   static bool isEqual(mlir::SuccessorRange lhs, mlir::SuccessorRange rhs) {
192     if (rhs.getBase() == getEmptyKey().getBase())
193       return lhs.getBase() == getEmptyKey().getBase();
194     if (rhs.getBase() == getTombstoneKey().getBase())
195       return lhs.getBase() == getTombstoneKey().getBase();
196     return lhs == rhs;
197   }
198 };
199 
200 //===----------------------------------------------------------------------===//
201 // ilist_traits for Operation
202 //===----------------------------------------------------------------------===//
203 
204 namespace ilist_detail {
205 // Explicitly define the node access for the operation list so that we can
206 // break the dependence on the Operation class in this header. This allows for
207 // operations to have trailing Regions without a circular include
208 // dependence.
209 template <>
210 struct SpecificNodeAccess<
211     typename compute_node_options<::mlir::Operation>::type> : NodeAccess {
212 protected:
213   using OptionsT = typename compute_node_options<mlir::Operation>::type;
214   using pointer = typename OptionsT::pointer;
215   using const_pointer = typename OptionsT::const_pointer;
216   using node_type = ilist_node_impl<OptionsT>;
217 
218   static node_type *getNodePtr(pointer N);
219   static const node_type *getNodePtr(const_pointer N);
220 
221   static pointer getValuePtr(node_type *N);
222   static const_pointer getValuePtr(const node_type *N);
223 };
224 } // namespace ilist_detail
225 
226 template <>
227 struct ilist_traits<::mlir::Operation> {
228   using Operation = ::mlir::Operation;
229   using op_iterator = simple_ilist<Operation>::iterator;
230 
231   static void deleteNode(Operation *op);
232   void addNodeToList(Operation *op);
233   void removeNodeFromList(Operation *op);
234   void transferNodesFromList(ilist_traits<Operation> &otherList,
235                              op_iterator first, op_iterator last);
236 
237 private:
238   mlir::Block *getContainingBlock();
239 };
240 
241 //===----------------------------------------------------------------------===//
242 // ilist_traits for Block
243 //===----------------------------------------------------------------------===//
244 
245 template <>
246 struct ilist_traits<::mlir::Block> : public ilist_alloc_traits<::mlir::Block> {
247   using Block = ::mlir::Block;
248   using block_iterator = simple_ilist<::mlir::Block>::iterator;
249 
250   void addNodeToList(Block *block);
251   void removeNodeFromList(Block *block);
252   void transferNodesFromList(ilist_traits<Block> &otherList,
253                              block_iterator first, block_iterator last);
254 
255 private:
256   mlir::Region *getParentRegion();
257 };
258 
259 } // namespace llvm
260 
261 #endif // MLIR_IR_BLOCKSUPPORT_H
262