1 //===-- FirBuilder.h -- FIR operation builder -------------------*- 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 // Builder routines for constructing the FIR dialect of MLIR. As FIR is a 10 // dialect of MLIR, it makes extensive use of MLIR interfaces and MLIR's coding 11 // style (https://mlir.llvm.org/getting_started/DeveloperGuide/) is used in this 12 // module. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #ifndef FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H 17 #define FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H 18 19 #include "flang/Common/MathOptionsBase.h" 20 #include "flang/Optimizer/Dialect/FIROps.h" 21 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 22 #include "flang/Optimizer/Dialect/FIRType.h" 23 #include "flang/Optimizer/Dialect/Support/FIRContext.h" 24 #include "flang/Optimizer/Dialect/Support/KindMapping.h" 25 #include "mlir/IR/Builders.h" 26 #include "mlir/IR/BuiltinOps.h" 27 #include "llvm/ADT/DenseMap.h" 28 #include <optional> 29 #include <utility> 30 31 namespace mlir { 32 class DataLayout; 33 class SymbolTable; 34 } 35 36 namespace fir { 37 class AbstractArrayBox; 38 class ExtendedValue; 39 class MutableBoxValue; 40 class BoxValue; 41 42 /// Get the integer type with a pointer size. 43 inline mlir::Type getIntPtrType(mlir::OpBuilder &builder) { 44 // TODO: Delay the need of such type until codegen or find a way to use 45 // llvm::DataLayout::getPointerSizeInBits here. 46 return builder.getI64Type(); 47 } 48 49 //===----------------------------------------------------------------------===// 50 // FirOpBuilder 51 //===----------------------------------------------------------------------===// 52 53 /// Extends the MLIR OpBuilder to provide methods for building common FIR 54 /// patterns. 55 class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener { 56 public: 57 explicit FirOpBuilder(mlir::Operation *op, fir::KindMapping kindMap, 58 mlir::SymbolTable *symbolTable = nullptr) 59 : OpBuilder{op, /*listener=*/this}, kindMap{std::move(kindMap)}, 60 symbolTable{symbolTable} {} 61 explicit FirOpBuilder(mlir::OpBuilder &builder, fir::KindMapping kindMap, 62 mlir::SymbolTable *symbolTable = nullptr) 63 : OpBuilder(builder), OpBuilder::Listener(), kindMap{std::move(kindMap)}, 64 symbolTable{symbolTable} { 65 setListener(this); 66 } 67 explicit FirOpBuilder(mlir::OpBuilder &builder, mlir::ModuleOp mod) 68 : OpBuilder(builder), OpBuilder::Listener(), 69 kindMap{getKindMapping(mod)} { 70 setListener(this); 71 } 72 explicit FirOpBuilder(mlir::OpBuilder &builder, fir::KindMapping kindMap, 73 mlir::Operation *op) 74 : OpBuilder(builder), OpBuilder::Listener(), kindMap{std::move(kindMap)} { 75 setListener(this); 76 auto fmi = mlir::dyn_cast<mlir::arith::ArithFastMathInterface>(*op); 77 if (fmi) { 78 // Set the builder with FastMathFlags attached to the operation. 79 setFastMathFlags(fmi.getFastMathFlagsAttr().getValue()); 80 } 81 } 82 FirOpBuilder(mlir::OpBuilder &builder, mlir::Operation *op) 83 : FirOpBuilder(builder, fir::getKindMapping(op), op) {} 84 85 // The listener self-reference has to be updated in case of copy-construction. 86 FirOpBuilder(const FirOpBuilder &other) 87 : OpBuilder(other), OpBuilder::Listener(), kindMap{other.kindMap}, 88 fastMathFlags{other.fastMathFlags}, 89 integerOverflowFlags{other.integerOverflowFlags}, 90 symbolTable{other.symbolTable} { 91 setListener(this); 92 } 93 94 FirOpBuilder(FirOpBuilder &&other) 95 : OpBuilder(other), OpBuilder::Listener(), 96 kindMap{std::move(other.kindMap)}, fastMathFlags{other.fastMathFlags}, 97 integerOverflowFlags{other.integerOverflowFlags}, 98 symbolTable{other.symbolTable} { 99 setListener(this); 100 } 101 102 /// Get the current Region of the insertion point. 103 mlir::Region &getRegion() { return *getBlock()->getParent(); } 104 105 /// Get the current Module 106 mlir::ModuleOp getModule() { 107 return getRegion().getParentOfType<mlir::ModuleOp>(); 108 } 109 110 /// Get the current Function 111 mlir::func::FuncOp getFunction() { 112 return getRegion().getParentOfType<mlir::func::FuncOp>(); 113 } 114 115 /// Get a reference to the kind map. 116 const fir::KindMapping &getKindMap() { return kindMap; } 117 118 /// Get func.func/fir.global symbol table attached to this builder if any. 119 mlir::SymbolTable *getMLIRSymbolTable() { return symbolTable; } 120 121 /// Get the default integer type 122 [[maybe_unused]] mlir::IntegerType getDefaultIntegerType() { 123 return getIntegerType( 124 getKindMap().getIntegerBitsize(getKindMap().defaultIntegerKind())); 125 } 126 127 /// The LHS and RHS are not always in agreement in terms of type. In some 128 /// cases, the disagreement is between COMPLEX and other scalar types. In that 129 /// case, the conversion must insert (extract) out of a COMPLEX value to have 130 /// the proper semantics and be strongly typed. E.g., converting an integer 131 /// (real) to a complex, the real part is filled using the integer (real) 132 /// after type conversion and the imaginary part is zero. 133 mlir::Value convertWithSemantics(mlir::Location loc, mlir::Type toTy, 134 mlir::Value val, 135 bool allowCharacterConversion = false, 136 bool allowRebox = false); 137 138 /// Get the entry block of the current Function 139 mlir::Block *getEntryBlock() { return &getFunction().front(); } 140 141 /// Get the block for adding Allocas. If OpenMP is enabled then get the 142 /// the alloca block from an Operation which can be Outlined. Otherwise 143 /// use the entry block of the current Function 144 mlir::Block *getAllocaBlock(); 145 146 /// Safely create a reference type to the type `eleTy`. 147 mlir::Type getRefType(mlir::Type eleTy); 148 149 /// Create a sequence of `eleTy` with `rank` dimensions of unknown size. 150 mlir::Type getVarLenSeqTy(mlir::Type eleTy, unsigned rank = 1); 151 152 /// Get character length type. 153 mlir::Type getCharacterLengthType() { return getIndexType(); } 154 155 /// Get the integer type whose bit width corresponds to the width of pointer 156 /// types, or is bigger. 157 mlir::Type getIntPtrType() { return fir::getIntPtrType(*this); } 158 159 /// Wrap `str` to a SymbolRefAttr. 160 mlir::SymbolRefAttr getSymbolRefAttr(llvm::StringRef str) { 161 return mlir::SymbolRefAttr::get(getContext(), str); 162 } 163 164 /// Get the mlir float type that implements Fortran REAL(kind). 165 mlir::Type getRealType(int kind); 166 167 fir::BoxProcType getBoxProcType(mlir::FunctionType funcTy) { 168 return fir::BoxProcType::get(getContext(), funcTy); 169 } 170 171 /// Create a null constant memory reference of type \p ptrType. 172 /// If \p ptrType is not provided, !fir.ref<none> type will be used. 173 mlir::Value createNullConstant(mlir::Location loc, mlir::Type ptrType = {}); 174 175 /// Create an integer constant of type \p type and value \p i. 176 /// Should not be used with negative values with integer types of more 177 /// than 64 bits. 178 mlir::Value createIntegerConstant(mlir::Location loc, mlir::Type integerType, 179 std::int64_t i); 180 181 /// Create an integer of \p integerType where all the bits have been set to 182 /// ones. Safe to use regardless of integerType bitwidth. 183 mlir::Value createAllOnesInteger(mlir::Location loc, mlir::Type integerType); 184 185 /// Create -1 constant of \p integerType. Safe to use regardless of 186 /// integerType bitwidth. 187 mlir::Value createMinusOneInteger(mlir::Location loc, 188 mlir::Type integerType) { 189 return createAllOnesInteger(loc, integerType); 190 } 191 192 /// Create a real constant from an integer value. 193 mlir::Value createRealConstant(mlir::Location loc, mlir::Type realType, 194 llvm::APFloat::integerPart val); 195 196 /// Create a real constant from an APFloat value. 197 mlir::Value createRealConstant(mlir::Location loc, mlir::Type realType, 198 const llvm::APFloat &val); 199 200 /// Create a real constant of type \p realType with a value zero. 201 mlir::Value createRealZeroConstant(mlir::Location loc, mlir::Type realType) { 202 return createRealConstant(loc, realType, 0u); 203 } 204 205 /// Create a slot for a local on the stack. Besides the variable's type and 206 /// shape, it may be given name, pinned, or target attributes. 207 mlir::Value allocateLocal(mlir::Location loc, mlir::Type ty, 208 llvm::StringRef uniqName, llvm::StringRef name, 209 bool pinned, llvm::ArrayRef<mlir::Value> shape, 210 llvm::ArrayRef<mlir::Value> lenParams, 211 bool asTarget = false); 212 mlir::Value allocateLocal(mlir::Location loc, mlir::Type ty, 213 llvm::StringRef uniqName, llvm::StringRef name, 214 llvm::ArrayRef<mlir::Value> shape, 215 llvm::ArrayRef<mlir::Value> lenParams, 216 bool asTarget = false); 217 218 /// Create a two dimensional ArrayAttr containing integer data as 219 /// IntegerAttrs, effectively: ArrayAttr<ArrayAttr<IntegerAttr>>>. 220 mlir::ArrayAttr create2DI64ArrayAttr( 221 llvm::SmallVectorImpl<llvm::SmallVector<int64_t>> &intData); 222 223 /// Create a temporary using `fir.alloca`. This function does not hoist. 224 /// It is the callers responsibility to set the insertion point if 225 /// hoisting is required. 226 mlir::Value createTemporaryAlloc( 227 mlir::Location loc, mlir::Type type, llvm::StringRef name, 228 mlir::ValueRange lenParams = {}, mlir::ValueRange shape = {}, 229 llvm::ArrayRef<mlir::NamedAttribute> attrs = {}, 230 std::optional<Fortran::common::CUDADataAttr> cudaAttr = std::nullopt); 231 232 /// Create a temporary. A temp is allocated using `fir.alloca` and can be read 233 /// and written using `fir.load` and `fir.store`, resp. The temporary can be 234 /// given a name via a front-end `Symbol` or a `StringRef`. 235 mlir::Value createTemporary( 236 mlir::Location loc, mlir::Type type, llvm::StringRef name = {}, 237 mlir::ValueRange shape = {}, mlir::ValueRange lenParams = {}, 238 llvm::ArrayRef<mlir::NamedAttribute> attrs = {}, 239 std::optional<Fortran::common::CUDADataAttr> cudaAttr = std::nullopt); 240 241 /// Create an unnamed and untracked temporary on the stack. 242 mlir::Value createTemporary(mlir::Location loc, mlir::Type type, 243 mlir::ValueRange shape) { 244 return createTemporary(loc, type, llvm::StringRef{}, shape); 245 } 246 247 mlir::Value createTemporary(mlir::Location loc, mlir::Type type, 248 llvm::ArrayRef<mlir::NamedAttribute> attrs) { 249 return createTemporary(loc, type, llvm::StringRef{}, {}, {}, attrs); 250 } 251 252 mlir::Value createTemporary(mlir::Location loc, mlir::Type type, 253 llvm::StringRef name, 254 llvm::ArrayRef<mlir::NamedAttribute> attrs) { 255 return createTemporary(loc, type, name, {}, {}, attrs); 256 } 257 258 /// Create a temporary on the heap. 259 mlir::Value 260 createHeapTemporary(mlir::Location loc, mlir::Type type, 261 llvm::StringRef name = {}, mlir::ValueRange shape = {}, 262 mlir::ValueRange lenParams = {}, 263 llvm::ArrayRef<mlir::NamedAttribute> attrs = {}); 264 265 /// Create an LLVM stack save intrinsic op. Returns the saved stack pointer. 266 /// The stack address space is fetched from the data layout of the current 267 /// module. 268 mlir::Value genStackSave(mlir::Location loc); 269 270 /// Create an LLVM stack restore intrinsic op. stackPointer should be a value 271 /// previously returned from genStackSave. 272 void genStackRestore(mlir::Location loc, mlir::Value stackPointer); 273 274 /// Create a global value. 275 fir::GlobalOp createGlobal(mlir::Location loc, mlir::Type type, 276 llvm::StringRef name, 277 mlir::StringAttr linkage = {}, 278 mlir::Attribute value = {}, bool isConst = false, 279 bool isTarget = false, 280 cuf::DataAttributeAttr dataAttr = {}); 281 282 fir::GlobalOp createGlobal(mlir::Location loc, mlir::Type type, 283 llvm::StringRef name, bool isConst, bool isTarget, 284 std::function<void(FirOpBuilder &)> bodyBuilder, 285 mlir::StringAttr linkage = {}, 286 cuf::DataAttributeAttr dataAttr = {}); 287 288 /// Create a global constant (read-only) value. 289 fir::GlobalOp createGlobalConstant(mlir::Location loc, mlir::Type type, 290 llvm::StringRef name, 291 mlir::StringAttr linkage = {}, 292 mlir::Attribute value = {}) { 293 return createGlobal(loc, type, name, linkage, value, /*isConst=*/true, 294 /*isTarget=*/false); 295 } 296 297 fir::GlobalOp 298 createGlobalConstant(mlir::Location loc, mlir::Type type, 299 llvm::StringRef name, 300 std::function<void(FirOpBuilder &)> bodyBuilder, 301 mlir::StringAttr linkage = {}) { 302 return createGlobal(loc, type, name, /*isConst=*/true, /*isTarget=*/false, 303 bodyBuilder, linkage); 304 } 305 306 /// Convert a StringRef string into a fir::StringLitOp. 307 fir::StringLitOp createStringLitOp(mlir::Location loc, 308 llvm::StringRef string); 309 310 std::pair<fir::TypeInfoOp, mlir::OpBuilder::InsertPoint> 311 createTypeInfoOp(mlir::Location loc, fir::RecordType recordType, 312 fir::RecordType parentType); 313 314 //===--------------------------------------------------------------------===// 315 // Linkage helpers (inline). The default linkage is external. 316 //===--------------------------------------------------------------------===// 317 318 mlir::StringAttr createCommonLinkage() { return getStringAttr("common"); } 319 320 mlir::StringAttr createInternalLinkage() { return getStringAttr("internal"); } 321 322 mlir::StringAttr createLinkOnceLinkage() { return getStringAttr("linkonce"); } 323 324 mlir::StringAttr createLinkOnceODRLinkage() { 325 return getStringAttr("linkonce_odr"); 326 } 327 328 mlir::StringAttr createWeakLinkage() { return getStringAttr("weak"); } 329 330 /// Get a function by name. If the function exists in the current module, it 331 /// is returned. Otherwise, a null FuncOp is returned. 332 mlir::func::FuncOp getNamedFunction(llvm::StringRef name) { 333 return getNamedFunction(getModule(), getMLIRSymbolTable(), name); 334 } 335 static mlir::func::FuncOp 336 getNamedFunction(mlir::ModuleOp module, const mlir::SymbolTable *symbolTable, 337 llvm::StringRef name); 338 339 /// Get a function by symbol name. The result will be null if there is no 340 /// function with the given symbol in the module. 341 mlir::func::FuncOp getNamedFunction(mlir::SymbolRefAttr symbol) { 342 return getNamedFunction(getModule(), getMLIRSymbolTable(), symbol); 343 } 344 static mlir::func::FuncOp 345 getNamedFunction(mlir::ModuleOp module, const mlir::SymbolTable *symbolTable, 346 mlir::SymbolRefAttr symbol); 347 348 fir::GlobalOp getNamedGlobal(llvm::StringRef name) { 349 return getNamedGlobal(getModule(), getMLIRSymbolTable(), name); 350 } 351 352 static fir::GlobalOp getNamedGlobal(mlir::ModuleOp module, 353 const mlir::SymbolTable *symbolTable, 354 llvm::StringRef name); 355 356 /// Lazy creation of fir.convert op. 357 mlir::Value createConvert(mlir::Location loc, mlir::Type toTy, 358 mlir::Value val); 359 360 /// Create a fir.store of \p val into \p addr. A lazy conversion 361 /// of \p val to the element type of \p addr is created if needed. 362 void createStoreWithConvert(mlir::Location loc, mlir::Value val, 363 mlir::Value addr); 364 365 /// Create a fir.load if \p val is a reference or pointer type. Return the 366 /// result of the load if it was created, otherwise return \p val 367 mlir::Value loadIfRef(mlir::Location loc, mlir::Value val); 368 369 /// Determine if the named function is already in the module. Return the 370 /// instance if found, otherwise add a new named function to the module. 371 mlir::func::FuncOp createFunction(mlir::Location loc, llvm::StringRef name, 372 mlir::FunctionType ty) { 373 return createFunction(loc, getModule(), name, ty, getMLIRSymbolTable()); 374 } 375 376 static mlir::func::FuncOp createFunction(mlir::Location loc, 377 mlir::ModuleOp module, 378 llvm::StringRef name, 379 mlir::FunctionType ty, 380 mlir::SymbolTable *); 381 382 /// Cast the input value to IndexType. 383 mlir::Value convertToIndexType(mlir::Location loc, mlir::Value val) { 384 return createConvert(loc, getIndexType(), val); 385 } 386 387 /// Construct one of the two forms of shape op from an array box. 388 mlir::Value genShape(mlir::Location loc, const fir::AbstractArrayBox &arr); 389 mlir::Value genShape(mlir::Location loc, llvm::ArrayRef<mlir::Value> shift, 390 llvm::ArrayRef<mlir::Value> exts); 391 mlir::Value genShape(mlir::Location loc, llvm::ArrayRef<mlir::Value> exts); 392 mlir::Value genShift(mlir::Location loc, llvm::ArrayRef<mlir::Value> shift); 393 394 /// Create one of the shape ops given an extended value. For a boxed value, 395 /// this may create a `fir.shift` op. 396 mlir::Value createShape(mlir::Location loc, const fir::ExtendedValue &exv); 397 398 /// Create a slice op extended value. The value to be sliced, `exv`, must be 399 /// an array. 400 mlir::Value createSlice(mlir::Location loc, const fir::ExtendedValue &exv, 401 mlir::ValueRange triples, mlir::ValueRange path); 402 403 /// Create a boxed value (Fortran descriptor) to be passed to the runtime. 404 /// \p exv is an extended value holding a memory reference to the object that 405 /// must be boxed. This function will crash if provided something that is not 406 /// a memory reference type. 407 /// Array entities are boxed with a shape and possibly a shift. Character 408 /// entities are boxed with a LEN parameter. 409 mlir::Value createBox(mlir::Location loc, const fir::ExtendedValue &exv, 410 bool isPolymorphic = false, bool isAssumedType = false); 411 412 mlir::Value createBox(mlir::Location loc, mlir::Type boxType, 413 mlir::Value addr, mlir::Value shape, mlir::Value slice, 414 llvm::ArrayRef<mlir::Value> lengths, mlir::Value tdesc); 415 416 /// Create constant i1 with value 1. if \p b is true or 0. otherwise 417 mlir::Value createBool(mlir::Location loc, bool b) { 418 return createIntegerConstant(loc, getIntegerType(1), b ? 1 : 0); 419 } 420 421 //===--------------------------------------------------------------------===// 422 // If-Then-Else generation helper 423 //===--------------------------------------------------------------------===// 424 425 /// Helper class to create if-then-else in a structured way: 426 /// Usage: genIfOp().genThen([&](){...}).genElse([&](){...}).end(); 427 /// Alternatively, getResults() can be used instead of end() to end the ifOp 428 /// and get the ifOp results. 429 class IfBuilder { 430 public: 431 IfBuilder(fir::IfOp ifOp, FirOpBuilder &builder) 432 : ifOp{ifOp}, builder{builder} {} 433 template <typename CC> 434 IfBuilder &genThen(CC func) { 435 builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); 436 func(); 437 return *this; 438 } 439 template <typename CC> 440 IfBuilder &genElse(CC func) { 441 assert(!ifOp.getElseRegion().empty() && "must have else region"); 442 builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 443 func(); 444 return *this; 445 } 446 void end() { builder.setInsertionPointAfter(ifOp); } 447 448 /// End the IfOp and return the results if any. 449 mlir::Operation::result_range getResults() { 450 end(); 451 return ifOp.getResults(); 452 } 453 454 fir::IfOp &getIfOp() { return ifOp; }; 455 456 private: 457 fir::IfOp ifOp; 458 FirOpBuilder &builder; 459 }; 460 461 /// Create an IfOp and returns an IfBuilder that can generate the else/then 462 /// bodies. 463 IfBuilder genIfOp(mlir::Location loc, mlir::TypeRange results, 464 mlir::Value cdt, bool withElseRegion) { 465 auto op = create<fir::IfOp>(loc, results, cdt, withElseRegion); 466 return IfBuilder(op, *this); 467 } 468 469 /// Create an IfOp with no "else" region, and no result values. 470 /// Usage: genIfThen(loc, cdt).genThen(lambda).end(); 471 IfBuilder genIfThen(mlir::Location loc, mlir::Value cdt) { 472 auto op = create<fir::IfOp>(loc, std::nullopt, cdt, false); 473 return IfBuilder(op, *this); 474 } 475 476 /// Create an IfOp with an "else" region, and no result values. 477 /// Usage: genIfThenElse(loc, cdt).genThen(lambda).genElse(lambda).end(); 478 IfBuilder genIfThenElse(mlir::Location loc, mlir::Value cdt) { 479 auto op = create<fir::IfOp>(loc, std::nullopt, cdt, true); 480 return IfBuilder(op, *this); 481 } 482 483 mlir::Value genNot(mlir::Location loc, mlir::Value boolean) { 484 return create<mlir::arith::CmpIOp>(loc, mlir::arith::CmpIPredicate::eq, 485 boolean, createBool(loc, false)); 486 } 487 488 /// Generate code testing \p addr is not a null address. 489 mlir::Value genIsNotNullAddr(mlir::Location loc, mlir::Value addr); 490 491 /// Generate code testing \p addr is a null address. 492 mlir::Value genIsNullAddr(mlir::Location loc, mlir::Value addr); 493 494 /// Compute the extent of (lb:ub:step) as max((ub-lb+step)/step, 0). See 495 /// Fortran 2018 9.5.3.3.2 section for more details. 496 mlir::Value genExtentFromTriplet(mlir::Location loc, mlir::Value lb, 497 mlir::Value ub, mlir::Value step, 498 mlir::Type type); 499 500 /// Create an AbsentOp of \p argTy type and handle special cases, such as 501 /// Character Procedure Tuple arguments. 502 mlir::Value genAbsentOp(mlir::Location loc, mlir::Type argTy); 503 504 /// Set default FastMathFlags value for all operations 505 /// supporting mlir::arith::FastMathAttr that will be created 506 /// by this builder. 507 void setFastMathFlags(mlir::arith::FastMathFlags flags) { 508 fastMathFlags = flags; 509 } 510 511 /// Set default FastMathFlags value from the passed MathOptionsBase 512 /// config. 513 void setFastMathFlags(Fortran::common::MathOptionsBase options); 514 515 /// Get current FastMathFlags value. 516 mlir::arith::FastMathFlags getFastMathFlags() const { return fastMathFlags; } 517 518 /// Stringify FastMathFlags set in a way 519 /// that the string may be used for mangling a function name. 520 /// If FastMathFlags are set to 'none', then the result is an empty 521 /// string. 522 std::string getFastMathFlagsString() { 523 mlir::arith::FastMathFlags flags = getFastMathFlags(); 524 if (flags == mlir::arith::FastMathFlags::none) 525 return {}; 526 527 std::string fmfString{mlir::arith::stringifyFastMathFlags(flags)}; 528 std::replace(fmfString.begin(), fmfString.end(), ',', '_'); 529 return fmfString; 530 } 531 532 /// Set default IntegerOverflowFlags value for all operations 533 /// supporting mlir::arith::IntegerOverflowFlagsAttr that will be created 534 /// by this builder. 535 void setIntegerOverflowFlags(mlir::arith::IntegerOverflowFlags flags) { 536 integerOverflowFlags = flags; 537 } 538 539 /// Get current IntegerOverflowFlags value. 540 mlir::arith::IntegerOverflowFlags getIntegerOverflowFlags() const { 541 return integerOverflowFlags; 542 } 543 544 /// Dump the current function. (debug) 545 LLVM_DUMP_METHOD void dumpFunc(); 546 547 /// FirOpBuilder hook for creating new operation. 548 void notifyOperationInserted(mlir::Operation *op, 549 mlir::OpBuilder::InsertPoint previous) override { 550 // We only care about newly created operations. 551 if (previous.isSet()) 552 return; 553 setCommonAttributes(op); 554 } 555 556 /// Construct a data layout on demand and return it 557 mlir::DataLayout &getDataLayout(); 558 559 /// Convert operands &/or result from/to unsigned so that the operation 560 /// only receives/produces signless operands. 561 template <typename OpTy> 562 mlir::Value createUnsigned(mlir::Location loc, mlir::Type resultType, 563 mlir::Value left, mlir::Value right) { 564 if (!resultType.isIntOrFloat()) 565 return create<OpTy>(loc, resultType, left, right); 566 mlir::Type signlessType = mlir::IntegerType::get( 567 getContext(), resultType.getIntOrFloatBitWidth(), 568 mlir::IntegerType::SignednessSemantics::Signless); 569 mlir::Type opResType = resultType; 570 if (left.getType().isUnsignedInteger()) { 571 left = createConvert(loc, signlessType, left); 572 opResType = signlessType; 573 } 574 if (right.getType().isUnsignedInteger()) { 575 right = createConvert(loc, signlessType, right); 576 opResType = signlessType; 577 } 578 mlir::Value result = create<OpTy>(loc, opResType, left, right); 579 if (resultType.isUnsignedInteger()) 580 result = createConvert(loc, resultType, result); 581 return result; 582 } 583 584 private: 585 /// Set attributes (e.g. FastMathAttr) to \p op operation 586 /// based on the current attributes setting. 587 void setCommonAttributes(mlir::Operation *op) const; 588 589 KindMapping kindMap; 590 591 /// FastMathFlags that need to be set for operations that support 592 /// mlir::arith::FastMathAttr. 593 mlir::arith::FastMathFlags fastMathFlags{}; 594 595 /// IntegerOverflowFlags that need to be set for operations that support 596 /// mlir::arith::IntegerOverflowFlagsAttr. 597 mlir::arith::IntegerOverflowFlags integerOverflowFlags{}; 598 599 /// fir::GlobalOp and func::FuncOp symbol table to speed-up 600 /// lookups. 601 mlir::SymbolTable *symbolTable = nullptr; 602 603 /// DataLayout constructed on demand. Access via getDataLayout(). 604 /// Stored via a unique_ptr rather than an optional so as not to bloat this 605 /// class when most instances won't ever need a data layout. 606 std::unique_ptr<mlir::DataLayout> dataLayout = nullptr; 607 }; 608 609 } // namespace fir 610 611 namespace fir::factory { 612 613 //===----------------------------------------------------------------------===// 614 // ExtendedValue inquiry helpers 615 //===----------------------------------------------------------------------===// 616 617 /// Read or get character length from \p box that must contain a character 618 /// entity. If the length value is contained in the ExtendedValue, this will 619 /// not generate any code, otherwise this will generate a read of the fir.box 620 /// describing the entity. 621 mlir::Value readCharLen(fir::FirOpBuilder &builder, mlir::Location loc, 622 const fir::ExtendedValue &box); 623 624 /// Read or get the extent in dimension \p dim of the array described by \p box. 625 mlir::Value readExtent(fir::FirOpBuilder &builder, mlir::Location loc, 626 const fir::ExtendedValue &box, unsigned dim); 627 628 /// Read or get the lower bound in dimension \p dim of the array described by 629 /// \p box. If the lower bound is left default in the ExtendedValue, 630 /// \p defaultValue will be returned. 631 mlir::Value readLowerBound(fir::FirOpBuilder &builder, mlir::Location loc, 632 const fir::ExtendedValue &box, unsigned dim, 633 mlir::Value defaultValue); 634 635 /// Read extents from \p box. 636 llvm::SmallVector<mlir::Value> readExtents(fir::FirOpBuilder &builder, 637 mlir::Location loc, 638 const fir::BoxValue &box); 639 640 /// Read a fir::BoxValue into an fir::UnboxValue, a fir::ArrayBoxValue or a 641 /// fir::CharArrayBoxValue. This should only be called if the fir::BoxValue is 642 /// known to be contiguous given the context (or if the resulting address will 643 /// not be used). If the value is polymorphic, its dynamic type will be lost. 644 /// This must not be used on unlimited polymorphic and assumed rank entities. 645 fir::ExtendedValue readBoxValue(fir::FirOpBuilder &builder, mlir::Location loc, 646 const fir::BoxValue &box); 647 648 /// Get the lower bounds of \p exv. NB: returns an empty vector if the lower 649 /// bounds are all ones, which is the default in Fortran. 650 llvm::SmallVector<mlir::Value> 651 getNonDefaultLowerBounds(fir::FirOpBuilder &builder, mlir::Location loc, 652 const fir::ExtendedValue &exv); 653 654 /// Return LEN parameters associated to \p exv that are not deferred (that are 655 /// available without having to read any fir.box values). Empty if \p exv has no 656 /// LEN parameters or if they are all deferred. 657 llvm::SmallVector<mlir::Value> 658 getNonDeferredLenParams(const fir::ExtendedValue &exv); 659 660 //===----------------------------------------------------------------------===// 661 // String literal helper helpers 662 //===----------------------------------------------------------------------===// 663 664 /// Create a !fir.char<1> string literal global and returns a fir::CharBoxValue 665 /// with its address and length. 666 fir::ExtendedValue createStringLiteral(fir::FirOpBuilder &, mlir::Location, 667 llvm::StringRef string); 668 669 /// Unique a compiler generated identifier. A short prefix should be provided 670 /// to hint at the origin of the identifier. 671 std::string uniqueCGIdent(llvm::StringRef prefix, llvm::StringRef name); 672 673 /// Lowers the extents from the sequence type to Values. 674 /// Any unknown extents are lowered to undefined values. 675 llvm::SmallVector<mlir::Value> createExtents(fir::FirOpBuilder &builder, 676 mlir::Location loc, 677 fir::SequenceType seqTy); 678 679 //===--------------------------------------------------------------------===// 680 // Location helpers 681 //===--------------------------------------------------------------------===// 682 683 /// Generate a string literal containing the file name and return its address 684 mlir::Value locationToFilename(fir::FirOpBuilder &, mlir::Location); 685 /// Generate a constant of the given type with the location line number 686 mlir::Value locationToLineNo(fir::FirOpBuilder &, mlir::Location, mlir::Type); 687 688 //===--------------------------------------------------------------------===// 689 // ExtendedValue helpers 690 //===--------------------------------------------------------------------===// 691 692 /// Return the extended value for a component of a derived type instance given 693 /// the address of the component. 694 fir::ExtendedValue componentToExtendedValue(fir::FirOpBuilder &builder, 695 mlir::Location loc, 696 mlir::Value component); 697 698 /// Given the address of an array element and the ExtendedValue describing the 699 /// array, returns the ExtendedValue describing the array element. The purpose 700 /// is to propagate the LEN parameters of the array to the element. This can be 701 /// used for elements of `array` or `array(i:j:k)`. If \p element belongs to an 702 /// array section `array%x` whose base is \p array, 703 /// arraySectionElementToExtendedValue must be used instead. 704 fir::ExtendedValue arrayElementToExtendedValue(fir::FirOpBuilder &builder, 705 mlir::Location loc, 706 const fir::ExtendedValue &array, 707 mlir::Value element); 708 709 /// Build the ExtendedValue for \p element that is an element of an array or 710 /// array section with \p array base (`array` or `array(i:j:k)%x%y`). 711 /// If it is an array section, \p slice must be provided and be a fir::SliceOp 712 /// that describes the section. 713 fir::ExtendedValue arraySectionElementToExtendedValue( 714 fir::FirOpBuilder &builder, mlir::Location loc, 715 const fir::ExtendedValue &array, mlir::Value element, mlir::Value slice); 716 717 /// Assign \p rhs to \p lhs. Both \p rhs and \p lhs must be scalars. The 718 /// assignment follows Fortran intrinsic assignment semantic (10.2.1.3). 719 void genScalarAssignment(fir::FirOpBuilder &builder, mlir::Location loc, 720 const fir::ExtendedValue &lhs, 721 const fir::ExtendedValue &rhs, 722 bool needFinalization = false, 723 bool isTemporaryLHS = false); 724 725 /// Assign \p rhs to \p lhs. Both \p rhs and \p lhs must be scalar derived 726 /// types. The assignment follows Fortran intrinsic assignment semantic for 727 /// derived types (10.2.1.3 point 13). 728 void genRecordAssignment(fir::FirOpBuilder &builder, mlir::Location loc, 729 const fir::ExtendedValue &lhs, 730 const fir::ExtendedValue &rhs, 731 bool needFinalization = false, 732 bool isTemporaryLHS = false); 733 734 /// Builds and returns the type of a ragged array header used to cache mask 735 /// evaluations. RaggedArrayHeader is defined in 736 /// flang/include/flang/Runtime/ragged.h. 737 mlir::TupleType getRaggedArrayHeaderType(fir::FirOpBuilder &builder); 738 739 /// Generate the, possibly dynamic, LEN of a CHARACTER. \p arrLoad determines 740 /// the base array. After applying \p path, the result must be a reference to a 741 /// `!fir.char` type object. \p substring must have 0, 1, or 2 members. The 742 /// first member is the starting offset. The second is the ending offset. 743 mlir::Value genLenOfCharacter(fir::FirOpBuilder &builder, mlir::Location loc, 744 fir::ArrayLoadOp arrLoad, 745 llvm::ArrayRef<mlir::Value> path, 746 llvm::ArrayRef<mlir::Value> substring); 747 mlir::Value genLenOfCharacter(fir::FirOpBuilder &builder, mlir::Location loc, 748 fir::SequenceType seqTy, mlir::Value memref, 749 llvm::ArrayRef<mlir::Value> typeParams, 750 llvm::ArrayRef<mlir::Value> path, 751 llvm::ArrayRef<mlir::Value> substring); 752 753 /// Create the zero value of a given the numerical or logical \p type (`false` 754 /// for logical types). 755 mlir::Value createZeroValue(fir::FirOpBuilder &builder, mlir::Location loc, 756 mlir::Type type); 757 758 /// Get the integer constants of triplet and compute the extent. 759 std::optional<std::int64_t> getExtentFromTriplet(mlir::Value lb, mlir::Value ub, 760 mlir::Value stride); 761 762 /// Generate max(\p value, 0) where \p value is a scalar integer. 763 mlir::Value genMaxWithZero(fir::FirOpBuilder &builder, mlir::Location loc, 764 mlir::Value value); 765 766 /// The type(C_PTR/C_FUNPTR) is defined as the derived type with only one 767 /// component of integer 64, and the component is the C address. Get the C 768 /// address. 769 mlir::Value genCPtrOrCFunptrAddr(fir::FirOpBuilder &builder, mlir::Location loc, 770 mlir::Value cPtr, mlir::Type ty); 771 772 /// The type(C_DEVPTR) is defined as the derived type with only one 773 /// component of C_PTR type. Get the C address from the C_PTR component. 774 mlir::Value genCDevPtrAddr(fir::FirOpBuilder &builder, mlir::Location loc, 775 mlir::Value cDevPtr, mlir::Type ty); 776 777 /// Get the C address value. 778 mlir::Value genCPtrOrCFunptrValue(fir::FirOpBuilder &builder, 779 mlir::Location loc, mlir::Value cPtr); 780 781 /// Create a fir.box from a fir::ExtendedValue and wrap it in a fir::BoxValue 782 /// to keep all the lower bound and explicit parameter information. 783 fir::BoxValue createBoxValue(fir::FirOpBuilder &builder, mlir::Location loc, 784 const fir::ExtendedValue &exv); 785 786 /// Generate Null BoxProc for procedure pointer null initialization. 787 mlir::Value createNullBoxProc(fir::FirOpBuilder &builder, mlir::Location loc, 788 mlir::Type boxType); 789 790 /// Convert a value to a new type. Return the value directly if it has the right 791 /// type. 792 mlir::Value createConvert(mlir::OpBuilder &, mlir::Location, mlir::Type, 793 mlir::Value); 794 795 /// Set internal linkage attribute on a function. 796 void setInternalLinkage(mlir::func::FuncOp); 797 798 llvm::SmallVector<mlir::Value> 799 elideExtentsAlreadyInType(mlir::Type type, mlir::ValueRange shape); 800 801 llvm::SmallVector<mlir::Value> 802 elideLengthsAlreadyInType(mlir::Type type, mlir::ValueRange lenParams); 803 804 /// Get the address space which should be used for allocas 805 uint64_t getAllocaAddressSpace(mlir::DataLayout *dataLayout); 806 807 /// The two vectors of MLIR values have the following property: 808 /// \p extents1[i] must have the same value as \p extents2[i] 809 /// The function returns a new vector of MLIR values that preserves 810 /// the same property vs \p extents1 and \p extents2, but allows 811 /// more optimizations. For example, if extents1[j] is a known constant, 812 /// and extents2[j] is not, then result[j] is the MLIR value extents1[j]. 813 llvm::SmallVector<mlir::Value> deduceOptimalExtents(mlir::ValueRange extents1, 814 mlir::ValueRange extents2); 815 816 /// Given array extents generate code that sets them all to zeroes, 817 /// if the array is empty, e.g.: 818 /// %false = arith.constant false 819 /// %c0 = arith.constant 0 : index 820 /// %p1 = arith.cmpi eq, %e0, %c0 : index 821 /// %p2 = arith.ori %false, %p1 : i1 822 /// %p3 = arith.cmpi eq, %e1, %c0 : index 823 /// %p4 = arith.ori %p1, %p2 : i1 824 /// %result0 = arith.select %p4, %c0, %e0 : index 825 /// %result1 = arith.select %p4, %c0, %e1 : index 826 llvm::SmallVector<mlir::Value> updateRuntimeExtentsForEmptyArrays( 827 fir::FirOpBuilder &builder, mlir::Location loc, mlir::ValueRange extents); 828 } // namespace fir::factory 829 830 #endif // FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H 831