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