xref: /llvm-project/mlir/include/mlir/IR/Builders.h (revision f4943464d769e2eacd5c54dfaaf0468788abeb84)
1 //===- Builders.h - Helpers for constructing MLIR Classes -------*- 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 #ifndef MLIR_IR_BUILDERS_H
10 #define MLIR_IR_BUILDERS_H
11 
12 #include "mlir/IR/OpDefinition.h"
13 #include "llvm/Support/Compiler.h"
14 #include <optional>
15 
16 namespace mlir {
17 
18 class AffineExpr;
19 class IRMapping;
20 class UnknownLoc;
21 class FileLineColLoc;
22 class FileLineColRange;
23 class Type;
24 class PrimitiveType;
25 class IntegerType;
26 class FloatType;
27 class FunctionType;
28 class IndexType;
29 class MemRefType;
30 class VectorType;
31 class RankedTensorType;
32 class UnrankedTensorType;
33 class TupleType;
34 class NoneType;
35 class BoolAttr;
36 class IntegerAttr;
37 class FloatAttr;
38 class StringAttr;
39 class TypeAttr;
40 class ArrayAttr;
41 class SymbolRefAttr;
42 class ElementsAttr;
43 class DenseElementsAttr;
44 class DenseIntElementsAttr;
45 class AffineMapAttr;
46 class AffineMap;
47 class UnitAttr;
48 
49 /// This class is a general helper class for creating context-global objects
50 /// like types, attributes, and affine expressions.
51 class Builder {
52 public:
53   explicit Builder(MLIRContext *context) : context(context) {}
54   explicit Builder(Operation *op) : Builder(op->getContext()) {}
55 
56   MLIRContext *getContext() const { return context; }
57 
58   // Locations.
59   Location getUnknownLoc();
60   Location getFusedLoc(ArrayRef<Location> locs,
61                        Attribute metadata = Attribute());
62 
63   // Types.
64   FloatType getBF16Type();
65   FloatType getF16Type();
66   FloatType getTF32Type();
67   FloatType getF32Type();
68   FloatType getF64Type();
69   FloatType getF80Type();
70   FloatType getF128Type();
71 
72   IndexType getIndexType();
73 
74   IntegerType getI1Type();
75   IntegerType getI2Type();
76   IntegerType getI4Type();
77   IntegerType getI8Type();
78   IntegerType getI16Type();
79   IntegerType getI32Type();
80   IntegerType getI64Type();
81   IntegerType getIntegerType(unsigned width);
82   IntegerType getIntegerType(unsigned width, bool isSigned);
83   FunctionType getFunctionType(TypeRange inputs, TypeRange results);
84   TupleType getTupleType(TypeRange elementTypes);
85   NoneType getNoneType();
86 
87   /// Get or construct an instance of the type `Ty` with provided arguments.
88   template <typename Ty, typename... Args>
89   Ty getType(Args &&...args) {
90     return Ty::get(context, std::forward<Args>(args)...);
91   }
92 
93   /// Get or construct an instance of the attribute `Attr` with provided
94   /// arguments.
95   template <typename Attr, typename... Args>
96   Attr getAttr(Args &&...args) {
97     return Attr::get(context, std::forward<Args>(args)...);
98   }
99 
100   // Attributes.
101   NamedAttribute getNamedAttr(StringRef name, Attribute val);
102 
103   UnitAttr getUnitAttr();
104   BoolAttr getBoolAttr(bool value);
105   DictionaryAttr getDictionaryAttr(ArrayRef<NamedAttribute> value);
106   IntegerAttr getIntegerAttr(Type type, int64_t value);
107   IntegerAttr getIntegerAttr(Type type, const APInt &value);
108   FloatAttr getFloatAttr(Type type, double value);
109   FloatAttr getFloatAttr(Type type, const APFloat &value);
110   StringAttr getStringAttr(const Twine &bytes);
111   ArrayAttr getArrayAttr(ArrayRef<Attribute> value);
112 
113   // Returns a 0-valued attribute of the given `type`. This function only
114   // supports boolean, integer, and 16-/32-/64-bit float types, and vector or
115   // ranked tensor of them. Returns null attribute otherwise.
116   TypedAttr getZeroAttr(Type type);
117   // Returns a 1-valued attribute of the given `type`.
118   // Type constraints are the same as `getZeroAttr`.
119   TypedAttr getOneAttr(Type type);
120 
121   // Convenience methods for fixed types.
122   FloatAttr getF16FloatAttr(float value);
123   FloatAttr getF32FloatAttr(float value);
124   FloatAttr getF64FloatAttr(double value);
125 
126   IntegerAttr getI8IntegerAttr(int8_t value);
127   IntegerAttr getI16IntegerAttr(int16_t value);
128   IntegerAttr getI32IntegerAttr(int32_t value);
129   IntegerAttr getI64IntegerAttr(int64_t value);
130   IntegerAttr getIndexAttr(int64_t value);
131 
132   /// Signed and unsigned integer attribute getters.
133   IntegerAttr getSI32IntegerAttr(int32_t value);
134   IntegerAttr getUI32IntegerAttr(uint32_t value);
135 
136   /// Vector-typed DenseIntElementsAttr getters. `values` must not be empty.
137   DenseIntElementsAttr getBoolVectorAttr(ArrayRef<bool> values);
138   DenseIntElementsAttr getI32VectorAttr(ArrayRef<int32_t> values);
139   DenseIntElementsAttr getI64VectorAttr(ArrayRef<int64_t> values);
140   DenseIntElementsAttr getIndexVectorAttr(ArrayRef<int64_t> values);
141 
142   DenseFPElementsAttr getF32VectorAttr(ArrayRef<float> values);
143   DenseFPElementsAttr getF64VectorAttr(ArrayRef<double> values);
144 
145   /// Tensor-typed DenseIntElementsAttr getters. `values` can be empty.
146   /// These are generally preferable for representing general lists of integers
147   /// as attributes.
148   DenseIntElementsAttr getI32TensorAttr(ArrayRef<int32_t> values);
149   DenseIntElementsAttr getI64TensorAttr(ArrayRef<int64_t> values);
150   DenseIntElementsAttr getIndexTensorAttr(ArrayRef<int64_t> values);
151 
152   /// Tensor-typed DenseArrayAttr getters.
153   DenseBoolArrayAttr getDenseBoolArrayAttr(ArrayRef<bool> values);
154   DenseI8ArrayAttr getDenseI8ArrayAttr(ArrayRef<int8_t> values);
155   DenseI16ArrayAttr getDenseI16ArrayAttr(ArrayRef<int16_t> values);
156   DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef<int32_t> values);
157   DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef<int64_t> values);
158   DenseF32ArrayAttr getDenseF32ArrayAttr(ArrayRef<float> values);
159   DenseF64ArrayAttr getDenseF64ArrayAttr(ArrayRef<double> values);
160 
161   ArrayAttr getAffineMapArrayAttr(ArrayRef<AffineMap> values);
162   ArrayAttr getBoolArrayAttr(ArrayRef<bool> values);
163   ArrayAttr getI32ArrayAttr(ArrayRef<int32_t> values);
164   ArrayAttr getI64ArrayAttr(ArrayRef<int64_t> values);
165   ArrayAttr getIndexArrayAttr(ArrayRef<int64_t> values);
166   ArrayAttr getF32ArrayAttr(ArrayRef<float> values);
167   ArrayAttr getF64ArrayAttr(ArrayRef<double> values);
168   ArrayAttr getStrArrayAttr(ArrayRef<StringRef> values);
169   ArrayAttr getTypeArrayAttr(TypeRange values);
170 
171   // Affine expressions and affine maps.
172   AffineExpr getAffineDimExpr(unsigned position);
173   AffineExpr getAffineSymbolExpr(unsigned position);
174   AffineExpr getAffineConstantExpr(int64_t constant);
175 
176   // Special cases of affine maps and integer sets
177   /// Returns a zero result affine map with no dimensions or symbols: () -> ().
178   AffineMap getEmptyAffineMap();
179   /// Returns a single constant result affine map with 0 dimensions and 0
180   /// symbols.  One constant result: () -> (val).
181   AffineMap getConstantAffineMap(int64_t val);
182   // One dimension id identity map: (i) -> (i).
183   AffineMap getDimIdentityMap();
184   // Multi-dimensional identity map: (d0, d1, d2) -> (d0, d1, d2).
185   AffineMap getMultiDimIdentityMap(unsigned rank);
186   // One symbol identity map: ()[s] -> (s).
187   AffineMap getSymbolIdentityMap();
188 
189   /// Returns a map that shifts its (single) input dimension by 'shift'.
190   /// (d0) -> (d0 + shift)
191   AffineMap getSingleDimShiftAffineMap(int64_t shift);
192 
193   /// Returns an affine map that is a translation (shift) of all result
194   /// expressions in 'map' by 'shift'.
195   /// Eg: input: (d0, d1)[s0] -> (d0, d1 + s0), shift = 2
196   ///   returns:    (d0, d1)[s0] -> (d0 + 2, d1 + s0 + 2)
197   AffineMap getShiftedAffineMap(AffineMap map, int64_t shift);
198 
199 protected:
200   MLIRContext *context;
201 };
202 
203 /// This class helps build Operations. Operations that are created are
204 /// automatically inserted at an insertion point. The builder is copyable.
205 class OpBuilder : public Builder {
206 public:
207   class InsertPoint;
208   struct Listener;
209 
210   /// Create a builder with the given context.
211   explicit OpBuilder(MLIRContext *ctx, Listener *listener = nullptr)
212       : Builder(ctx), listener(listener) {}
213 
214   /// Create a builder and set the insertion point to the start of the region.
215   explicit OpBuilder(Region *region, Listener *listener = nullptr)
216       : OpBuilder(region->getContext(), listener) {
217     if (!region->empty())
218       setInsertionPointToStart(&region->front());
219   }
220   explicit OpBuilder(Region &region, Listener *listener = nullptr)
221       : OpBuilder(&region, listener) {}
222 
223   /// Create a builder and set insertion point to the given operation, which
224   /// will cause subsequent insertions to go right before it.
225   explicit OpBuilder(Operation *op, Listener *listener = nullptr)
226       : OpBuilder(op->getContext(), listener) {
227     setInsertionPoint(op);
228   }
229 
230   OpBuilder(Block *block, Block::iterator insertPoint,
231             Listener *listener = nullptr)
232       : OpBuilder(block->getParent()->getContext(), listener) {
233     setInsertionPoint(block, insertPoint);
234   }
235 
236   /// Create a builder and set the insertion point to before the first operation
237   /// in the block but still inside the block.
238   static OpBuilder atBlockBegin(Block *block, Listener *listener = nullptr) {
239     return OpBuilder(block, block->begin(), listener);
240   }
241 
242   /// Create a builder and set the insertion point to after the last operation
243   /// in the block but still inside the block.
244   static OpBuilder atBlockEnd(Block *block, Listener *listener = nullptr) {
245     return OpBuilder(block, block->end(), listener);
246   }
247 
248   /// Create a builder and set the insertion point to before the block
249   /// terminator.
250   static OpBuilder atBlockTerminator(Block *block,
251                                      Listener *listener = nullptr) {
252     auto *terminator = block->getTerminator();
253     assert(terminator != nullptr && "the block has no terminator");
254     return OpBuilder(block, Block::iterator(terminator), listener);
255   }
256 
257   //===--------------------------------------------------------------------===//
258   // Listeners
259   //===--------------------------------------------------------------------===//
260 
261   /// Base class for listeners.
262   struct ListenerBase {
263     /// The kind of listener.
264     enum class Kind {
265       /// OpBuilder::Listener or user-derived class.
266       OpBuilderListener = 0,
267 
268       /// RewriterBase::Listener or user-derived class.
269       RewriterBaseListener = 1
270     };
271 
272     Kind getKind() const { return kind; }
273 
274   protected:
275     ListenerBase(Kind kind) : kind(kind) {}
276 
277   private:
278     const Kind kind;
279   };
280 
281   /// This class represents a listener that may be used to hook into various
282   /// actions within an OpBuilder.
283   struct Listener : public ListenerBase {
284     Listener() : ListenerBase(ListenerBase::Kind::OpBuilderListener) {}
285 
286     virtual ~Listener() = default;
287 
288     /// Notify the listener that the specified operation was inserted.
289     ///
290     /// * If the operation was moved, then `previous` is the previous location
291     ///   of the op.
292     /// * If the operation was unlinked before it was inserted, then `previous`
293     ///   is empty.
294     ///
295     /// Note: Creating an (unlinked) op does not trigger this notification.
296     virtual void notifyOperationInserted(Operation *op, InsertPoint previous) {}
297 
298     /// Notify the listener that the specified block was inserted.
299     ///
300     /// * If the block was moved, then `previous` and `previousIt` are the
301     ///   previous location of the block.
302     /// * If the block was unlinked before it was inserted, then `previous`
303     ///   is "nullptr".
304     ///
305     /// Note: Creating an (unlinked) block does not trigger this notification.
306     virtual void notifyBlockInserted(Block *block, Region *previous,
307                                      Region::iterator previousIt) {}
308 
309   protected:
310     Listener(Kind kind) : ListenerBase(kind) {}
311   };
312 
313   /// Sets the listener of this builder to the one provided.
314   void setListener(Listener *newListener) { listener = newListener; }
315 
316   /// Returns the current listener of this builder, or nullptr if this builder
317   /// doesn't have a listener.
318   Listener *getListener() const { return listener; }
319 
320   //===--------------------------------------------------------------------===//
321   // Insertion Point Management
322   //===--------------------------------------------------------------------===//
323 
324   /// This class represents a saved insertion point.
325   class InsertPoint {
326   public:
327     /// Creates a new insertion point which doesn't point to anything.
328     InsertPoint() = default;
329 
330     /// Creates a new insertion point at the given location.
331     InsertPoint(Block *insertBlock, Block::iterator insertPt)
332         : block(insertBlock), point(insertPt) {}
333 
334     /// Returns true if this insert point is set.
335     bool isSet() const { return (block != nullptr); }
336 
337     Block *getBlock() const { return block; }
338     Block::iterator getPoint() const { return point; }
339 
340   private:
341     Block *block = nullptr;
342     Block::iterator point;
343   };
344 
345   /// RAII guard to reset the insertion point of the builder when destroyed.
346   class InsertionGuard {
347   public:
348     InsertionGuard(OpBuilder &builder)
349         : builder(&builder), ip(builder.saveInsertionPoint()) {}
350 
351     ~InsertionGuard() {
352       if (builder)
353         builder->restoreInsertionPoint(ip);
354     }
355 
356     InsertionGuard(const InsertionGuard &) = delete;
357     InsertionGuard &operator=(const InsertionGuard &) = delete;
358 
359     /// Implement the move constructor to clear the builder field of `other`.
360     /// That way it does not restore the insertion point upon destruction as
361     /// that should be done exclusively by the just constructed InsertionGuard.
362     InsertionGuard(InsertionGuard &&other) noexcept
363         : builder(other.builder), ip(other.ip) {
364       other.builder = nullptr;
365     }
366 
367     InsertionGuard &operator=(InsertionGuard &&other) = delete;
368 
369   private:
370     OpBuilder *builder;
371     OpBuilder::InsertPoint ip;
372   };
373 
374   /// Reset the insertion point to no location.  Creating an operation without a
375   /// set insertion point is an error, but this can still be useful when the
376   /// current insertion point a builder refers to is being removed.
377   void clearInsertionPoint() {
378     this->block = nullptr;
379     insertPoint = Block::iterator();
380   }
381 
382   /// Return a saved insertion point.
383   InsertPoint saveInsertionPoint() const {
384     return InsertPoint(getInsertionBlock(), getInsertionPoint());
385   }
386 
387   /// Restore the insert point to a previously saved point.
388   void restoreInsertionPoint(InsertPoint ip) {
389     if (ip.isSet())
390       setInsertionPoint(ip.getBlock(), ip.getPoint());
391     else
392       clearInsertionPoint();
393   }
394 
395   /// Set the insertion point to the specified location.
396   void setInsertionPoint(Block *block, Block::iterator insertPoint) {
397     // TODO: check that insertPoint is in this rather than some other block.
398     this->block = block;
399     this->insertPoint = insertPoint;
400   }
401 
402   /// Sets the insertion point to the specified operation, which will cause
403   /// subsequent insertions to go right before it.
404   void setInsertionPoint(Operation *op) {
405     setInsertionPoint(op->getBlock(), Block::iterator(op));
406   }
407 
408   /// Sets the insertion point to the node after the specified operation, which
409   /// will cause subsequent insertions to go right after it.
410   void setInsertionPointAfter(Operation *op) {
411     setInsertionPoint(op->getBlock(), ++Block::iterator(op));
412   }
413 
414   /// Sets the insertion point to the node after the specified value. If value
415   /// has a defining operation, sets the insertion point to the node after such
416   /// defining operation. This will cause subsequent insertions to go right
417   /// after it. Otherwise, value is a BlockArgument. Sets the insertion point to
418   /// the start of its block.
419   void setInsertionPointAfterValue(Value val) {
420     if (Operation *op = val.getDefiningOp()) {
421       setInsertionPointAfter(op);
422     } else {
423       auto blockArg = llvm::cast<BlockArgument>(val);
424       setInsertionPointToStart(blockArg.getOwner());
425     }
426   }
427 
428   /// Sets the insertion point to the start of the specified block.
429   void setInsertionPointToStart(Block *block) {
430     setInsertionPoint(block, block->begin());
431   }
432 
433   /// Sets the insertion point to the end of the specified block.
434   void setInsertionPointToEnd(Block *block) {
435     setInsertionPoint(block, block->end());
436   }
437 
438   /// Return the block the current insertion point belongs to.  Note that the
439   /// insertion point is not necessarily the end of the block.
440   Block *getInsertionBlock() const { return block; }
441 
442   /// Returns the current insertion point of the builder.
443   Block::iterator getInsertionPoint() const { return insertPoint; }
444 
445   /// Returns the current block of the builder.
446   Block *getBlock() const { return block; }
447 
448   //===--------------------------------------------------------------------===//
449   // Block Creation
450   //===--------------------------------------------------------------------===//
451 
452   /// Add new block with 'argTypes' arguments and set the insertion point to the
453   /// end of it. The block is inserted at the provided insertion point of
454   /// 'parent'. `locs` contains the locations of the inserted arguments, and
455   /// should match the size of `argTypes`.
456   Block *createBlock(Region *parent, Region::iterator insertPt = {},
457                      TypeRange argTypes = std::nullopt,
458                      ArrayRef<Location> locs = std::nullopt);
459 
460   /// Add new block with 'argTypes' arguments and set the insertion point to the
461   /// end of it. The block is placed before 'insertBefore'. `locs` contains the
462   /// locations of the inserted arguments, and should match the size of
463   /// `argTypes`.
464   Block *createBlock(Block *insertBefore, TypeRange argTypes = std::nullopt,
465                      ArrayRef<Location> locs = std::nullopt);
466 
467   //===--------------------------------------------------------------------===//
468   // Operation Creation
469   //===--------------------------------------------------------------------===//
470 
471   /// Insert the given operation at the current insertion point and return it.
472   Operation *insert(Operation *op);
473 
474   /// Creates an operation given the fields represented as an OperationState.
475   Operation *create(const OperationState &state);
476 
477   /// Creates an operation with the given fields.
478   Operation *create(Location loc, StringAttr opName, ValueRange operands,
479                     TypeRange types = {},
480                     ArrayRef<NamedAttribute> attributes = {},
481                     BlockRange successors = {},
482                     MutableArrayRef<std::unique_ptr<Region>> regions = {});
483 
484 private:
485   /// Helper for sanity checking preconditions for create* methods below.
486   template <typename OpT>
487   RegisteredOperationName getCheckRegisteredInfo(MLIRContext *ctx) {
488     std::optional<RegisteredOperationName> opName =
489         RegisteredOperationName::lookup(TypeID::get<OpT>(), ctx);
490     if (LLVM_UNLIKELY(!opName)) {
491       llvm::report_fatal_error(
492           "Building op `" + OpT::getOperationName() +
493           "` but it isn't known in this MLIRContext: the dialect may not "
494           "be loaded or this operation hasn't been added by the dialect. See "
495           "also https://mlir.llvm.org/getting_started/Faq/"
496           "#registered-loaded-dependent-whats-up-with-dialects-management");
497     }
498     return *opName;
499   }
500 
501 public:
502   /// Create an operation of specific op type at the current insertion point.
503   template <typename OpTy, typename... Args>
504   OpTy create(Location location, Args &&...args) {
505     OperationState state(location,
506                          getCheckRegisteredInfo<OpTy>(location.getContext()));
507     OpTy::build(*this, state, std::forward<Args>(args)...);
508     auto *op = create(state);
509     auto result = dyn_cast<OpTy>(op);
510     assert(result && "builder didn't return the right type");
511     return result;
512   }
513 
514   /// Create an operation of specific op type at the current insertion point,
515   /// and immediately try to fold it. This functions populates 'results' with
516   /// the results of the operation.
517   template <typename OpTy, typename... Args>
518   void createOrFold(SmallVectorImpl<Value> &results, Location location,
519                     Args &&...args) {
520     // Create the operation without using 'create' as we want to control when
521     // the listener is notified.
522     OperationState state(location,
523                          getCheckRegisteredInfo<OpTy>(location.getContext()));
524     OpTy::build(*this, state, std::forward<Args>(args)...);
525     Operation *op = Operation::create(state);
526     if (block)
527       block->getOperations().insert(insertPoint, op);
528 
529     // Attempt to fold the operation.
530     if (succeeded(tryFold(op, results)) && !results.empty()) {
531       // Erase the operation, if the fold removed the need for this operation.
532       // Note: The fold already populated the results in this case.
533       op->erase();
534       return;
535     }
536 
537     ResultRange opResults = op->getResults();
538     results.assign(opResults.begin(), opResults.end());
539     if (block && listener)
540       listener->notifyOperationInserted(op, /*previous=*/{});
541   }
542 
543   /// Overload to create or fold a single result operation.
544   template <typename OpTy, typename... Args>
545   std::enable_if_t<OpTy::template hasTrait<OpTrait::OneResult>(), Value>
546   createOrFold(Location location, Args &&...args) {
547     SmallVector<Value, 1> results;
548     createOrFold<OpTy>(results, location, std::forward<Args>(args)...);
549     return results.front();
550   }
551 
552   /// Overload to create or fold a zero result operation.
553   template <typename OpTy, typename... Args>
554   std::enable_if_t<OpTy::template hasTrait<OpTrait::ZeroResults>(), OpTy>
555   createOrFold(Location location, Args &&...args) {
556     auto op = create<OpTy>(location, std::forward<Args>(args)...);
557     SmallVector<Value, 0> unused;
558     (void)tryFold(op.getOperation(), unused);
559 
560     // Folding cannot remove a zero-result operation, so for convenience we
561     // continue to return it.
562     return op;
563   }
564 
565   /// Attempts to fold the given operation and places new results within
566   /// `results`. Returns success if the operation was folded, failure otherwise.
567   /// If the fold was in-place, `results` will not be filled.
568   /// Note: This function does not erase the operation on a successful fold.
569   LogicalResult tryFold(Operation *op, SmallVectorImpl<Value> &results);
570 
571   /// Creates a deep copy of the specified operation, remapping any operands
572   /// that use values outside of the operation using the map that is provided
573   /// ( leaving them alone if no entry is present).  Replaces references to
574   /// cloned sub-operations to the corresponding operation that is copied,
575   /// and adds those mappings to the map.
576   Operation *clone(Operation &op, IRMapping &mapper);
577   Operation *clone(Operation &op);
578 
579   /// Creates a deep copy of this operation but keep the operation regions
580   /// empty. Operands are remapped using `mapper` (if present), and `mapper` is
581   /// updated to contain the results.
582   Operation *cloneWithoutRegions(Operation &op, IRMapping &mapper) {
583     return insert(op.cloneWithoutRegions(mapper));
584   }
585   Operation *cloneWithoutRegions(Operation &op) {
586     return insert(op.cloneWithoutRegions());
587   }
588   template <typename OpT>
589   OpT cloneWithoutRegions(OpT op) {
590     return cast<OpT>(cloneWithoutRegions(*op.getOperation()));
591   }
592 
593   /// Clone the blocks that belong to "region" before the given position in
594   /// another region "parent". The two regions must be different. The caller is
595   /// responsible for creating or updating the operation transferring flow of
596   /// control to the region and passing it the correct block arguments.
597   void cloneRegionBefore(Region &region, Region &parent,
598                          Region::iterator before, IRMapping &mapping);
599   void cloneRegionBefore(Region &region, Region &parent,
600                          Region::iterator before);
601   void cloneRegionBefore(Region &region, Block *before);
602 
603 protected:
604   /// The optional listener for events of this builder.
605   Listener *listener;
606 
607 private:
608   /// The current block this builder is inserting into.
609   Block *block = nullptr;
610   /// The insertion point within the block that this builder is inserting
611   /// before.
612   Block::iterator insertPoint;
613 };
614 
615 } // namespace mlir
616 
617 #endif
618