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(®ion->front(), region->front().begin()); 197 } 198 explicit OpBuilder(Region ®ion, Listener *listener = nullptr) 199 : OpBuilder(®ion, 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