1 //===-- ConvertExpr.cpp ---------------------------------------------------===// 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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "flang/Lower/ConvertExpr.h" 14 #include "flang/Evaluate/fold.h" 15 #include "flang/Evaluate/traverse.h" 16 #include "flang/Lower/AbstractConverter.h" 17 #include "flang/Lower/Allocatable.h" 18 #include "flang/Lower/BuiltinModules.h" 19 #include "flang/Lower/CallInterface.h" 20 #include "flang/Lower/ComponentPath.h" 21 #include "flang/Lower/ConvertType.h" 22 #include "flang/Lower/ConvertVariable.h" 23 #include "flang/Lower/CustomIntrinsicCall.h" 24 #include "flang/Lower/DumpEvaluateExpr.h" 25 #include "flang/Lower/IntrinsicCall.h" 26 #include "flang/Lower/Mangler.h" 27 #include "flang/Lower/StatementContext.h" 28 #include "flang/Lower/SymbolMap.h" 29 #include "flang/Lower/Todo.h" 30 #include "flang/Optimizer/Builder/Character.h" 31 #include "flang/Optimizer/Builder/Complex.h" 32 #include "flang/Optimizer/Builder/Factory.h" 33 #include "flang/Optimizer/Builder/LowLevelIntrinsics.h" 34 #include "flang/Optimizer/Builder/MutableBox.h" 35 #include "flang/Optimizer/Builder/Runtime/Character.h" 36 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h" 37 #include "flang/Optimizer/Builder/Runtime/Ragged.h" 38 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 39 #include "flang/Optimizer/Support/Matcher.h" 40 #include "flang/Semantics/expression.h" 41 #include "flang/Semantics/symbol.h" 42 #include "flang/Semantics/tools.h" 43 #include "flang/Semantics/type.h" 44 #include "mlir/Dialect/Func/IR/FuncOps.h" 45 #include "llvm/Support/CommandLine.h" 46 #include "llvm/Support/Debug.h" 47 48 #define DEBUG_TYPE "flang-lower-expr" 49 50 //===----------------------------------------------------------------------===// 51 // The composition and structure of Fortran::evaluate::Expr is defined in 52 // the various header files in include/flang/Evaluate. You are referred 53 // there for more information on these data structures. Generally speaking, 54 // these data structures are a strongly typed family of abstract data types 55 // that, composed as trees, describe the syntax of Fortran expressions. 56 // 57 // This part of the bridge can traverse these tree structures and lower them 58 // to the correct FIR representation in SSA form. 59 //===----------------------------------------------------------------------===// 60 61 static llvm::cl::opt<bool> generateArrayCoordinate( 62 "gen-array-coor", 63 llvm::cl::desc("in lowering create ArrayCoorOp instead of CoordinateOp"), 64 llvm::cl::init(false)); 65 66 // The default attempts to balance a modest allocation size with expected user 67 // input to minimize bounds checks and reallocations during dynamic array 68 // construction. Some user codes may have very large array constructors for 69 // which the default can be increased. 70 static llvm::cl::opt<unsigned> clInitialBufferSize( 71 "array-constructor-initial-buffer-size", 72 llvm::cl::desc( 73 "set the incremental array construction buffer size (default=32)"), 74 llvm::cl::init(32u)); 75 76 /// The various semantics of a program constituent (or a part thereof) as it may 77 /// appear in an expression. 78 /// 79 /// Given the following Fortran declarations. 80 /// ```fortran 81 /// REAL :: v1, v2, v3 82 /// REAL, POINTER :: vp1 83 /// REAL :: a1(c), a2(c) 84 /// REAL ELEMENTAL FUNCTION f1(arg) ! array -> array 85 /// FUNCTION f2(arg) ! array -> array 86 /// vp1 => v3 ! 1 87 /// v1 = v2 * vp1 ! 2 88 /// a1 = a1 + a2 ! 3 89 /// a1 = f1(a2) ! 4 90 /// a1 = f2(a2) ! 5 91 /// ``` 92 /// 93 /// In line 1, `vp1` is a BoxAddr to copy a box value into. The box value is 94 /// constructed from the DataAddr of `v3`. 95 /// In line 2, `v1` is a DataAddr to copy a value into. The value is constructed 96 /// from the DataValue of `v2` and `vp1`. DataValue is implicitly a double 97 /// dereference in the `vp1` case. 98 /// In line 3, `a1` and `a2` on the rhs are RefTransparent. The `a1` on the lhs 99 /// is CopyInCopyOut as `a1` is replaced elementally by the additions. 100 /// In line 4, `a2` can be RefTransparent, ByValueArg, RefOpaque, or BoxAddr if 101 /// `arg` is declared as C-like pass-by-value, VALUE, INTENT(?), or ALLOCATABLE/ 102 /// POINTER, respectively. `a1` on the lhs is CopyInCopyOut. 103 /// In line 5, `a2` may be DataAddr or BoxAddr assuming f2 is transformational. 104 /// `a1` on the lhs is again CopyInCopyOut. 105 enum class ConstituentSemantics { 106 // Scalar data reference semantics. 107 // 108 // For these let `v` be the location in memory of a variable with value `x` 109 DataValue, // refers to the value `x` 110 DataAddr, // refers to the address `v` 111 BoxValue, // refers to a box value containing `v` 112 BoxAddr, // refers to the address of a box value containing `v` 113 114 // Array data reference semantics. 115 // 116 // For these let `a` be the location in memory of a sequence of value `[xs]`. 117 // Let `x_i` be the `i`-th value in the sequence `[xs]`. 118 119 // Referentially transparent. Refers to the array's value, `[xs]`. 120 RefTransparent, 121 // Refers to an ephemeral address `tmp` containing value `x_i` (15.5.2.3.p7 122 // note 2). (Passing a copy by reference to simulate pass-by-value.) 123 ByValueArg, 124 // Refers to the merge of array value `[xs]` with another array value `[ys]`. 125 // This merged array value will be written into memory location `a`. 126 CopyInCopyOut, 127 // Similar to CopyInCopyOut but `a` may be a transient projection (rather than 128 // a whole array). 129 ProjectedCopyInCopyOut, 130 // Similar to ProjectedCopyInCopyOut, except the merge value is not assigned 131 // automatically by the framework. Instead, and address for `[xs]` is made 132 // accessible so that custom assignments to `[xs]` can be implemented. 133 CustomCopyInCopyOut, 134 // Referentially opaque. Refers to the address of `x_i`. 135 RefOpaque 136 }; 137 138 /// Convert parser's INTEGER relational operators to MLIR. TODO: using 139 /// unordered, but we may want to cons ordered in certain situation. 140 static mlir::arith::CmpIPredicate 141 translateRelational(Fortran::common::RelationalOperator rop) { 142 switch (rop) { 143 case Fortran::common::RelationalOperator::LT: 144 return mlir::arith::CmpIPredicate::slt; 145 case Fortran::common::RelationalOperator::LE: 146 return mlir::arith::CmpIPredicate::sle; 147 case Fortran::common::RelationalOperator::EQ: 148 return mlir::arith::CmpIPredicate::eq; 149 case Fortran::common::RelationalOperator::NE: 150 return mlir::arith::CmpIPredicate::ne; 151 case Fortran::common::RelationalOperator::GT: 152 return mlir::arith::CmpIPredicate::sgt; 153 case Fortran::common::RelationalOperator::GE: 154 return mlir::arith::CmpIPredicate::sge; 155 } 156 llvm_unreachable("unhandled INTEGER relational operator"); 157 } 158 159 /// Convert parser's REAL relational operators to MLIR. 160 /// The choice of order (O prefix) vs unorder (U prefix) follows Fortran 2018 161 /// requirements in the IEEE context (table 17.1 of F2018). This choice is 162 /// also applied in other contexts because it is easier and in line with 163 /// other Fortran compilers. 164 /// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not 165 /// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee 166 /// whether the comparison will signal or not in case of quiet NaN argument. 167 static mlir::arith::CmpFPredicate 168 translateFloatRelational(Fortran::common::RelationalOperator rop) { 169 switch (rop) { 170 case Fortran::common::RelationalOperator::LT: 171 return mlir::arith::CmpFPredicate::OLT; 172 case Fortran::common::RelationalOperator::LE: 173 return mlir::arith::CmpFPredicate::OLE; 174 case Fortran::common::RelationalOperator::EQ: 175 return mlir::arith::CmpFPredicate::OEQ; 176 case Fortran::common::RelationalOperator::NE: 177 return mlir::arith::CmpFPredicate::UNE; 178 case Fortran::common::RelationalOperator::GT: 179 return mlir::arith::CmpFPredicate::OGT; 180 case Fortran::common::RelationalOperator::GE: 181 return mlir::arith::CmpFPredicate::OGE; 182 } 183 llvm_unreachable("unhandled REAL relational operator"); 184 } 185 186 static mlir::Value genActualIsPresentTest(fir::FirOpBuilder &builder, 187 mlir::Location loc, 188 fir::ExtendedValue actual) { 189 if (const auto *ptrOrAlloc = actual.getBoxOf<fir::MutableBoxValue>()) 190 return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, 191 *ptrOrAlloc); 192 // Optional case (not that optional allocatable/pointer cannot be absent 193 // when passed to CMPLX as per 15.5.2.12 point 3 (7) and (8)). It is 194 // therefore possible to catch them in the `then` case above. 195 return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), 196 fir::getBase(actual)); 197 } 198 199 /// Convert the array_load, `load`, to an extended value. If `path` is not 200 /// empty, then traverse through the components designated. The base value is 201 /// `newBase`. This does not accept an array_load with a slice operand. 202 static fir::ExtendedValue 203 arrayLoadExtValue(fir::FirOpBuilder &builder, mlir::Location loc, 204 fir::ArrayLoadOp load, llvm::ArrayRef<mlir::Value> path, 205 mlir::Value newBase, mlir::Value newLen = {}) { 206 // Recover the extended value from the load. 207 assert(!load.getSlice() && "slice is not allowed"); 208 mlir::Type arrTy = load.getType(); 209 if (!path.empty()) { 210 mlir::Type ty = fir::applyPathToType(arrTy, path); 211 if (!ty) 212 fir::emitFatalError(loc, "path does not apply to type"); 213 if (!ty.isa<fir::SequenceType>()) { 214 if (fir::isa_char(ty)) { 215 mlir::Value len = newLen; 216 if (!len) 217 len = fir::factory::CharacterExprHelper{builder, loc}.getLength( 218 load.getMemref()); 219 if (!len) { 220 assert(load.getTypeparams().size() == 1 && 221 "length must be in array_load"); 222 len = load.getTypeparams()[0]; 223 } 224 return fir::CharBoxValue{newBase, len}; 225 } 226 return newBase; 227 } 228 arrTy = ty.cast<fir::SequenceType>(); 229 } 230 231 // Use the shape op, if there is one. 232 mlir::Value shapeVal = load.getShape(); 233 if (shapeVal) { 234 if (!mlir::isa<fir::ShiftOp>(shapeVal.getDefiningOp())) { 235 mlir::Type eleTy = fir::unwrapSequenceType(arrTy); 236 std::vector<mlir::Value> extents = fir::factory::getExtents(shapeVal); 237 std::vector<mlir::Value> origins = fir::factory::getOrigins(shapeVal); 238 if (fir::isa_char(eleTy)) { 239 mlir::Value len = newLen; 240 if (!len) 241 len = fir::factory::CharacterExprHelper{builder, loc}.getLength( 242 load.getMemref()); 243 if (!len) { 244 assert(load.getTypeparams().size() == 1 && 245 "length must be in array_load"); 246 len = load.getTypeparams()[0]; 247 } 248 return fir::CharArrayBoxValue(newBase, len, extents, origins); 249 } 250 return fir::ArrayBoxValue(newBase, extents, origins); 251 } 252 if (!fir::isa_box_type(load.getMemref().getType())) 253 fir::emitFatalError(loc, "shift op is invalid in this context"); 254 } 255 256 // There is no shape or the array is in a box. Extents and lower bounds must 257 // be read at runtime. 258 if (path.empty() && !shapeVal) { 259 fir::ExtendedValue exv = 260 fir::factory::readBoxValue(builder, loc, load.getMemref()); 261 return fir::substBase(exv, newBase); 262 } 263 TODO(loc, "component is boxed, retreive its type parameters"); 264 } 265 266 /// Place \p exv in memory if it is not already a memory reference. If 267 /// \p forceValueType is provided, the value is first casted to the provided 268 /// type before being stored (this is mainly intended for logicals whose value 269 /// may be `i1` but needed to be stored as Fortran logicals). 270 static fir::ExtendedValue 271 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc, 272 const fir::ExtendedValue &exv, 273 mlir::Type storageType) { 274 mlir::Value valBase = fir::getBase(exv); 275 if (fir::conformsWithPassByRef(valBase.getType())) 276 return exv; 277 278 assert(!fir::hasDynamicSize(storageType) && 279 "only expect statically sized scalars to be by value"); 280 281 // Since `a` is not itself a valid referent, determine its value and 282 // create a temporary location at the beginning of the function for 283 // referencing. 284 mlir::Value val = builder.createConvert(loc, storageType, valBase); 285 mlir::Value temp = builder.createTemporary( 286 loc, storageType, 287 llvm::ArrayRef<mlir::NamedAttribute>{ 288 Fortran::lower::getAdaptToByRefAttr(builder)}); 289 builder.create<fir::StoreOp>(loc, val, temp); 290 return fir::substBase(exv, temp); 291 } 292 293 // Copy a copy of scalar \p exv in a new temporary. 294 static fir::ExtendedValue 295 createInMemoryScalarCopy(fir::FirOpBuilder &builder, mlir::Location loc, 296 const fir::ExtendedValue &exv) { 297 assert(exv.rank() == 0 && "input to scalar memory copy must be a scalar"); 298 if (exv.getCharBox() != nullptr) 299 return fir::factory::CharacterExprHelper{builder, loc}.createTempFrom(exv); 300 if (fir::isDerivedWithLengthParameters(exv)) 301 TODO(loc, "copy derived type with length parameters"); 302 mlir::Type type = fir::unwrapPassByRefType(fir::getBase(exv).getType()); 303 fir::ExtendedValue temp = builder.createTemporary(loc, type); 304 fir::factory::genScalarAssignment(builder, loc, temp, exv); 305 return temp; 306 } 307 308 // An expression with non-zero rank is an array expression. 309 template <typename A> 310 static bool isArray(const A &x) { 311 return x.Rank() != 0; 312 } 313 314 /// Is this a variable wrapped in parentheses? 315 template <typename A> 316 static bool isParenthesizedVariable(const A &) { 317 return false; 318 } 319 template <typename T> 320 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) { 321 using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u); 322 using Parentheses = Fortran::evaluate::Parentheses<T>; 323 if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) { 324 if (const auto *parentheses = std::get_if<Parentheses>(&expr.u)) 325 return Fortran::evaluate::IsVariable(parentheses->left()); 326 return false; 327 } else { 328 return std::visit([&](const auto &x) { return isParenthesizedVariable(x); }, 329 expr.u); 330 } 331 } 332 333 /// Does \p expr only refer to symbols that are mapped to IR values in \p symMap 334 /// ? 335 static bool allSymbolsInExprPresentInMap(const Fortran::lower::SomeExpr &expr, 336 Fortran::lower::SymMap &symMap) { 337 for (const auto &sym : Fortran::evaluate::CollectSymbols(expr)) 338 if (!symMap.lookupSymbol(sym)) 339 return false; 340 return true; 341 } 342 343 /// Generate a load of a value from an address. Beware that this will lose 344 /// any dynamic type information for polymorphic entities (note that unlimited 345 /// polymorphic cannot be loaded and must not be provided here). 346 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder, 347 mlir::Location loc, 348 const fir::ExtendedValue &addr) { 349 return addr.match( 350 [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; }, 351 [&](const fir::UnboxedValue &v) -> fir::ExtendedValue { 352 if (fir::unwrapRefType(fir::getBase(v).getType()) 353 .isa<fir::RecordType>()) 354 return v; 355 return builder.create<fir::LoadOp>(loc, fir::getBase(v)); 356 }, 357 [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue { 358 return genLoad(builder, loc, 359 fir::factory::genMutableBoxRead(builder, loc, box)); 360 }, 361 [&](const fir::BoxValue &box) -> fir::ExtendedValue { 362 if (box.isUnlimitedPolymorphic()) 363 fir::emitFatalError( 364 loc, 365 "lowering attempting to load an unlimited polymorphic entity"); 366 return genLoad(builder, loc, 367 fir::factory::readBoxValue(builder, loc, box)); 368 }, 369 [&](const auto &) -> fir::ExtendedValue { 370 fir::emitFatalError( 371 loc, "attempting to load whole array or procedure address"); 372 }); 373 } 374 375 /// Create an optional dummy argument value from entity \p exv that may be 376 /// absent. This can only be called with numerical or logical scalar \p exv. 377 /// If \p exv is considered absent according to 15.5.2.12 point 1., the returned 378 /// value is zero (or false), otherwise it is the value of \p exv. 379 static fir::ExtendedValue genOptionalValue(fir::FirOpBuilder &builder, 380 mlir::Location loc, 381 const fir::ExtendedValue &exv, 382 mlir::Value isPresent) { 383 mlir::Type eleType = fir::getBaseTypeOf(exv); 384 assert(exv.rank() == 0 && fir::isa_trivial(eleType) && 385 "must be a numerical or logical scalar"); 386 return builder 387 .genIfOp(loc, {eleType}, isPresent, 388 /*withElseRegion=*/true) 389 .genThen([&]() { 390 mlir::Value val = fir::getBase(genLoad(builder, loc, exv)); 391 builder.create<fir::ResultOp>(loc, val); 392 }) 393 .genElse([&]() { 394 mlir::Value zero = fir::factory::createZeroValue(builder, loc, eleType); 395 builder.create<fir::ResultOp>(loc, zero); 396 }) 397 .getResults()[0]; 398 } 399 400 /// Create an optional dummy argument address from entity \p exv that may be 401 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the 402 /// returned value is a null pointer, otherwise it is the address of \p exv. 403 static fir::ExtendedValue genOptionalAddr(fir::FirOpBuilder &builder, 404 mlir::Location loc, 405 const fir::ExtendedValue &exv, 406 mlir::Value isPresent) { 407 // If it is an exv pointer/allocatable, then it cannot be absent 408 // because it is passed to a non-pointer/non-allocatable. 409 if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>()) 410 return fir::factory::genMutableBoxRead(builder, loc, *box); 411 // If this is not a POINTER or ALLOCATABLE, then it is already an OPTIONAL 412 // address and can be passed directly. 413 return exv; 414 } 415 416 /// Create an optional dummy argument address from entity \p exv that may be 417 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the 418 /// returned value is an absent fir.box, otherwise it is a fir.box describing \p 419 /// exv. 420 static fir::ExtendedValue genOptionalBox(fir::FirOpBuilder &builder, 421 mlir::Location loc, 422 const fir::ExtendedValue &exv, 423 mlir::Value isPresent) { 424 // Non allocatable/pointer optional box -> simply forward 425 if (exv.getBoxOf<fir::BoxValue>()) 426 return exv; 427 428 fir::ExtendedValue newExv = exv; 429 // Optional allocatable/pointer -> Cannot be absent, but need to translate 430 // unallocated/diassociated into absent fir.box. 431 if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>()) 432 newExv = fir::factory::genMutableBoxRead(builder, loc, *box); 433 434 // createBox will not do create any invalid memory dereferences if exv is 435 // absent. The created fir.box will not be usable, but the SelectOp below 436 // ensures it won't be. 437 mlir::Value box = builder.createBox(loc, newExv); 438 mlir::Type boxType = box.getType(); 439 auto absent = builder.create<fir::AbsentOp>(loc, boxType); 440 auto boxOrAbsent = builder.create<mlir::arith::SelectOp>( 441 loc, boxType, isPresent, box, absent); 442 return fir::BoxValue(boxOrAbsent); 443 } 444 445 /// Is this a call to an elemental procedure with at least one array argument? 446 static bool 447 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) { 448 if (procRef.IsElemental()) 449 for (const std::optional<Fortran::evaluate::ActualArgument> &arg : 450 procRef.arguments()) 451 if (arg && arg->Rank() != 0) 452 return true; 453 return false; 454 } 455 template <typename T> 456 static bool isElementalProcWithArrayArgs(const Fortran::evaluate::Expr<T> &) { 457 return false; 458 } 459 template <> 460 bool isElementalProcWithArrayArgs(const Fortran::lower::SomeExpr &x) { 461 if (const auto *procRef = std::get_if<Fortran::evaluate::ProcedureRef>(&x.u)) 462 return isElementalProcWithArrayArgs(*procRef); 463 return false; 464 } 465 466 /// Some auxiliary data for processing initialization in ScalarExprLowering 467 /// below. This is currently used for generating dense attributed global 468 /// arrays. 469 struct InitializerData { 470 explicit InitializerData(bool getRawVals = false) : genRawVals{getRawVals} {} 471 llvm::SmallVector<mlir::Attribute> rawVals; // initialization raw values 472 mlir::Type rawType; // Type of elements processed for rawVals vector. 473 bool genRawVals; // generate the rawVals vector if set. 474 }; 475 476 /// If \p arg is the address of a function with a denoted host-association tuple 477 /// argument, then return the host-associations tuple value of the current 478 /// procedure. Otherwise, return nullptr. 479 static mlir::Value 480 argumentHostAssocs(Fortran::lower::AbstractConverter &converter, 481 mlir::Value arg) { 482 if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) { 483 auto &builder = converter.getFirOpBuilder(); 484 if (auto funcOp = builder.getNamedFunction(addr.getSymbol())) 485 if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName())) 486 return converter.hostAssocTupleValue(); 487 } 488 return {}; 489 } 490 491 /// \p argTy must be a tuple (pair) of boxproc and integral types. Convert the 492 /// \p funcAddr argument to a boxproc value, with the host-association as 493 /// required. Call the factory function to finish creating the tuple value. 494 static mlir::Value 495 createBoxProcCharTuple(Fortran::lower::AbstractConverter &converter, 496 mlir::Type argTy, mlir::Value funcAddr, 497 mlir::Value charLen) { 498 auto boxTy = 499 argTy.cast<mlir::TupleType>().getType(0).cast<fir::BoxProcType>(); 500 mlir::Location loc = converter.getCurrentLocation(); 501 auto &builder = converter.getFirOpBuilder(); 502 auto boxProc = [&]() -> mlir::Value { 503 if (auto host = argumentHostAssocs(converter, funcAddr)) 504 return builder.create<fir::EmboxProcOp>( 505 loc, boxTy, llvm::ArrayRef<mlir::Value>{funcAddr, host}); 506 return builder.create<fir::EmboxProcOp>(loc, boxTy, funcAddr); 507 }(); 508 return fir::factory::createCharacterProcedureTuple(builder, loc, argTy, 509 boxProc, charLen); 510 } 511 512 // Helper to get the ultimate first symbol. This works around the fact that 513 // symbol resolution in the front end doesn't always resolve a symbol to its 514 // ultimate symbol but may leave placeholder indirections for use and host 515 // associations. 516 template <typename A> 517 const Fortran::semantics::Symbol &getFirstSym(const A &obj) { 518 return obj.GetFirstSymbol().GetUltimate(); 519 } 520 521 // Helper to get the ultimate last symbol. 522 template <typename A> 523 const Fortran::semantics::Symbol &getLastSym(const A &obj) { 524 return obj.GetLastSymbol().GetUltimate(); 525 } 526 527 namespace { 528 529 /// Lowering of Fortran::evaluate::Expr<T> expressions 530 class ScalarExprLowering { 531 public: 532 using ExtValue = fir::ExtendedValue; 533 534 explicit ScalarExprLowering(mlir::Location loc, 535 Fortran::lower::AbstractConverter &converter, 536 Fortran::lower::SymMap &symMap, 537 Fortran::lower::StatementContext &stmtCtx, 538 InitializerData *initializer = nullptr) 539 : location{loc}, converter{converter}, 540 builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap}, 541 inInitializer{initializer} {} 542 543 ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) { 544 return gen(expr); 545 } 546 547 /// Lower `expr` to be passed as a fir.box argument. Do not create a temp 548 /// for the expr if it is a variable that can be described as a fir.box. 549 ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) { 550 bool saveUseBoxArg = useBoxArg; 551 useBoxArg = true; 552 ExtValue result = gen(expr); 553 useBoxArg = saveUseBoxArg; 554 return result; 555 } 556 557 ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) { 558 return genval(expr); 559 } 560 561 /// Lower an expression that is a pointer or an allocatable to a 562 /// MutableBoxValue. 563 fir::MutableBoxValue 564 genMutableBoxValue(const Fortran::lower::SomeExpr &expr) { 565 // Pointers and allocatables can only be: 566 // - a simple designator "x" 567 // - a component designator "a%b(i,j)%x" 568 // - a function reference "foo()" 569 // - result of NULL() or NULL(MOLD) intrinsic. 570 // NULL() requires some context to be lowered, so it is not handled 571 // here and must be lowered according to the context where it appears. 572 ExtValue exv = std::visit( 573 [&](const auto &x) { return genMutableBoxValueImpl(x); }, expr.u); 574 const fir::MutableBoxValue *mutableBox = 575 exv.getBoxOf<fir::MutableBoxValue>(); 576 if (!mutableBox) 577 fir::emitFatalError(getLoc(), "expr was not lowered to MutableBoxValue"); 578 return *mutableBox; 579 } 580 581 template <typename T> 582 ExtValue genMutableBoxValueImpl(const T &) { 583 // NULL() case should not be handled here. 584 fir::emitFatalError(getLoc(), "NULL() must be lowered in its context"); 585 } 586 587 /// A `NULL()` in a position where a mutable box is expected has the same 588 /// semantics as an absent optional box value. 589 ExtValue genMutableBoxValueImpl(const Fortran::evaluate::NullPointer &) { 590 mlir::Location loc = getLoc(); 591 auto nullConst = builder.createNullConstant(loc); 592 auto noneTy = mlir::NoneType::get(builder.getContext()); 593 auto polyRefTy = fir::LLVMPointerType::get(noneTy); 594 // MutableBoxValue will dereference the box, so create a bogus temporary for 595 // the `nullptr`. The LLVM optimizer will garbage collect the temp. 596 auto temp = 597 builder.createTemporary(loc, polyRefTy, /*shape=*/mlir::ValueRange{}); 598 auto nullPtr = builder.createConvert(loc, polyRefTy, nullConst); 599 builder.create<fir::StoreOp>(loc, nullPtr, temp); 600 auto nullBoxTy = builder.getRefType(fir::BoxType::get(noneTy)); 601 return fir::MutableBoxValue(builder.createConvert(loc, nullBoxTy, temp), 602 /*lenParameters=*/mlir::ValueRange{}, 603 /*mutableProperties=*/{}); 604 } 605 606 template <typename T> 607 ExtValue 608 genMutableBoxValueImpl(const Fortran::evaluate::FunctionRef<T> &funRef) { 609 return genRawProcedureRef(funRef, converter.genType(toEvExpr(funRef))); 610 } 611 612 template <typename T> 613 ExtValue 614 genMutableBoxValueImpl(const Fortran::evaluate::Designator<T> &designator) { 615 return std::visit( 616 Fortran::common::visitors{ 617 [&](const Fortran::evaluate::SymbolRef &sym) -> ExtValue { 618 return symMap.lookupSymbol(*sym).toExtendedValue(); 619 }, 620 [&](const Fortran::evaluate::Component &comp) -> ExtValue { 621 return genComponent(comp); 622 }, 623 [&](const auto &) -> ExtValue { 624 fir::emitFatalError(getLoc(), 625 "not an allocatable or pointer designator"); 626 }}, 627 designator.u); 628 } 629 630 template <typename T> 631 ExtValue genMutableBoxValueImpl(const Fortran::evaluate::Expr<T> &expr) { 632 return std::visit([&](const auto &x) { return genMutableBoxValueImpl(x); }, 633 expr.u); 634 } 635 636 mlir::Location getLoc() { return location; } 637 638 template <typename A> 639 mlir::Value genunbox(const A &expr) { 640 ExtValue e = genval(expr); 641 if (const fir::UnboxedValue *r = e.getUnboxed()) 642 return *r; 643 fir::emitFatalError(getLoc(), "unboxed expression expected"); 644 } 645 646 /// Generate an integral constant of `value` 647 template <int KIND> 648 mlir::Value genIntegerConstant(mlir::MLIRContext *context, 649 std::int64_t value) { 650 mlir::Type type = 651 converter.genType(Fortran::common::TypeCategory::Integer, KIND); 652 return builder.createIntegerConstant(getLoc(), type, value); 653 } 654 655 /// Generate a logical/boolean constant of `value` 656 mlir::Value genBoolConstant(bool value) { 657 return builder.createBool(getLoc(), value); 658 } 659 660 /// Generate a real constant with a value `value`. 661 template <int KIND> 662 mlir::Value genRealConstant(mlir::MLIRContext *context, 663 const llvm::APFloat &value) { 664 mlir::Type fltTy = Fortran::lower::convertReal(context, KIND); 665 return builder.createRealConstant(getLoc(), fltTy, value); 666 } 667 668 template <typename OpTy> 669 mlir::Value createCompareOp(mlir::arith::CmpIPredicate pred, 670 const ExtValue &left, const ExtValue &right) { 671 if (const fir::UnboxedValue *lhs = left.getUnboxed()) 672 if (const fir::UnboxedValue *rhs = right.getUnboxed()) 673 return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs); 674 fir::emitFatalError(getLoc(), "array compare should be handled in genarr"); 675 } 676 template <typename OpTy, typename A> 677 mlir::Value createCompareOp(const A &ex, mlir::arith::CmpIPredicate pred) { 678 ExtValue left = genval(ex.left()); 679 return createCompareOp<OpTy>(pred, left, genval(ex.right())); 680 } 681 682 template <typename OpTy> 683 mlir::Value createFltCmpOp(mlir::arith::CmpFPredicate pred, 684 const ExtValue &left, const ExtValue &right) { 685 if (const fir::UnboxedValue *lhs = left.getUnboxed()) 686 if (const fir::UnboxedValue *rhs = right.getUnboxed()) 687 return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs); 688 fir::emitFatalError(getLoc(), "array compare should be handled in genarr"); 689 } 690 template <typename OpTy, typename A> 691 mlir::Value createFltCmpOp(const A &ex, mlir::arith::CmpFPredicate pred) { 692 ExtValue left = genval(ex.left()); 693 return createFltCmpOp<OpTy>(pred, left, genval(ex.right())); 694 } 695 696 /// Create a call to the runtime to compare two CHARACTER values. 697 /// Precondition: This assumes that the two values have `fir.boxchar` type. 698 mlir::Value createCharCompare(mlir::arith::CmpIPredicate pred, 699 const ExtValue &left, const ExtValue &right) { 700 return fir::runtime::genCharCompare(builder, getLoc(), pred, left, right); 701 } 702 703 template <typename A> 704 mlir::Value createCharCompare(const A &ex, mlir::arith::CmpIPredicate pred) { 705 ExtValue left = genval(ex.left()); 706 return createCharCompare(pred, left, genval(ex.right())); 707 } 708 709 /// Returns a reference to a symbol or its box/boxChar descriptor if it has 710 /// one. 711 ExtValue gen(Fortran::semantics::SymbolRef sym) { 712 if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym)) 713 return val.match( 714 [&](const Fortran::lower::SymbolBox::PointerOrAllocatable &boxAddr) { 715 return fir::factory::genMutableBoxRead(builder, getLoc(), boxAddr); 716 }, 717 [&val](auto &) { return val.toExtendedValue(); }); 718 LLVM_DEBUG(llvm::dbgs() 719 << "unknown symbol: " << sym << "\nmap: " << symMap << '\n'); 720 fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value"); 721 } 722 723 ExtValue genLoad(const ExtValue &exv) { 724 return ::genLoad(builder, getLoc(), exv); 725 } 726 727 ExtValue genval(Fortran::semantics::SymbolRef sym) { 728 mlir::Location loc = getLoc(); 729 ExtValue var = gen(sym); 730 if (const fir::UnboxedValue *s = var.getUnboxed()) 731 if (fir::isReferenceLike(s->getType())) { 732 // A function with multiple entry points returning different types 733 // tags all result variables with one of the largest types to allow 734 // them to share the same storage. A reference to a result variable 735 // of one of the other types requires conversion to the actual type. 736 fir::UnboxedValue addr = *s; 737 if (Fortran::semantics::IsFunctionResult(sym)) { 738 mlir::Type resultType = converter.genType(*sym); 739 if (addr.getType() != resultType) 740 addr = builder.createConvert(loc, builder.getRefType(resultType), 741 addr); 742 } 743 return genLoad(addr); 744 } 745 return var; 746 } 747 748 ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) { 749 TODO(getLoc(), "genval BOZ"); 750 } 751 752 /// Return indirection to function designated in ProcedureDesignator. 753 /// The type of the function indirection is not guaranteed to match the one 754 /// of the ProcedureDesignator due to Fortran implicit typing rules. 755 ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) { 756 mlir::Location loc = getLoc(); 757 if (const Fortran::evaluate::SpecificIntrinsic *intrinsic = 758 proc.GetSpecificIntrinsic()) { 759 mlir::FunctionType signature = 760 Fortran::lower::translateSignature(proc, converter); 761 // Intrinsic lowering is based on the generic name, so retrieve it here in 762 // case it is different from the specific name. The type of the specific 763 // intrinsic is retained in the signature. 764 std::string genericName = 765 converter.getFoldingContext().intrinsics().GetGenericIntrinsicName( 766 intrinsic->name); 767 mlir::SymbolRefAttr symbolRefAttr = 768 Fortran::lower::getUnrestrictedIntrinsicSymbolRefAttr( 769 builder, loc, genericName, signature); 770 mlir::Value funcPtr = 771 builder.create<fir::AddrOfOp>(loc, signature, symbolRefAttr); 772 return funcPtr; 773 } 774 const Fortran::semantics::Symbol *symbol = proc.GetSymbol(); 775 assert(symbol && "expected symbol in ProcedureDesignator"); 776 mlir::Value funcPtr; 777 mlir::Value funcPtrResultLength; 778 if (Fortran::semantics::IsDummy(*symbol)) { 779 Fortran::lower::SymbolBox val = symMap.lookupSymbol(*symbol); 780 assert(val && "Dummy procedure not in symbol map"); 781 funcPtr = val.getAddr(); 782 if (fir::isCharacterProcedureTuple(funcPtr.getType(), 783 /*acceptRawFunc=*/false)) 784 std::tie(funcPtr, funcPtrResultLength) = 785 fir::factory::extractCharacterProcedureTuple(builder, loc, funcPtr); 786 } else { 787 std::string name = converter.mangleName(*symbol); 788 mlir::FuncOp func = 789 Fortran::lower::getOrDeclareFunction(name, proc, converter); 790 funcPtr = builder.create<fir::AddrOfOp>(loc, func.getFunctionType(), 791 builder.getSymbolRefAttr(name)); 792 } 793 if (Fortran::lower::mustPassLengthWithDummyProcedure(proc, converter)) { 794 // The result length, if available here, must be propagated along the 795 // procedure address so that call sites where the result length is assumed 796 // can retrieve the length. 797 Fortran::evaluate::DynamicType resultType = proc.GetType().value(); 798 if (const auto &lengthExpr = resultType.GetCharLength()) { 799 // The length expression may refer to dummy argument symbols that are 800 // meaningless without any actual arguments. Leave the length as 801 // unknown in that case, it be resolved on the call site 802 // with the actual arguments. 803 if (allSymbolsInExprPresentInMap(toEvExpr(*lengthExpr), symMap)) { 804 mlir::Value rawLen = fir::getBase(genval(*lengthExpr)); 805 // F2018 7.4.4.2 point 5. 806 funcPtrResultLength = 807 Fortran::lower::genMaxWithZero(builder, getLoc(), rawLen); 808 } 809 } 810 if (!funcPtrResultLength) 811 funcPtrResultLength = builder.createIntegerConstant( 812 loc, builder.getCharacterLengthType(), -1); 813 return fir::CharBoxValue{funcPtr, funcPtrResultLength}; 814 } 815 return funcPtr; 816 } 817 ExtValue genval(const Fortran::evaluate::NullPointer &) { 818 return builder.createNullConstant(getLoc()); 819 } 820 821 static bool 822 isDerivedTypeWithLengthParameters(const Fortran::semantics::Symbol &sym) { 823 if (const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType()) 824 if (const Fortran::semantics::DerivedTypeSpec *derived = 825 declTy->AsDerived()) 826 return Fortran::semantics::CountLenParameters(*derived) > 0; 827 return false; 828 } 829 830 static bool isBuiltinCPtr(const Fortran::semantics::Symbol &sym) { 831 if (const Fortran::semantics::DeclTypeSpec *declType = sym.GetType()) 832 if (const Fortran::semantics::DerivedTypeSpec *derived = 833 declType->AsDerived()) 834 return Fortran::semantics::IsIsoCType(derived); 835 return false; 836 } 837 838 /// Lower structure constructor without a temporary. This can be used in 839 /// fir::GloablOp, and assumes that the structure component is a constant. 840 ExtValue genStructComponentInInitializer( 841 const Fortran::evaluate::StructureConstructor &ctor) { 842 mlir::Location loc = getLoc(); 843 mlir::Type ty = translateSomeExprToFIRType(converter, toEvExpr(ctor)); 844 auto recTy = ty.cast<fir::RecordType>(); 845 auto fieldTy = fir::FieldType::get(ty.getContext()); 846 mlir::Value res = builder.create<fir::UndefOp>(loc, recTy); 847 848 for (const auto &[sym, expr] : ctor.values()) { 849 // Parent components need more work because they do not appear in the 850 // fir.rec type. 851 if (sym->test(Fortran::semantics::Symbol::Flag::ParentComp)) 852 TODO(loc, "parent component in structure constructor"); 853 854 llvm::StringRef name = toStringRef(sym->name()); 855 mlir::Type componentTy = recTy.getType(name); 856 // FIXME: type parameters must come from the derived-type-spec 857 auto field = builder.create<fir::FieldIndexOp>( 858 loc, fieldTy, name, ty, 859 /*typeParams=*/mlir::ValueRange{} /*TODO*/); 860 861 if (Fortran::semantics::IsAllocatable(sym)) 862 TODO(loc, "allocatable component in structure constructor"); 863 864 if (Fortran::semantics::IsPointer(sym)) { 865 mlir::Value initialTarget = Fortran::lower::genInitialDataTarget( 866 converter, loc, componentTy, expr.value()); 867 res = builder.create<fir::InsertValueOp>( 868 loc, recTy, res, initialTarget, 869 builder.getArrayAttr(field.getAttributes())); 870 continue; 871 } 872 873 if (isDerivedTypeWithLengthParameters(sym)) 874 TODO(loc, "component with length parameters in structure constructor"); 875 876 if (isBuiltinCPtr(sym)) { 877 // Builtin c_ptr and c_funptr have special handling because initial 878 // value are handled for them as an extension. 879 mlir::Value addr = fir::getBase(Fortran::lower::genExtAddrInInitializer( 880 converter, loc, expr.value())); 881 if (addr.getType() == componentTy) { 882 // Do nothing. The Ev::Expr was returned as a value that can be 883 // inserted directly to the component without an intermediary. 884 } else { 885 // The Ev::Expr returned is an initializer that is a pointer (e.g., 886 // null) that must be inserted into an intermediate cptr record 887 // value's address field, which ought to be an intptr_t on the target. 888 assert((fir::isa_ref_type(addr.getType()) || 889 addr.getType().isa<mlir::FunctionType>()) && 890 "expect reference type for address field"); 891 assert(fir::isa_derived(componentTy) && 892 "expect C_PTR, C_FUNPTR to be a record"); 893 auto cPtrRecTy = componentTy.cast<fir::RecordType>(); 894 llvm::StringRef addrFieldName = 895 Fortran::lower::builtin::cptrFieldName; 896 mlir::Type addrFieldTy = cPtrRecTy.getType(addrFieldName); 897 auto addrField = builder.create<fir::FieldIndexOp>( 898 loc, fieldTy, addrFieldName, componentTy, 899 /*typeParams=*/mlir::ValueRange{}); 900 mlir::Value castAddr = builder.createConvert(loc, addrFieldTy, addr); 901 auto undef = builder.create<fir::UndefOp>(loc, componentTy); 902 addr = builder.create<fir::InsertValueOp>( 903 loc, componentTy, undef, castAddr, 904 builder.getArrayAttr(addrField.getAttributes())); 905 } 906 res = builder.create<fir::InsertValueOp>( 907 loc, recTy, res, addr, builder.getArrayAttr(field.getAttributes())); 908 continue; 909 } 910 911 mlir::Value val = fir::getBase(genval(expr.value())); 912 assert(!fir::isa_ref_type(val.getType()) && "expecting a constant value"); 913 mlir::Value castVal = builder.createConvert(loc, componentTy, val); 914 res = builder.create<fir::InsertValueOp>( 915 loc, recTy, res, castVal, 916 builder.getArrayAttr(field.getAttributes())); 917 } 918 return res; 919 } 920 921 /// A structure constructor is lowered two ways. In an initializer context, 922 /// the entire structure must be constant, so the aggregate value is 923 /// constructed inline. This allows it to be the body of a GlobalOp. 924 /// Otherwise, the structure constructor is in an expression. In that case, a 925 /// temporary object is constructed in the stack frame of the procedure. 926 ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) { 927 if (inInitializer) 928 return genStructComponentInInitializer(ctor); 929 mlir::Location loc = getLoc(); 930 mlir::Type ty = translateSomeExprToFIRType(converter, toEvExpr(ctor)); 931 auto recTy = ty.cast<fir::RecordType>(); 932 auto fieldTy = fir::FieldType::get(ty.getContext()); 933 mlir::Value res = builder.createTemporary(loc, recTy); 934 935 for (const auto &value : ctor.values()) { 936 const Fortran::semantics::Symbol &sym = *value.first; 937 const Fortran::lower::SomeExpr &expr = value.second.value(); 938 // Parent components need more work because they do not appear in the 939 // fir.rec type. 940 if (sym.test(Fortran::semantics::Symbol::Flag::ParentComp)) 941 TODO(loc, "parent component in structure constructor"); 942 943 if (isDerivedTypeWithLengthParameters(sym)) 944 TODO(loc, "component with length parameters in structure constructor"); 945 946 llvm::StringRef name = toStringRef(sym.name()); 947 // FIXME: type parameters must come from the derived-type-spec 948 mlir::Value field = builder.create<fir::FieldIndexOp>( 949 loc, fieldTy, name, ty, 950 /*typeParams=*/mlir::ValueRange{} /*TODO*/); 951 mlir::Type coorTy = builder.getRefType(recTy.getType(name)); 952 auto coor = builder.create<fir::CoordinateOp>(loc, coorTy, 953 fir::getBase(res), field); 954 ExtValue to = fir::factory::componentToExtendedValue(builder, loc, coor); 955 to.match( 956 [&](const fir::UnboxedValue &toPtr) { 957 ExtValue value = genval(expr); 958 fir::factory::genScalarAssignment(builder, loc, to, value); 959 }, 960 [&](const fir::CharBoxValue &) { 961 ExtValue value = genval(expr); 962 fir::factory::genScalarAssignment(builder, loc, to, value); 963 }, 964 [&](const fir::ArrayBoxValue &) { 965 Fortran::lower::createSomeArrayAssignment(converter, to, expr, 966 symMap, stmtCtx); 967 }, 968 [&](const fir::CharArrayBoxValue &) { 969 Fortran::lower::createSomeArrayAssignment(converter, to, expr, 970 symMap, stmtCtx); 971 }, 972 [&](const fir::BoxValue &toBox) { 973 fir::emitFatalError(loc, "derived type components must not be " 974 "represented by fir::BoxValue"); 975 }, 976 [&](const fir::MutableBoxValue &toBox) { 977 if (toBox.isPointer()) { 978 Fortran::lower::associateMutableBox( 979 converter, loc, toBox, expr, /*lbounds=*/llvm::None, stmtCtx); 980 return; 981 } 982 // For allocatable components, a deep copy is needed. 983 TODO(loc, "allocatable components in derived type assignment"); 984 }, 985 [&](const fir::ProcBoxValue &toBox) { 986 TODO(loc, "procedure pointer component in derived type assignment"); 987 }); 988 } 989 return res; 990 } 991 992 /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol. 993 ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) { 994 return converter.impliedDoBinding(toStringRef(var.name)); 995 } 996 997 ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) { 998 ExtValue exv = desc.base().IsSymbol() ? gen(getLastSym(desc.base())) 999 : gen(desc.base().GetComponent()); 1000 mlir::IndexType idxTy = builder.getIndexType(); 1001 mlir::Location loc = getLoc(); 1002 auto castResult = [&](mlir::Value v) { 1003 using ResTy = Fortran::evaluate::DescriptorInquiry::Result; 1004 return builder.createConvert( 1005 loc, converter.genType(ResTy::category, ResTy::kind), v); 1006 }; 1007 switch (desc.field()) { 1008 case Fortran::evaluate::DescriptorInquiry::Field::Len: 1009 return castResult(fir::factory::readCharLen(builder, loc, exv)); 1010 case Fortran::evaluate::DescriptorInquiry::Field::LowerBound: 1011 return castResult(fir::factory::readLowerBound( 1012 builder, loc, exv, desc.dimension(), 1013 builder.createIntegerConstant(loc, idxTy, 1))); 1014 case Fortran::evaluate::DescriptorInquiry::Field::Extent: 1015 return castResult( 1016 fir::factory::readExtent(builder, loc, exv, desc.dimension())); 1017 case Fortran::evaluate::DescriptorInquiry::Field::Rank: 1018 TODO(loc, "rank inquiry on assumed rank"); 1019 case Fortran::evaluate::DescriptorInquiry::Field::Stride: 1020 // So far the front end does not generate this inquiry. 1021 TODO(loc, "Stride inquiry"); 1022 } 1023 llvm_unreachable("unknown descriptor inquiry"); 1024 } 1025 1026 ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) { 1027 TODO(getLoc(), "genval TypeParamInquiry"); 1028 } 1029 1030 template <int KIND> 1031 ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) { 1032 TODO(getLoc(), "genval ComplexComponent"); 1033 } 1034 1035 template <int KIND> 1036 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 1037 Fortran::common::TypeCategory::Integer, KIND>> &op) { 1038 mlir::Value input = genunbox(op.left()); 1039 // Like LLVM, integer negation is the binary op "0 - value" 1040 mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0); 1041 return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input); 1042 } 1043 1044 template <int KIND> 1045 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 1046 Fortran::common::TypeCategory::Real, KIND>> &op) { 1047 return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left())); 1048 } 1049 template <int KIND> 1050 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 1051 Fortran::common::TypeCategory::Complex, KIND>> &op) { 1052 return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left())); 1053 } 1054 1055 template <typename OpTy> 1056 mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) { 1057 assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right)); 1058 mlir::Value lhs = fir::getBase(left); 1059 mlir::Value rhs = fir::getBase(right); 1060 assert(lhs.getType() == rhs.getType() && "types must be the same"); 1061 return builder.create<OpTy>(getLoc(), lhs, rhs); 1062 } 1063 1064 template <typename OpTy, typename A> 1065 mlir::Value createBinaryOp(const A &ex) { 1066 ExtValue left = genval(ex.left()); 1067 return createBinaryOp<OpTy>(left, genval(ex.right())); 1068 } 1069 1070 #undef GENBIN 1071 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \ 1072 template <int KIND> \ 1073 ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \ 1074 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \ 1075 return createBinaryOp<GenBinFirOp>(x); \ 1076 } 1077 1078 GENBIN(Add, Integer, mlir::arith::AddIOp) 1079 GENBIN(Add, Real, mlir::arith::AddFOp) 1080 GENBIN(Add, Complex, fir::AddcOp) 1081 GENBIN(Subtract, Integer, mlir::arith::SubIOp) 1082 GENBIN(Subtract, Real, mlir::arith::SubFOp) 1083 GENBIN(Subtract, Complex, fir::SubcOp) 1084 GENBIN(Multiply, Integer, mlir::arith::MulIOp) 1085 GENBIN(Multiply, Real, mlir::arith::MulFOp) 1086 GENBIN(Multiply, Complex, fir::MulcOp) 1087 GENBIN(Divide, Integer, mlir::arith::DivSIOp) 1088 GENBIN(Divide, Real, mlir::arith::DivFOp) 1089 GENBIN(Divide, Complex, fir::DivcOp) 1090 1091 template <Fortran::common::TypeCategory TC, int KIND> 1092 ExtValue genval( 1093 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) { 1094 mlir::Type ty = converter.genType(TC, KIND); 1095 mlir::Value lhs = genunbox(op.left()); 1096 mlir::Value rhs = genunbox(op.right()); 1097 return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs); 1098 } 1099 1100 template <Fortran::common::TypeCategory TC, int KIND> 1101 ExtValue genval( 1102 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>> 1103 &op) { 1104 mlir::Type ty = converter.genType(TC, KIND); 1105 mlir::Value lhs = genunbox(op.left()); 1106 mlir::Value rhs = genunbox(op.right()); 1107 return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs); 1108 } 1109 1110 template <int KIND> 1111 ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) { 1112 mlir::Value realPartValue = genunbox(op.left()); 1113 return fir::factory::Complex{builder, getLoc()}.createComplex( 1114 KIND, realPartValue, genunbox(op.right())); 1115 } 1116 1117 template <int KIND> 1118 ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) { 1119 ExtValue lhs = genval(op.left()); 1120 ExtValue rhs = genval(op.right()); 1121 const fir::CharBoxValue *lhsChar = lhs.getCharBox(); 1122 const fir::CharBoxValue *rhsChar = rhs.getCharBox(); 1123 if (lhsChar && rhsChar) 1124 return fir::factory::CharacterExprHelper{builder, getLoc()} 1125 .createConcatenate(*lhsChar, *rhsChar); 1126 TODO(getLoc(), "character array concatenate"); 1127 } 1128 1129 /// MIN and MAX operations 1130 template <Fortran::common::TypeCategory TC, int KIND> 1131 ExtValue 1132 genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> 1133 &op) { 1134 TODO(getLoc(), "genval Extremum<TC, KIND>"); 1135 } 1136 1137 // Change the dynamic length information without actually changing the 1138 // underlying character storage. 1139 fir::ExtendedValue 1140 replaceScalarCharacterLength(const fir::ExtendedValue &scalarChar, 1141 mlir::Value newLenValue) { 1142 mlir::Location loc = getLoc(); 1143 const fir::CharBoxValue *charBox = scalarChar.getCharBox(); 1144 if (!charBox) 1145 fir::emitFatalError(loc, "expected scalar character"); 1146 mlir::Value charAddr = charBox->getAddr(); 1147 auto charType = 1148 fir::unwrapPassByRefType(charAddr.getType()).cast<fir::CharacterType>(); 1149 if (charType.hasConstantLen()) { 1150 // Erase previous constant length from the base type. 1151 fir::CharacterType::LenType newLen = fir::CharacterType::unknownLen(); 1152 mlir::Type newCharTy = fir::CharacterType::get( 1153 builder.getContext(), charType.getFKind(), newLen); 1154 mlir::Type newType = fir::ReferenceType::get(newCharTy); 1155 charAddr = builder.createConvert(loc, newType, charAddr); 1156 return fir::CharBoxValue{charAddr, newLenValue}; 1157 } 1158 return fir::CharBoxValue{charAddr, newLenValue}; 1159 } 1160 1161 template <int KIND> 1162 ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) { 1163 mlir::Value newLenValue = genunbox(x.right()); 1164 fir::ExtendedValue lhs = gen(x.left()); 1165 return replaceScalarCharacterLength(lhs, newLenValue); 1166 } 1167 1168 template <int KIND> 1169 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 1170 Fortran::common::TypeCategory::Integer, KIND>> &op) { 1171 return createCompareOp<mlir::arith::CmpIOp>(op, 1172 translateRelational(op.opr)); 1173 } 1174 template <int KIND> 1175 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 1176 Fortran::common::TypeCategory::Real, KIND>> &op) { 1177 return createFltCmpOp<mlir::arith::CmpFOp>( 1178 op, translateFloatRelational(op.opr)); 1179 } 1180 template <int KIND> 1181 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 1182 Fortran::common::TypeCategory::Complex, KIND>> &op) { 1183 TODO(getLoc(), "genval complex comparison"); 1184 } 1185 template <int KIND> 1186 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 1187 Fortran::common::TypeCategory::Character, KIND>> &op) { 1188 return createCharCompare(op, translateRelational(op.opr)); 1189 } 1190 1191 ExtValue 1192 genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) { 1193 return std::visit([&](const auto &x) { return genval(x); }, op.u); 1194 } 1195 1196 template <Fortran::common::TypeCategory TC1, int KIND, 1197 Fortran::common::TypeCategory TC2> 1198 ExtValue 1199 genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, 1200 TC2> &convert) { 1201 mlir::Type ty = converter.genType(TC1, KIND); 1202 mlir::Value operand = genunbox(convert.left()); 1203 return builder.convertWithSemantics(getLoc(), ty, operand); 1204 } 1205 1206 template <typename A> 1207 ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) { 1208 TODO(getLoc(), "genval parentheses<A>"); 1209 } 1210 1211 template <int KIND> 1212 ExtValue genval(const Fortran::evaluate::Not<KIND> &op) { 1213 mlir::Value logical = genunbox(op.left()); 1214 mlir::Value one = genBoolConstant(true); 1215 mlir::Value val = 1216 builder.createConvert(getLoc(), builder.getI1Type(), logical); 1217 return builder.create<mlir::arith::XOrIOp>(getLoc(), val, one); 1218 } 1219 1220 template <int KIND> 1221 ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) { 1222 mlir::IntegerType i1Type = builder.getI1Type(); 1223 mlir::Value slhs = genunbox(op.left()); 1224 mlir::Value srhs = genunbox(op.right()); 1225 mlir::Value lhs = builder.createConvert(getLoc(), i1Type, slhs); 1226 mlir::Value rhs = builder.createConvert(getLoc(), i1Type, srhs); 1227 switch (op.logicalOperator) { 1228 case Fortran::evaluate::LogicalOperator::And: 1229 return createBinaryOp<mlir::arith::AndIOp>(lhs, rhs); 1230 case Fortran::evaluate::LogicalOperator::Or: 1231 return createBinaryOp<mlir::arith::OrIOp>(lhs, rhs); 1232 case Fortran::evaluate::LogicalOperator::Eqv: 1233 return createCompareOp<mlir::arith::CmpIOp>( 1234 mlir::arith::CmpIPredicate::eq, lhs, rhs); 1235 case Fortran::evaluate::LogicalOperator::Neqv: 1236 return createCompareOp<mlir::arith::CmpIOp>( 1237 mlir::arith::CmpIPredicate::ne, lhs, rhs); 1238 case Fortran::evaluate::LogicalOperator::Not: 1239 // lib/evaluate expression for .NOT. is Fortran::evaluate::Not<KIND>. 1240 llvm_unreachable(".NOT. is not a binary operator"); 1241 } 1242 llvm_unreachable("unhandled logical operation"); 1243 } 1244 1245 /// Convert a scalar literal constant to IR. 1246 template <Fortran::common::TypeCategory TC, int KIND> 1247 ExtValue genScalarLit( 1248 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> 1249 &value) { 1250 if constexpr (TC == Fortran::common::TypeCategory::Integer) { 1251 return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64()); 1252 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) { 1253 return genBoolConstant(value.IsTrue()); 1254 } else if constexpr (TC == Fortran::common::TypeCategory::Real) { 1255 std::string str = value.DumpHexadecimal(); 1256 if constexpr (KIND == 2) { 1257 llvm::APFloat floatVal{llvm::APFloatBase::IEEEhalf(), str}; 1258 return genRealConstant<KIND>(builder.getContext(), floatVal); 1259 } else if constexpr (KIND == 3) { 1260 llvm::APFloat floatVal{llvm::APFloatBase::BFloat(), str}; 1261 return genRealConstant<KIND>(builder.getContext(), floatVal); 1262 } else if constexpr (KIND == 4) { 1263 llvm::APFloat floatVal{llvm::APFloatBase::IEEEsingle(), str}; 1264 return genRealConstant<KIND>(builder.getContext(), floatVal); 1265 } else if constexpr (KIND == 10) { 1266 llvm::APFloat floatVal{llvm::APFloatBase::x87DoubleExtended(), str}; 1267 return genRealConstant<KIND>(builder.getContext(), floatVal); 1268 } else if constexpr (KIND == 16) { 1269 llvm::APFloat floatVal{llvm::APFloatBase::IEEEquad(), str}; 1270 return genRealConstant<KIND>(builder.getContext(), floatVal); 1271 } else { 1272 // convert everything else to double 1273 llvm::APFloat floatVal{llvm::APFloatBase::IEEEdouble(), str}; 1274 return genRealConstant<KIND>(builder.getContext(), floatVal); 1275 } 1276 } else if constexpr (TC == Fortran::common::TypeCategory::Complex) { 1277 using TR = 1278 Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>; 1279 Fortran::evaluate::ComplexConstructor<KIND> ctor( 1280 Fortran::evaluate::Expr<TR>{ 1281 Fortran::evaluate::Constant<TR>{value.REAL()}}, 1282 Fortran::evaluate::Expr<TR>{ 1283 Fortran::evaluate::Constant<TR>{value.AIMAG()}}); 1284 return genunbox(ctor); 1285 } else /*constexpr*/ { 1286 llvm_unreachable("unhandled constant"); 1287 } 1288 } 1289 1290 /// Generate a raw literal value and store it in the rawVals vector. 1291 template <Fortran::common::TypeCategory TC, int KIND> 1292 void 1293 genRawLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> 1294 &value) { 1295 mlir::Attribute val; 1296 assert(inInitializer != nullptr); 1297 if constexpr (TC == Fortran::common::TypeCategory::Integer) { 1298 inInitializer->rawType = converter.genType(TC, KIND); 1299 val = builder.getIntegerAttr(inInitializer->rawType, value.ToInt64()); 1300 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) { 1301 inInitializer->rawType = 1302 converter.genType(Fortran::common::TypeCategory::Integer, KIND); 1303 val = builder.getIntegerAttr(inInitializer->rawType, value.IsTrue()); 1304 } else if constexpr (TC == Fortran::common::TypeCategory::Real) { 1305 std::string str = value.DumpHexadecimal(); 1306 inInitializer->rawType = converter.genType(TC, KIND); 1307 llvm::APFloat floatVal{builder.getKindMap().getFloatSemantics(KIND), str}; 1308 val = builder.getFloatAttr(inInitializer->rawType, floatVal); 1309 } else if constexpr (TC == Fortran::common::TypeCategory::Complex) { 1310 std::string strReal = value.REAL().DumpHexadecimal(); 1311 std::string strImg = value.AIMAG().DumpHexadecimal(); 1312 inInitializer->rawType = converter.genType(TC, KIND); 1313 llvm::APFloat realVal{builder.getKindMap().getFloatSemantics(KIND), 1314 strReal}; 1315 val = builder.getFloatAttr(inInitializer->rawType, realVal); 1316 inInitializer->rawVals.push_back(val); 1317 llvm::APFloat imgVal{builder.getKindMap().getFloatSemantics(KIND), 1318 strImg}; 1319 val = builder.getFloatAttr(inInitializer->rawType, imgVal); 1320 } 1321 inInitializer->rawVals.push_back(val); 1322 } 1323 1324 /// Convert a scalar literal CHARACTER to IR. 1325 template <int KIND> 1326 ExtValue 1327 genScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type< 1328 Fortran::common::TypeCategory::Character, KIND>> &value, 1329 int64_t len) { 1330 using ET = typename std::decay_t<decltype(value)>::value_type; 1331 if constexpr (KIND == 1) { 1332 assert(value.size() == static_cast<std::uint64_t>(len)); 1333 // Outline character constant in ro data if it is not in an initializer. 1334 if (!inInitializer) 1335 return fir::factory::createStringLiteral(builder, getLoc(), value); 1336 // When in an initializer context, construct the literal op itself and do 1337 // not construct another constant object in rodata. 1338 fir::StringLitOp stringLit = builder.createStringLitOp(getLoc(), value); 1339 mlir::Value lenp = builder.createIntegerConstant( 1340 getLoc(), builder.getCharacterLengthType(), len); 1341 return fir::CharBoxValue{stringLit.getResult(), lenp}; 1342 } 1343 fir::CharacterType type = 1344 fir::CharacterType::get(builder.getContext(), KIND, len); 1345 auto consLit = [&]() -> fir::StringLitOp { 1346 mlir::MLIRContext *context = builder.getContext(); 1347 std::int64_t size = static_cast<std::int64_t>(value.size()); 1348 mlir::ShapedType shape = mlir::RankedTensorType::get( 1349 llvm::ArrayRef<std::int64_t>{size}, 1350 mlir::IntegerType::get(builder.getContext(), sizeof(ET) * 8)); 1351 auto denseAttr = mlir::DenseElementsAttr::get( 1352 shape, llvm::ArrayRef<ET>{value.data(), value.size()}); 1353 auto denseTag = mlir::StringAttr::get(context, fir::StringLitOp::xlist()); 1354 mlir::NamedAttribute dataAttr(denseTag, denseAttr); 1355 auto sizeTag = mlir::StringAttr::get(context, fir::StringLitOp::size()); 1356 mlir::NamedAttribute sizeAttr(sizeTag, builder.getI64IntegerAttr(len)); 1357 llvm::SmallVector<mlir::NamedAttribute> attrs = {dataAttr, sizeAttr}; 1358 return builder.create<fir::StringLitOp>( 1359 getLoc(), llvm::ArrayRef<mlir::Type>{type}, llvm::None, attrs); 1360 }; 1361 1362 mlir::Value lenp = builder.createIntegerConstant( 1363 getLoc(), builder.getCharacterLengthType(), len); 1364 // When in an initializer context, construct the literal op itself and do 1365 // not construct another constant object in rodata. 1366 if (inInitializer) 1367 return fir::CharBoxValue{consLit().getResult(), lenp}; 1368 1369 // Otherwise, the string is in a plain old expression so "outline" the value 1370 // by hashconsing it to a constant literal object. 1371 1372 std::string globalName = 1373 fir::factory::uniqueCGIdent("cl", (const char *)value.c_str()); 1374 fir::GlobalOp global = builder.getNamedGlobal(globalName); 1375 if (!global) 1376 global = builder.createGlobalConstant( 1377 getLoc(), type, globalName, 1378 [&](fir::FirOpBuilder &builder) { 1379 fir::StringLitOp str = consLit(); 1380 builder.create<fir::HasValueOp>(getLoc(), str); 1381 }, 1382 builder.createLinkOnceLinkage()); 1383 auto addr = builder.create<fir::AddrOfOp>(getLoc(), global.resultType(), 1384 global.getSymbol()); 1385 return fir::CharBoxValue{addr, lenp}; 1386 } 1387 1388 template <Fortran::common::TypeCategory TC, int KIND> 1389 ExtValue genArrayLit( 1390 const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>> 1391 &con) { 1392 mlir::Location loc = getLoc(); 1393 mlir::IndexType idxTy = builder.getIndexType(); 1394 Fortran::evaluate::ConstantSubscript size = 1395 Fortran::evaluate::GetSize(con.shape()); 1396 fir::SequenceType::Shape shape(con.shape().begin(), con.shape().end()); 1397 mlir::Type eleTy; 1398 if constexpr (TC == Fortran::common::TypeCategory::Character) 1399 eleTy = converter.genType(TC, KIND, {con.LEN()}); 1400 else 1401 eleTy = converter.genType(TC, KIND); 1402 auto arrayTy = fir::SequenceType::get(shape, eleTy); 1403 mlir::Value array; 1404 llvm::SmallVector<mlir::Value> lbounds; 1405 llvm::SmallVector<mlir::Value> extents; 1406 if (!inInitializer || !inInitializer->genRawVals) { 1407 array = builder.create<fir::UndefOp>(loc, arrayTy); 1408 for (auto [lb, extent] : llvm::zip(con.lbounds(), shape)) { 1409 lbounds.push_back(builder.createIntegerConstant(loc, idxTy, lb - 1)); 1410 extents.push_back(builder.createIntegerConstant(loc, idxTy, extent)); 1411 } 1412 } 1413 if (size == 0) { 1414 if constexpr (TC == Fortran::common::TypeCategory::Character) { 1415 mlir::Value len = builder.createIntegerConstant(loc, idxTy, con.LEN()); 1416 return fir::CharArrayBoxValue{array, len, extents, lbounds}; 1417 } else { 1418 return fir::ArrayBoxValue{array, extents, lbounds}; 1419 } 1420 } 1421 Fortran::evaluate::ConstantSubscripts subscripts = con.lbounds(); 1422 auto createIdx = [&]() { 1423 llvm::SmallVector<mlir::Attribute> idx; 1424 for (size_t i = 0; i < subscripts.size(); ++i) 1425 idx.push_back( 1426 builder.getIntegerAttr(idxTy, subscripts[i] - con.lbounds()[i])); 1427 return idx; 1428 }; 1429 if constexpr (TC == Fortran::common::TypeCategory::Character) { 1430 assert(array && "array must not be nullptr"); 1431 do { 1432 mlir::Value elementVal = 1433 fir::getBase(genScalarLit<KIND>(con.At(subscripts), con.LEN())); 1434 array = builder.create<fir::InsertValueOp>( 1435 loc, arrayTy, array, elementVal, builder.getArrayAttr(createIdx())); 1436 } while (con.IncrementSubscripts(subscripts)); 1437 mlir::Value len = builder.createIntegerConstant(loc, idxTy, con.LEN()); 1438 return fir::CharArrayBoxValue{array, len, extents, lbounds}; 1439 } else { 1440 llvm::SmallVector<mlir::Attribute> rangeStartIdx; 1441 uint64_t rangeSize = 0; 1442 do { 1443 if (inInitializer && inInitializer->genRawVals) { 1444 genRawLit<TC, KIND>(con.At(subscripts)); 1445 continue; 1446 } 1447 auto getElementVal = [&]() { 1448 return builder.createConvert( 1449 loc, eleTy, 1450 fir::getBase(genScalarLit<TC, KIND>(con.At(subscripts)))); 1451 }; 1452 Fortran::evaluate::ConstantSubscripts nextSubscripts = subscripts; 1453 bool nextIsSame = con.IncrementSubscripts(nextSubscripts) && 1454 con.At(subscripts) == con.At(nextSubscripts); 1455 if (!rangeSize && !nextIsSame) { // single (non-range) value 1456 array = builder.create<fir::InsertValueOp>( 1457 loc, arrayTy, array, getElementVal(), 1458 builder.getArrayAttr(createIdx())); 1459 } else if (!rangeSize) { // start a range 1460 rangeStartIdx = createIdx(); 1461 rangeSize = 1; 1462 } else if (nextIsSame) { // expand a range 1463 ++rangeSize; 1464 } else { // end a range 1465 llvm::SmallVector<int64_t> rangeBounds; 1466 llvm::SmallVector<mlir::Attribute> idx = createIdx(); 1467 for (size_t i = 0; i < idx.size(); ++i) { 1468 rangeBounds.push_back(rangeStartIdx[i] 1469 .cast<mlir::IntegerAttr>() 1470 .getValue() 1471 .getSExtValue()); 1472 rangeBounds.push_back( 1473 idx[i].cast<mlir::IntegerAttr>().getValue().getSExtValue()); 1474 } 1475 array = builder.create<fir::InsertOnRangeOp>( 1476 loc, arrayTy, array, getElementVal(), 1477 builder.getIndexVectorAttr(rangeBounds)); 1478 rangeSize = 0; 1479 } 1480 } while (con.IncrementSubscripts(subscripts)); 1481 return fir::ArrayBoxValue{array, extents, lbounds}; 1482 } 1483 } 1484 1485 fir::ExtendedValue genArrayLit( 1486 const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) { 1487 mlir::Location loc = getLoc(); 1488 mlir::IndexType idxTy = builder.getIndexType(); 1489 Fortran::evaluate::ConstantSubscript size = 1490 Fortran::evaluate::GetSize(con.shape()); 1491 fir::SequenceType::Shape shape(con.shape().begin(), con.shape().end()); 1492 mlir::Type eleTy = converter.genType(con.GetType().GetDerivedTypeSpec()); 1493 auto arrayTy = fir::SequenceType::get(shape, eleTy); 1494 mlir::Value array = builder.create<fir::UndefOp>(loc, arrayTy); 1495 llvm::SmallVector<mlir::Value> lbounds; 1496 llvm::SmallVector<mlir::Value> extents; 1497 for (auto [lb, extent] : llvm::zip(con.lbounds(), con.shape())) { 1498 lbounds.push_back(builder.createIntegerConstant(loc, idxTy, lb - 1)); 1499 extents.push_back(builder.createIntegerConstant(loc, idxTy, extent)); 1500 } 1501 if (size == 0) 1502 return fir::ArrayBoxValue{array, extents, lbounds}; 1503 Fortran::evaluate::ConstantSubscripts subscripts = con.lbounds(); 1504 do { 1505 mlir::Value derivedVal = fir::getBase(genval(con.At(subscripts))); 1506 llvm::SmallVector<mlir::Attribute> idx; 1507 for (auto [dim, lb] : llvm::zip(subscripts, con.lbounds())) 1508 idx.push_back(builder.getIntegerAttr(idxTy, dim - lb)); 1509 array = builder.create<fir::InsertValueOp>( 1510 loc, arrayTy, array, derivedVal, builder.getArrayAttr(idx)); 1511 } while (con.IncrementSubscripts(subscripts)); 1512 return fir::ArrayBoxValue{array, extents, lbounds}; 1513 } 1514 1515 template <Fortran::common::TypeCategory TC, int KIND> 1516 ExtValue 1517 genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>> 1518 &con) { 1519 if (con.Rank() > 0) 1520 return genArrayLit(con); 1521 std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>> 1522 opt = con.GetScalarValue(); 1523 assert(opt.has_value() && "constant has no value"); 1524 if constexpr (TC == Fortran::common::TypeCategory::Character) { 1525 return genScalarLit<KIND>(opt.value(), con.LEN()); 1526 } else { 1527 return genScalarLit<TC, KIND>(opt.value()); 1528 } 1529 } 1530 1531 fir::ExtendedValue genval( 1532 const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) { 1533 if (con.Rank() > 0) 1534 return genArrayLit(con); 1535 if (auto ctor = con.GetScalarValue()) 1536 return genval(ctor.value()); 1537 fir::emitFatalError(getLoc(), 1538 "constant of derived type has no constructor"); 1539 } 1540 1541 template <typename A> 1542 ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) { 1543 TODO(getLoc(), "genval ArrayConstructor<A>"); 1544 } 1545 1546 ExtValue gen(const Fortran::evaluate::ComplexPart &x) { 1547 TODO(getLoc(), "gen ComplexPart"); 1548 } 1549 ExtValue genval(const Fortran::evaluate::ComplexPart &x) { 1550 TODO(getLoc(), "genval ComplexPart"); 1551 } 1552 1553 /// Reference to a substring. 1554 ExtValue gen(const Fortran::evaluate::Substring &s) { 1555 // Get base string 1556 auto baseString = std::visit( 1557 Fortran::common::visitors{ 1558 [&](const Fortran::evaluate::DataRef &x) { return gen(x); }, 1559 [&](const Fortran::evaluate::StaticDataObject::Pointer &p) 1560 -> ExtValue { 1561 if (std::optional<std::string> str = p->AsString()) 1562 return fir::factory::createStringLiteral(builder, getLoc(), 1563 *str); 1564 // TODO: convert StaticDataObject to Constant<T> and use normal 1565 // constant path. Beware that StaticDataObject data() takes into 1566 // account build machine endianness. 1567 TODO(getLoc(), 1568 "StaticDataObject::Pointer substring with kind > 1"); 1569 }, 1570 }, 1571 s.parent()); 1572 llvm::SmallVector<mlir::Value> bounds; 1573 mlir::Value lower = genunbox(s.lower()); 1574 bounds.push_back(lower); 1575 if (Fortran::evaluate::MaybeExtentExpr upperBound = s.upper()) { 1576 mlir::Value upper = genunbox(*upperBound); 1577 bounds.push_back(upper); 1578 } 1579 fir::factory::CharacterExprHelper charHelper{builder, getLoc()}; 1580 return baseString.match( 1581 [&](const fir::CharBoxValue &x) -> ExtValue { 1582 return charHelper.createSubstring(x, bounds); 1583 }, 1584 [&](const fir::CharArrayBoxValue &) -> ExtValue { 1585 fir::emitFatalError( 1586 getLoc(), 1587 "array substring should be handled in array expression"); 1588 }, 1589 [&](const auto &) -> ExtValue { 1590 fir::emitFatalError(getLoc(), "substring base is not a CharBox"); 1591 }); 1592 } 1593 1594 /// The value of a substring. 1595 ExtValue genval(const Fortran::evaluate::Substring &ss) { 1596 // FIXME: why is the value of a substring being lowered the same as the 1597 // address of a substring? 1598 return gen(ss); 1599 } 1600 1601 ExtValue genval(const Fortran::evaluate::Subscript &subs) { 1602 if (auto *s = std::get_if<Fortran::evaluate::IndirectSubscriptIntegerExpr>( 1603 &subs.u)) { 1604 if (s->value().Rank() > 0) 1605 fir::emitFatalError(getLoc(), "vector subscript is not scalar"); 1606 return {genval(s->value())}; 1607 } 1608 fir::emitFatalError(getLoc(), "subscript triple notation is not scalar"); 1609 } 1610 1611 ExtValue genSubscript(const Fortran::evaluate::Subscript &subs) { 1612 return genval(subs); 1613 } 1614 1615 ExtValue gen(const Fortran::evaluate::DataRef &dref) { 1616 return std::visit([&](const auto &x) { return gen(x); }, dref.u); 1617 } 1618 ExtValue genval(const Fortran::evaluate::DataRef &dref) { 1619 return std::visit([&](const auto &x) { return genval(x); }, dref.u); 1620 } 1621 1622 // Helper function to turn the Component structure into a list of nested 1623 // components, ordered from largest/leftmost to smallest/rightmost: 1624 // - where only the smallest/rightmost item may be allocatable or a pointer 1625 // (nested allocatable/pointer components require nested coordinate_of ops) 1626 // - that does not contain any parent components 1627 // (the front end places parent components directly in the object) 1628 // Return the object used as the base coordinate for the component chain. 1629 static Fortran::evaluate::DataRef const * 1630 reverseComponents(const Fortran::evaluate::Component &cmpt, 1631 std::list<const Fortran::evaluate::Component *> &list) { 1632 if (!cmpt.GetLastSymbol().test( 1633 Fortran::semantics::Symbol::Flag::ParentComp)) 1634 list.push_front(&cmpt); 1635 return std::visit( 1636 Fortran::common::visitors{ 1637 [&](const Fortran::evaluate::Component &x) { 1638 if (Fortran::semantics::IsAllocatableOrPointer(x.GetLastSymbol())) 1639 return &cmpt.base(); 1640 return reverseComponents(x, list); 1641 }, 1642 [&](auto &) { return &cmpt.base(); }, 1643 }, 1644 cmpt.base().u); 1645 } 1646 1647 // Return the coordinate of the component reference 1648 ExtValue genComponent(const Fortran::evaluate::Component &cmpt) { 1649 std::list<const Fortran::evaluate::Component *> list; 1650 const Fortran::evaluate::DataRef *base = reverseComponents(cmpt, list); 1651 llvm::SmallVector<mlir::Value> coorArgs; 1652 ExtValue obj = gen(*base); 1653 mlir::Type ty = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(obj).getType()); 1654 mlir::Location loc = getLoc(); 1655 auto fldTy = fir::FieldType::get(&converter.getMLIRContext()); 1656 // FIXME: need to thread the LEN type parameters here. 1657 for (const Fortran::evaluate::Component *field : list) { 1658 auto recTy = ty.cast<fir::RecordType>(); 1659 const Fortran::semantics::Symbol &sym = field->GetLastSymbol(); 1660 llvm::StringRef name = toStringRef(sym.name()); 1661 coorArgs.push_back(builder.create<fir::FieldIndexOp>( 1662 loc, fldTy, name, recTy, fir::getTypeParams(obj))); 1663 ty = recTy.getType(name); 1664 } 1665 ty = builder.getRefType(ty); 1666 return fir::factory::componentToExtendedValue( 1667 builder, loc, 1668 builder.create<fir::CoordinateOp>(loc, ty, fir::getBase(obj), 1669 coorArgs)); 1670 } 1671 1672 ExtValue gen(const Fortran::evaluate::Component &cmpt) { 1673 // Components may be pointer or allocatable. In the gen() path, the mutable 1674 // aspect is lost to simplify handling on the client side. To retain the 1675 // mutable aspect, genMutableBoxValue should be used. 1676 return genComponent(cmpt).match( 1677 [&](const fir::MutableBoxValue &mutableBox) { 1678 return fir::factory::genMutableBoxRead(builder, getLoc(), mutableBox); 1679 }, 1680 [](auto &box) -> ExtValue { return box; }); 1681 } 1682 1683 ExtValue genval(const Fortran::evaluate::Component &cmpt) { 1684 return genLoad(gen(cmpt)); 1685 } 1686 1687 ExtValue genval(const Fortran::semantics::Bound &bound) { 1688 TODO(getLoc(), "genval Bound"); 1689 } 1690 1691 /// Return lower bounds of \p box in dimension \p dim. The returned value 1692 /// has type \ty. 1693 mlir::Value getLBound(const ExtValue &box, unsigned dim, mlir::Type ty) { 1694 assert(box.rank() > 0 && "must be an array"); 1695 mlir::Location loc = getLoc(); 1696 mlir::Value one = builder.createIntegerConstant(loc, ty, 1); 1697 mlir::Value lb = fir::factory::readLowerBound(builder, loc, box, dim, one); 1698 return builder.createConvert(loc, ty, lb); 1699 } 1700 1701 static bool isSlice(const Fortran::evaluate::ArrayRef &aref) { 1702 for (const Fortran::evaluate::Subscript &sub : aref.subscript()) 1703 if (std::holds_alternative<Fortran::evaluate::Triplet>(sub.u)) 1704 return true; 1705 return false; 1706 } 1707 1708 /// Lower an ArrayRef to a fir.coordinate_of given its lowered base. 1709 ExtValue genCoordinateOp(const ExtValue &array, 1710 const Fortran::evaluate::ArrayRef &aref) { 1711 mlir::Location loc = getLoc(); 1712 // References to array of rank > 1 with non constant shape that are not 1713 // fir.box must be collapsed into an offset computation in lowering already. 1714 // The same is needed with dynamic length character arrays of all ranks. 1715 mlir::Type baseType = 1716 fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(array).getType()); 1717 if ((array.rank() > 1 && fir::hasDynamicSize(baseType)) || 1718 fir::characterWithDynamicLen(fir::unwrapSequenceType(baseType))) 1719 if (!array.getBoxOf<fir::BoxValue>()) 1720 return genOffsetAndCoordinateOp(array, aref); 1721 // Generate a fir.coordinate_of with zero based array indexes. 1722 llvm::SmallVector<mlir::Value> args; 1723 for (const auto &subsc : llvm::enumerate(aref.subscript())) { 1724 ExtValue subVal = genSubscript(subsc.value()); 1725 assert(fir::isUnboxedValue(subVal) && "subscript must be simple scalar"); 1726 mlir::Value val = fir::getBase(subVal); 1727 mlir::Type ty = val.getType(); 1728 mlir::Value lb = getLBound(array, subsc.index(), ty); 1729 args.push_back(builder.create<mlir::arith::SubIOp>(loc, ty, val, lb)); 1730 } 1731 1732 mlir::Value base = fir::getBase(array); 1733 auto seqTy = 1734 fir::dyn_cast_ptrOrBoxEleTy(base.getType()).cast<fir::SequenceType>(); 1735 assert(args.size() == seqTy.getDimension()); 1736 mlir::Type ty = builder.getRefType(seqTy.getEleTy()); 1737 auto addr = builder.create<fir::CoordinateOp>(loc, ty, base, args); 1738 return fir::factory::arrayElementToExtendedValue(builder, loc, array, addr); 1739 } 1740 1741 /// Lower an ArrayRef to a fir.coordinate_of using an element offset instead 1742 /// of array indexes. 1743 /// This generates offset computation from the indexes and length parameters, 1744 /// and use the offset to access the element with a fir.coordinate_of. This 1745 /// must only be used if it is not possible to generate a normal 1746 /// fir.coordinate_of using array indexes (i.e. when the shape information is 1747 /// unavailable in the IR). 1748 ExtValue genOffsetAndCoordinateOp(const ExtValue &array, 1749 const Fortran::evaluate::ArrayRef &aref) { 1750 mlir::Location loc = getLoc(); 1751 mlir::Value addr = fir::getBase(array); 1752 mlir::Type arrTy = fir::dyn_cast_ptrEleTy(addr.getType()); 1753 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy(); 1754 mlir::Type seqTy = builder.getRefType(builder.getVarLenSeqTy(eleTy)); 1755 mlir::Type refTy = builder.getRefType(eleTy); 1756 mlir::Value base = builder.createConvert(loc, seqTy, addr); 1757 mlir::IndexType idxTy = builder.getIndexType(); 1758 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); 1759 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); 1760 auto getLB = [&](const auto &arr, unsigned dim) -> mlir::Value { 1761 return arr.getLBounds().empty() ? one : arr.getLBounds()[dim]; 1762 }; 1763 auto genFullDim = [&](const auto &arr, mlir::Value delta) -> mlir::Value { 1764 mlir::Value total = zero; 1765 assert(arr.getExtents().size() == aref.subscript().size()); 1766 delta = builder.createConvert(loc, idxTy, delta); 1767 unsigned dim = 0; 1768 for (auto [ext, sub] : llvm::zip(arr.getExtents(), aref.subscript())) { 1769 ExtValue subVal = genSubscript(sub); 1770 assert(fir::isUnboxedValue(subVal)); 1771 mlir::Value val = 1772 builder.createConvert(loc, idxTy, fir::getBase(subVal)); 1773 mlir::Value lb = builder.createConvert(loc, idxTy, getLB(arr, dim)); 1774 mlir::Value diff = builder.create<mlir::arith::SubIOp>(loc, val, lb); 1775 mlir::Value prod = 1776 builder.create<mlir::arith::MulIOp>(loc, delta, diff); 1777 total = builder.create<mlir::arith::AddIOp>(loc, prod, total); 1778 if (ext) 1779 delta = builder.create<mlir::arith::MulIOp>(loc, delta, ext); 1780 ++dim; 1781 } 1782 mlir::Type origRefTy = refTy; 1783 if (fir::factory::CharacterExprHelper::isCharacterScalar(refTy)) { 1784 fir::CharacterType chTy = 1785 fir::factory::CharacterExprHelper::getCharacterType(refTy); 1786 if (fir::characterWithDynamicLen(chTy)) { 1787 mlir::MLIRContext *ctx = builder.getContext(); 1788 fir::KindTy kind = 1789 fir::factory::CharacterExprHelper::getCharacterKind(chTy); 1790 fir::CharacterType singleTy = 1791 fir::CharacterType::getSingleton(ctx, kind); 1792 refTy = builder.getRefType(singleTy); 1793 mlir::Type seqRefTy = 1794 builder.getRefType(builder.getVarLenSeqTy(singleTy)); 1795 base = builder.createConvert(loc, seqRefTy, base); 1796 } 1797 } 1798 auto coor = builder.create<fir::CoordinateOp>( 1799 loc, refTy, base, llvm::ArrayRef<mlir::Value>{total}); 1800 // Convert to expected, original type after address arithmetic. 1801 return builder.createConvert(loc, origRefTy, coor); 1802 }; 1803 return array.match( 1804 [&](const fir::ArrayBoxValue &arr) -> ExtValue { 1805 // FIXME: this check can be removed when slicing is implemented 1806 if (isSlice(aref)) 1807 fir::emitFatalError( 1808 getLoc(), 1809 "slice should be handled in array expression context"); 1810 return genFullDim(arr, one); 1811 }, 1812 [&](const fir::CharArrayBoxValue &arr) -> ExtValue { 1813 mlir::Value delta = arr.getLen(); 1814 // If the length is known in the type, fir.coordinate_of will 1815 // already take the length into account. 1816 if (fir::factory::CharacterExprHelper::hasConstantLengthInType(arr)) 1817 delta = one; 1818 return fir::CharBoxValue(genFullDim(arr, delta), arr.getLen()); 1819 }, 1820 [&](const fir::BoxValue &arr) -> ExtValue { 1821 // CoordinateOp for BoxValue is not generated here. The dimensions 1822 // must be kept in the fir.coordinate_op so that potential fir.box 1823 // strides can be applied by codegen. 1824 fir::emitFatalError( 1825 loc, "internal: BoxValue in dim-collapsed fir.coordinate_of"); 1826 }, 1827 [&](const auto &) -> ExtValue { 1828 fir::emitFatalError(loc, "internal: array lowering failed"); 1829 }); 1830 } 1831 1832 /// Lower an ArrayRef to a fir.array_coor. 1833 ExtValue genArrayCoorOp(const ExtValue &exv, 1834 const Fortran::evaluate::ArrayRef &aref) { 1835 mlir::Location loc = getLoc(); 1836 mlir::Value addr = fir::getBase(exv); 1837 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(addr.getType()); 1838 mlir::Type eleTy = arrTy.cast<fir::SequenceType>().getEleTy(); 1839 mlir::Type refTy = builder.getRefType(eleTy); 1840 mlir::IndexType idxTy = builder.getIndexType(); 1841 llvm::SmallVector<mlir::Value> arrayCoorArgs; 1842 // The ArrayRef is expected to be scalar here, arrays are handled in array 1843 // expression lowering. So no vector subscript or triplet is expected here. 1844 for (const auto &sub : aref.subscript()) { 1845 ExtValue subVal = genSubscript(sub); 1846 assert(fir::isUnboxedValue(subVal)); 1847 arrayCoorArgs.push_back( 1848 builder.createConvert(loc, idxTy, fir::getBase(subVal))); 1849 } 1850 mlir::Value shape = builder.createShape(loc, exv); 1851 mlir::Value elementAddr = builder.create<fir::ArrayCoorOp>( 1852 loc, refTy, addr, shape, /*slice=*/mlir::Value{}, arrayCoorArgs, 1853 fir::getTypeParams(exv)); 1854 return fir::factory::arrayElementToExtendedValue(builder, loc, exv, 1855 elementAddr); 1856 } 1857 1858 /// Return the coordinate of the array reference. 1859 ExtValue gen(const Fortran::evaluate::ArrayRef &aref) { 1860 ExtValue base = aref.base().IsSymbol() ? gen(getFirstSym(aref.base())) 1861 : gen(aref.base().GetComponent()); 1862 // Check for command-line override to use array_coor op. 1863 if (generateArrayCoordinate) 1864 return genArrayCoorOp(base, aref); 1865 // Otherwise, use coordinate_of op. 1866 return genCoordinateOp(base, aref); 1867 } 1868 1869 ExtValue genval(const Fortran::evaluate::ArrayRef &aref) { 1870 return genLoad(gen(aref)); 1871 } 1872 1873 ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) { 1874 TODO(getLoc(), "gen CoarrayRef"); 1875 } 1876 ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) { 1877 TODO(getLoc(), "genval CoarrayRef"); 1878 } 1879 1880 template <typename A> 1881 ExtValue gen(const Fortran::evaluate::Designator<A> &des) { 1882 return std::visit([&](const auto &x) { return gen(x); }, des.u); 1883 } 1884 template <typename A> 1885 ExtValue genval(const Fortran::evaluate::Designator<A> &des) { 1886 return std::visit([&](const auto &x) { return genval(x); }, des.u); 1887 } 1888 1889 mlir::Type genType(const Fortran::evaluate::DynamicType &dt) { 1890 if (dt.category() != Fortran::common::TypeCategory::Derived) 1891 return converter.genType(dt.category(), dt.kind()); 1892 return converter.genType(dt.GetDerivedTypeSpec()); 1893 } 1894 1895 /// Lower a function reference 1896 template <typename A> 1897 ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) { 1898 if (!funcRef.GetType().has_value()) 1899 fir::emitFatalError(getLoc(), "a function must have a type"); 1900 mlir::Type resTy = genType(*funcRef.GetType()); 1901 return genProcedureRef(funcRef, {resTy}); 1902 } 1903 1904 /// Lower function call `funcRef` and return a reference to the resultant 1905 /// value. This is required for lowering expressions such as `f1(f2(v))`. 1906 template <typename A> 1907 ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) { 1908 ExtValue retVal = genFunctionRef(funcRef); 1909 mlir::Type resultType = converter.genType(toEvExpr(funcRef)); 1910 return placeScalarValueInMemory(builder, getLoc(), retVal, resultType); 1911 } 1912 1913 /// helper to detect statement functions 1914 static bool 1915 isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) { 1916 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol()) 1917 if (const auto *details = 1918 symbol->detailsIf<Fortran::semantics::SubprogramDetails>()) 1919 return details->stmtFunction().has_value(); 1920 return false; 1921 } 1922 /// Generate Statement function calls 1923 ExtValue genStmtFunctionRef(const Fortran::evaluate::ProcedureRef &procRef) { 1924 const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol(); 1925 assert(symbol && "expected symbol in ProcedureRef of statement functions"); 1926 const auto &details = symbol->get<Fortran::semantics::SubprogramDetails>(); 1927 1928 // Statement functions have their own scope, we just need to associate 1929 // the dummy symbols to argument expressions. They are no 1930 // optional/alternate return arguments. Statement functions cannot be 1931 // recursive (directly or indirectly) so it is safe to add dummy symbols to 1932 // the local map here. 1933 symMap.pushScope(); 1934 for (auto [arg, bind] : 1935 llvm::zip(details.dummyArgs(), procRef.arguments())) { 1936 assert(arg && "alternate return in statement function"); 1937 assert(bind && "optional argument in statement function"); 1938 const auto *expr = bind->UnwrapExpr(); 1939 // TODO: assumed type in statement function, that surprisingly seems 1940 // allowed, probably because nobody thought of restricting this usage. 1941 // gfortran/ifort compiles this. 1942 assert(expr && "assumed type used as statement function argument"); 1943 // As per Fortran 2018 C1580, statement function arguments can only be 1944 // scalars, so just pass the box with the address. The only care is to 1945 // to use the dummy character explicit length if any instead of the 1946 // actual argument length (that can be bigger). 1947 if (const Fortran::semantics::DeclTypeSpec *type = arg->GetType()) 1948 if (type->category() == Fortran::semantics::DeclTypeSpec::Character) 1949 if (const Fortran::semantics::MaybeIntExpr &lenExpr = 1950 type->characterTypeSpec().length().GetExplicit()) { 1951 mlir::Value len = fir::getBase(genval(*lenExpr)); 1952 // F2018 7.4.4.2 point 5. 1953 len = Fortran::lower::genMaxWithZero(builder, getLoc(), len); 1954 symMap.addSymbol(*arg, 1955 replaceScalarCharacterLength(gen(*expr), len)); 1956 continue; 1957 } 1958 symMap.addSymbol(*arg, gen(*expr)); 1959 } 1960 1961 // Explicitly map statement function host associated symbols to their 1962 // parent scope lowered symbol box. 1963 for (const Fortran::semantics::SymbolRef &sym : 1964 Fortran::evaluate::CollectSymbols(*details.stmtFunction())) 1965 if (const auto *details = 1966 sym->detailsIf<Fortran::semantics::HostAssocDetails>()) 1967 if (!symMap.lookupSymbol(*sym)) 1968 symMap.addSymbol(*sym, gen(details->symbol())); 1969 1970 ExtValue result = genval(details.stmtFunction().value()); 1971 LLVM_DEBUG(llvm::dbgs() << "stmt-function: " << result << '\n'); 1972 symMap.popScope(); 1973 return result; 1974 } 1975 1976 /// Helper to package a Value and its properties into an ExtendedValue. 1977 static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base, 1978 llvm::ArrayRef<mlir::Value> extents, 1979 llvm::ArrayRef<mlir::Value> lengths) { 1980 mlir::Type type = base.getType(); 1981 if (type.isa<fir::BoxType>()) 1982 return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents); 1983 type = fir::unwrapRefType(type); 1984 if (type.isa<fir::BoxType>()) 1985 return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {}); 1986 if (auto seqTy = type.dyn_cast<fir::SequenceType>()) { 1987 if (seqTy.getDimension() != extents.size()) 1988 fir::emitFatalError(loc, "incorrect number of extents for array"); 1989 if (seqTy.getEleTy().isa<fir::CharacterType>()) { 1990 if (lengths.empty()) 1991 fir::emitFatalError(loc, "missing length for character"); 1992 assert(lengths.size() == 1); 1993 return fir::CharArrayBoxValue(base, lengths[0], extents); 1994 } 1995 return fir::ArrayBoxValue(base, extents); 1996 } 1997 if (type.isa<fir::CharacterType>()) { 1998 if (lengths.empty()) 1999 fir::emitFatalError(loc, "missing length for character"); 2000 assert(lengths.size() == 1); 2001 return fir::CharBoxValue(base, lengths[0]); 2002 } 2003 return base; 2004 } 2005 2006 // Find the argument that corresponds to the host associations. 2007 // Verify some assumptions about how the signature was built here. 2008 [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::FuncOp fn) { 2009 // Scan the argument list from last to first as the host associations are 2010 // appended for now. 2011 for (unsigned i = fn.getNumArguments(); i > 0; --i) 2012 if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) { 2013 // Host assoc tuple must be last argument (for now). 2014 assert(i == fn.getNumArguments() && "tuple must be last"); 2015 return i - 1; 2016 } 2017 llvm_unreachable("anyFuncArgsHaveAttr failed"); 2018 } 2019 2020 /// Create a contiguous temporary array with the same shape, 2021 /// length parameters and type as mold. It is up to the caller to deallocate 2022 /// the temporary. 2023 ExtValue genArrayTempFromMold(const ExtValue &mold, 2024 llvm::StringRef tempName) { 2025 mlir::Type type = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(mold).getType()); 2026 assert(type && "expected descriptor or memory type"); 2027 mlir::Location loc = getLoc(); 2028 llvm::SmallVector<mlir::Value> extents = 2029 fir::factory::getExtents(builder, loc, mold); 2030 llvm::SmallVector<mlir::Value> allocMemTypeParams = 2031 fir::getTypeParams(mold); 2032 mlir::Value charLen; 2033 mlir::Type elementType = fir::unwrapSequenceType(type); 2034 if (auto charType = elementType.dyn_cast<fir::CharacterType>()) { 2035 charLen = allocMemTypeParams.empty() 2036 ? fir::factory::readCharLen(builder, loc, mold) 2037 : allocMemTypeParams[0]; 2038 if (charType.hasDynamicLen() && allocMemTypeParams.empty()) 2039 allocMemTypeParams.push_back(charLen); 2040 } else if (fir::hasDynamicSize(elementType)) { 2041 TODO(loc, "Creating temporary for derived type with length parameters"); 2042 } 2043 2044 mlir::Value temp = builder.create<fir::AllocMemOp>( 2045 loc, type, tempName, allocMemTypeParams, extents); 2046 if (fir::unwrapSequenceType(type).isa<fir::CharacterType>()) 2047 return fir::CharArrayBoxValue{temp, charLen, extents}; 2048 return fir::ArrayBoxValue{temp, extents}; 2049 } 2050 2051 /// Copy \p source array into \p dest array. Both arrays must be 2052 /// conforming, but neither array must be contiguous. 2053 void genArrayCopy(ExtValue dest, ExtValue source) { 2054 return createSomeArrayAssignment(converter, dest, source, symMap, stmtCtx); 2055 } 2056 2057 /// Lower a non-elemental procedure reference and read allocatable and pointer 2058 /// results into normal values. 2059 ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef, 2060 llvm::Optional<mlir::Type> resultType) { 2061 ExtValue res = genRawProcedureRef(procRef, resultType); 2062 // In most contexts, pointers and allocatable do not appear as allocatable 2063 // or pointer variable on the caller side (see 8.5.3 note 1 for 2064 // allocatables). The few context where this can happen must call 2065 // genRawProcedureRef directly. 2066 if (const auto *box = res.getBoxOf<fir::MutableBoxValue>()) 2067 return fir::factory::genMutableBoxRead(builder, getLoc(), *box); 2068 return res; 2069 } 2070 2071 /// Given a call site for which the arguments were already lowered, generate 2072 /// the call and return the result. This function deals with explicit result 2073 /// allocation and lowering if needed. It also deals with passing the host 2074 /// link to internal procedures. 2075 ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller, 2076 mlir::FunctionType callSiteType, 2077 llvm::Optional<mlir::Type> resultType) { 2078 mlir::Location loc = getLoc(); 2079 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 2080 // Handle cases where caller must allocate the result or a fir.box for it. 2081 bool mustPopSymMap = false; 2082 if (caller.mustMapInterfaceSymbols()) { 2083 symMap.pushScope(); 2084 mustPopSymMap = true; 2085 Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap); 2086 } 2087 // If this is an indirect call, retrieve the function address. Also retrieve 2088 // the result length if this is a character function (note that this length 2089 // will be used only if there is no explicit length in the local interface). 2090 mlir::Value funcPointer; 2091 mlir::Value charFuncPointerLength; 2092 if (const Fortran::semantics::Symbol *sym = 2093 caller.getIfIndirectCallSymbol()) { 2094 funcPointer = symMap.lookupSymbol(*sym).getAddr(); 2095 if (!funcPointer) 2096 fir::emitFatalError(loc, "failed to find indirect call symbol address"); 2097 if (fir::isCharacterProcedureTuple(funcPointer.getType(), 2098 /*acceptRawFunc=*/false)) 2099 std::tie(funcPointer, charFuncPointerLength) = 2100 fir::factory::extractCharacterProcedureTuple(builder, loc, 2101 funcPointer); 2102 } 2103 2104 mlir::IndexType idxTy = builder.getIndexType(); 2105 auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value { 2106 return builder.createConvert( 2107 loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx))); 2108 }; 2109 llvm::SmallVector<mlir::Value> resultLengths; 2110 auto allocatedResult = [&]() -> llvm::Optional<ExtValue> { 2111 llvm::SmallVector<mlir::Value> extents; 2112 llvm::SmallVector<mlir::Value> lengths; 2113 if (!caller.callerAllocateResult()) 2114 return {}; 2115 mlir::Type type = caller.getResultStorageType(); 2116 if (type.isa<fir::SequenceType>()) 2117 caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) { 2118 extents.emplace_back(lowerSpecExpr(e)); 2119 }); 2120 caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) { 2121 lengths.emplace_back(lowerSpecExpr(e)); 2122 }); 2123 2124 // Result length parameters should not be provided to box storage 2125 // allocation and save_results, but they are still useful information to 2126 // keep in the ExtendedValue if non-deferred. 2127 if (!type.isa<fir::BoxType>()) { 2128 if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) { 2129 // Calling an assumed length function. This is only possible if this 2130 // is a call to a character dummy procedure. 2131 if (!charFuncPointerLength) 2132 fir::emitFatalError(loc, "failed to retrieve character function " 2133 "length while calling it"); 2134 lengths.push_back(charFuncPointerLength); 2135 } 2136 resultLengths = lengths; 2137 } 2138 2139 if (!extents.empty() || !lengths.empty()) { 2140 auto *bldr = &converter.getFirOpBuilder(); 2141 auto stackSaveFn = fir::factory::getLlvmStackSave(builder); 2142 auto stackSaveSymbol = bldr->getSymbolRefAttr(stackSaveFn.getName()); 2143 mlir::Value sp = 2144 bldr->create<fir::CallOp>( 2145 loc, stackSaveFn.getFunctionType().getResults(), 2146 stackSaveSymbol, mlir::ValueRange{}) 2147 .getResult(0); 2148 stmtCtx.attachCleanup([bldr, loc, sp]() { 2149 auto stackRestoreFn = fir::factory::getLlvmStackRestore(*bldr); 2150 auto stackRestoreSymbol = 2151 bldr->getSymbolRefAttr(stackRestoreFn.getName()); 2152 bldr->create<fir::CallOp>( 2153 loc, stackRestoreFn.getFunctionType().getResults(), 2154 stackRestoreSymbol, mlir::ValueRange{sp}); 2155 }); 2156 } 2157 mlir::Value temp = 2158 builder.createTemporary(loc, type, ".result", extents, resultLengths); 2159 return toExtendedValue(loc, temp, extents, lengths); 2160 }(); 2161 2162 if (mustPopSymMap) 2163 symMap.popScope(); 2164 2165 // Place allocated result or prepare the fir.save_result arguments. 2166 mlir::Value arrayResultShape; 2167 if (allocatedResult) { 2168 if (std::optional<Fortran::lower::CallInterface< 2169 Fortran::lower::CallerInterface>::PassedEntity> 2170 resultArg = caller.getPassedResult()) { 2171 if (resultArg->passBy == PassBy::AddressAndLength) 2172 caller.placeAddressAndLengthInput(*resultArg, 2173 fir::getBase(*allocatedResult), 2174 fir::getLen(*allocatedResult)); 2175 else if (resultArg->passBy == PassBy::BaseAddress) 2176 caller.placeInput(*resultArg, fir::getBase(*allocatedResult)); 2177 else 2178 fir::emitFatalError( 2179 loc, "only expect character scalar result to be passed by ref"); 2180 } else { 2181 assert(caller.mustSaveResult()); 2182 arrayResultShape = allocatedResult->match( 2183 [&](const fir::CharArrayBoxValue &) { 2184 return builder.createShape(loc, *allocatedResult); 2185 }, 2186 [&](const fir::ArrayBoxValue &) { 2187 return builder.createShape(loc, *allocatedResult); 2188 }, 2189 [&](const auto &) { return mlir::Value{}; }); 2190 } 2191 } 2192 2193 // In older Fortran, procedure argument types are inferred. This may lead 2194 // different view of what the function signature is in different locations. 2195 // Casts are inserted as needed below to accommodate this. 2196 2197 // The mlir::FuncOp type prevails, unless it has a different number of 2198 // arguments which can happen in legal program if it was passed as a dummy 2199 // procedure argument earlier with no further type information. 2200 mlir::SymbolRefAttr funcSymbolAttr; 2201 bool addHostAssociations = false; 2202 if (!funcPointer) { 2203 mlir::FunctionType funcOpType = caller.getFuncOp().getFunctionType(); 2204 mlir::SymbolRefAttr symbolAttr = 2205 builder.getSymbolRefAttr(caller.getMangledName()); 2206 if (callSiteType.getNumResults() == funcOpType.getNumResults() && 2207 callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() && 2208 fir::anyFuncArgsHaveAttr(caller.getFuncOp(), 2209 fir::getHostAssocAttrName())) { 2210 // The number of arguments is off by one, and we're lowering a function 2211 // with host associations. Modify call to include host associations 2212 // argument by appending the value at the end of the operands. 2213 assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) == 2214 converter.hostAssocTupleValue().getType()); 2215 addHostAssociations = true; 2216 } 2217 if (!addHostAssociations && 2218 (callSiteType.getNumResults() != funcOpType.getNumResults() || 2219 callSiteType.getNumInputs() != funcOpType.getNumInputs())) { 2220 // Deal with argument number mismatch by making a function pointer so 2221 // that function type cast can be inserted. Do not emit a warning here 2222 // because this can happen in legal program if the function is not 2223 // defined here and it was first passed as an argument without any more 2224 // information. 2225 funcPointer = 2226 builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr); 2227 } else if (callSiteType.getResults() != funcOpType.getResults()) { 2228 // Implicit interface result type mismatch are not standard Fortran, but 2229 // some compilers are not complaining about it. The front end is not 2230 // protecting lowering from this currently. Support this with a 2231 // discouraging warning. 2232 LLVM_DEBUG(mlir::emitWarning( 2233 loc, "a return type mismatch is not standard compliant and may " 2234 "lead to undefined behavior.")); 2235 // Cast the actual function to the current caller implicit type because 2236 // that is the behavior we would get if we could not see the definition. 2237 funcPointer = 2238 builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr); 2239 } else { 2240 funcSymbolAttr = symbolAttr; 2241 } 2242 } 2243 2244 mlir::FunctionType funcType = 2245 funcPointer ? callSiteType : caller.getFuncOp().getFunctionType(); 2246 llvm::SmallVector<mlir::Value> operands; 2247 // First operand of indirect call is the function pointer. Cast it to 2248 // required function type for the call to handle procedures that have a 2249 // compatible interface in Fortran, but that have different signatures in 2250 // FIR. 2251 if (funcPointer) { 2252 operands.push_back( 2253 funcPointer.getType().isa<fir::BoxProcType>() 2254 ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer) 2255 : builder.createConvert(loc, funcType, funcPointer)); 2256 } 2257 2258 // Deal with potential mismatches in arguments types. Passing an array to a 2259 // scalar argument should for instance be tolerated here. 2260 bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface(); 2261 for (auto [fst, snd] : 2262 llvm::zip(caller.getInputs(), funcType.getInputs())) { 2263 // When passing arguments to a procedure that can be called an implicit 2264 // interface, allow character actual arguments to be passed to dummy 2265 // arguments of any type and vice versa 2266 mlir::Value cast; 2267 auto *context = builder.getContext(); 2268 if (snd.isa<fir::BoxProcType>() && 2269 fst.getType().isa<mlir::FunctionType>()) { 2270 auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None); 2271 auto boxProcTy = builder.getBoxProcType(funcTy); 2272 if (mlir::Value host = argumentHostAssocs(converter, fst)) { 2273 cast = builder.create<fir::EmboxProcOp>( 2274 loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host}); 2275 } else { 2276 cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst); 2277 } 2278 } else { 2279 cast = builder.convertWithSemantics(loc, snd, fst, 2280 callingImplicitInterface); 2281 } 2282 operands.push_back(cast); 2283 } 2284 2285 // Add host associations as necessary. 2286 if (addHostAssociations) 2287 operands.push_back(converter.hostAssocTupleValue()); 2288 2289 auto call = builder.create<fir::CallOp>(loc, funcType.getResults(), 2290 funcSymbolAttr, operands); 2291 2292 if (caller.mustSaveResult()) 2293 builder.create<fir::SaveResultOp>( 2294 loc, call.getResult(0), fir::getBase(allocatedResult.getValue()), 2295 arrayResultShape, resultLengths); 2296 2297 if (allocatedResult) { 2298 allocatedResult->match( 2299 [&](const fir::MutableBoxValue &box) { 2300 if (box.isAllocatable()) { 2301 // 9.7.3.2 point 4. Finalize allocatables. 2302 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 2303 stmtCtx.attachCleanup([bldr, loc, box]() { 2304 fir::factory::genFinalization(*bldr, loc, box); 2305 }); 2306 } 2307 }, 2308 [](const auto &) {}); 2309 return *allocatedResult; 2310 } 2311 2312 if (!resultType.hasValue()) 2313 return mlir::Value{}; // subroutine call 2314 // For now, Fortran return values are implemented with a single MLIR 2315 // function return value. 2316 assert(call.getNumResults() == 1 && 2317 "Expected exactly one result in FUNCTION call"); 2318 return call.getResult(0); 2319 } 2320 2321 /// Like genExtAddr, but ensure the address returned is a temporary even if \p 2322 /// expr is variable inside parentheses. 2323 ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) { 2324 // In general, genExtAddr might not create a temp for variable inside 2325 // parentheses to avoid creating array temporary in sub-expressions. It only 2326 // ensures the sub-expression is not re-associated with other parts of the 2327 // expression. In the call semantics, there is a difference between expr and 2328 // variable (see R1524). For expressions, a variable storage must not be 2329 // argument associated since it could be modified inside the call, or the 2330 // variable could also be modified by other means during the call. 2331 if (!isParenthesizedVariable(expr)) 2332 return genExtAddr(expr); 2333 mlir::Location loc = getLoc(); 2334 if (expr.Rank() > 0) 2335 TODO(loc, "genTempExtAddr array"); 2336 return genExtValue(expr).match( 2337 [&](const fir::CharBoxValue &boxChar) -> ExtValue { 2338 TODO(loc, "genTempExtAddr CharBoxValue"); 2339 }, 2340 [&](const fir::UnboxedValue &v) -> ExtValue { 2341 mlir::Type type = v.getType(); 2342 mlir::Value value = v; 2343 if (fir::isa_ref_type(type)) 2344 value = builder.create<fir::LoadOp>(loc, value); 2345 mlir::Value temp = builder.createTemporary(loc, value.getType()); 2346 builder.create<fir::StoreOp>(loc, value, temp); 2347 return temp; 2348 }, 2349 [&](const fir::BoxValue &x) -> ExtValue { 2350 // Derived type scalar that may be polymorphic. 2351 assert(!x.hasRank() && x.isDerived()); 2352 if (x.isDerivedWithLengthParameters()) 2353 fir::emitFatalError( 2354 loc, "making temps for derived type with length parameters"); 2355 // TODO: polymorphic aspects should be kept but for now the temp 2356 // created always has the declared type. 2357 mlir::Value var = 2358 fir::getBase(fir::factory::readBoxValue(builder, loc, x)); 2359 auto value = builder.create<fir::LoadOp>(loc, var); 2360 mlir::Value temp = builder.createTemporary(loc, value.getType()); 2361 builder.create<fir::StoreOp>(loc, value, temp); 2362 return temp; 2363 }, 2364 [&](const auto &) -> ExtValue { 2365 fir::emitFatalError(loc, "expr is not a scalar value"); 2366 }); 2367 } 2368 2369 /// Helper structure to track potential copy-in of non contiguous variable 2370 /// argument into a contiguous temp. It is used to deallocate the temp that 2371 /// may have been created as well as to the copy-out from the temp to the 2372 /// variable after the call. 2373 struct CopyOutPair { 2374 ExtValue var; 2375 ExtValue temp; 2376 // Flag to indicate if the argument may have been modified by the 2377 // callee, in which case it must be copied-out to the variable. 2378 bool argMayBeModifiedByCall; 2379 // Optional boolean value that, if present and false, prevents 2380 // the copy-out and temp deallocation. 2381 llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime; 2382 }; 2383 using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>; 2384 2385 /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories 2386 /// not based on fir.box. 2387 /// This will lose any non contiguous stride information and dynamic type and 2388 /// should only be called if \p exv is known to be contiguous or if its base 2389 /// address will be replaced by a contiguous one. If \p exv is not a 2390 /// fir::BoxValue, this is a no-op. 2391 ExtValue readIfBoxValue(const ExtValue &exv) { 2392 if (const auto *box = exv.getBoxOf<fir::BoxValue>()) 2393 return fir::factory::readBoxValue(builder, getLoc(), *box); 2394 return exv; 2395 } 2396 2397 /// Generate a contiguous temp to pass \p actualArg as argument \p arg. The 2398 /// creation of the temp and copy-in can be made conditional at runtime by 2399 /// providing a runtime boolean flag \p restrictCopyAtRuntime (in which case 2400 /// the temp and copy will only be made if the value is true at runtime). 2401 ExtValue genCopyIn(const ExtValue &actualArg, 2402 const Fortran::lower::CallerInterface::PassedEntity &arg, 2403 CopyOutPairs ©OutPairs, 2404 llvm::Optional<mlir::Value> restrictCopyAtRuntime) { 2405 if (!restrictCopyAtRuntime) { 2406 ExtValue temp = genArrayTempFromMold(actualArg, ".copyinout"); 2407 if (arg.mayBeReadByCall()) 2408 genArrayCopy(temp, actualArg); 2409 copyOutPairs.emplace_back(CopyOutPair{ 2410 actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime}); 2411 return temp; 2412 } 2413 // Otherwise, need to be careful to only copy-in if allowed at runtime. 2414 mlir::Location loc = getLoc(); 2415 auto addrType = fir::HeapType::get( 2416 fir::unwrapPassByRefType(fir::getBase(actualArg).getType())); 2417 mlir::Value addr = 2418 builder 2419 .genIfOp(loc, {addrType}, *restrictCopyAtRuntime, 2420 /*withElseRegion=*/true) 2421 .genThen([&]() { 2422 auto temp = genArrayTempFromMold(actualArg, ".copyinout"); 2423 if (arg.mayBeReadByCall()) 2424 genArrayCopy(temp, actualArg); 2425 builder.create<fir::ResultOp>(loc, fir::getBase(temp)); 2426 }) 2427 .genElse([&]() { 2428 auto nullPtr = builder.createNullConstant(loc, addrType); 2429 builder.create<fir::ResultOp>(loc, nullPtr); 2430 }) 2431 .getResults()[0]; 2432 // Associate the temp address with actualArg lengths and extents. 2433 fir::ExtendedValue temp = fir::substBase(readIfBoxValue(actualArg), addr); 2434 copyOutPairs.emplace_back(CopyOutPair{ 2435 actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime}); 2436 return temp; 2437 } 2438 2439 /// Generate copy-out if needed and free the temporary for an argument that 2440 /// has been copied-in into a contiguous temp. 2441 void genCopyOut(const CopyOutPair ©OutPair) { 2442 mlir::Location loc = getLoc(); 2443 if (!copyOutPair.restrictCopyAndFreeAtRuntime) { 2444 if (copyOutPair.argMayBeModifiedByCall) 2445 genArrayCopy(copyOutPair.var, copyOutPair.temp); 2446 builder.create<fir::FreeMemOp>(loc, fir::getBase(copyOutPair.temp)); 2447 return; 2448 } 2449 builder.genIfThen(loc, *copyOutPair.restrictCopyAndFreeAtRuntime) 2450 .genThen([&]() { 2451 if (copyOutPair.argMayBeModifiedByCall) 2452 genArrayCopy(copyOutPair.var, copyOutPair.temp); 2453 builder.create<fir::FreeMemOp>(loc, fir::getBase(copyOutPair.temp)); 2454 }) 2455 .end(); 2456 } 2457 2458 /// Lower a non-elemental procedure reference. 2459 ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef, 2460 llvm::Optional<mlir::Type> resultType) { 2461 mlir::Location loc = getLoc(); 2462 if (isElementalProcWithArrayArgs(procRef)) 2463 fir::emitFatalError(loc, "trying to lower elemental procedure with array " 2464 "arguments as normal procedure"); 2465 if (const Fortran::evaluate::SpecificIntrinsic *intrinsic = 2466 procRef.proc().GetSpecificIntrinsic()) 2467 return genIntrinsicRef(procRef, *intrinsic, resultType); 2468 2469 if (isStatementFunctionCall(procRef)) 2470 return genStmtFunctionRef(procRef); 2471 2472 Fortran::lower::CallerInterface caller(procRef, converter); 2473 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 2474 2475 llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall; 2476 // List of <var, temp> where temp must be copied into var after the call. 2477 CopyOutPairs copyOutPairs; 2478 2479 mlir::FunctionType callSiteType = caller.genFunctionType(); 2480 2481 // Lower the actual arguments and map the lowered values to the dummy 2482 // arguments. 2483 for (const Fortran::lower::CallInterface< 2484 Fortran::lower::CallerInterface>::PassedEntity &arg : 2485 caller.getPassedArguments()) { 2486 const auto *actual = arg.entity; 2487 mlir::Type argTy = callSiteType.getInput(arg.firArgument); 2488 if (!actual) { 2489 // Optional dummy argument for which there is no actual argument. 2490 caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy)); 2491 continue; 2492 } 2493 const auto *expr = actual->UnwrapExpr(); 2494 if (!expr) 2495 TODO(loc, "assumed type actual argument lowering"); 2496 2497 if (arg.passBy == PassBy::Value) { 2498 ExtValue argVal = genval(*expr); 2499 if (!fir::isUnboxedValue(argVal)) 2500 fir::emitFatalError( 2501 loc, "internal error: passing non trivial value by value"); 2502 caller.placeInput(arg, fir::getBase(argVal)); 2503 continue; 2504 } 2505 2506 if (arg.passBy == PassBy::MutableBox) { 2507 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 2508 *expr)) { 2509 // If expr is NULL(), the mutableBox created must be a deallocated 2510 // pointer with the dummy argument characteristics (see table 16.5 2511 // in Fortran 2018 standard). 2512 // No length parameters are set for the created box because any non 2513 // deferred type parameters of the dummy will be evaluated on the 2514 // callee side, and it is illegal to use NULL without a MOLD if any 2515 // dummy length parameters are assumed. 2516 mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy); 2517 assert(boxTy && boxTy.isa<fir::BoxType>() && 2518 "must be a fir.box type"); 2519 mlir::Value boxStorage = builder.createTemporary(loc, boxTy); 2520 mlir::Value nullBox = fir::factory::createUnallocatedBox( 2521 builder, loc, boxTy, /*nonDeferredParams=*/{}); 2522 builder.create<fir::StoreOp>(loc, nullBox, boxStorage); 2523 caller.placeInput(arg, boxStorage); 2524 continue; 2525 } 2526 if (fir::isPointerType(argTy) && 2527 !Fortran::evaluate::IsObjectPointer( 2528 *expr, converter.getFoldingContext())) { 2529 // Passing a non POINTER actual argument to a POINTER dummy argument. 2530 // Create a pointer of the dummy argument type and assign the actual 2531 // argument to it. 2532 mlir::Value irBox = 2533 builder.createTemporary(loc, fir::unwrapRefType(argTy)); 2534 // Non deferred parameters will be evaluated on the callee side. 2535 fir::MutableBoxValue pointer(irBox, 2536 /*nonDeferredParams=*/mlir::ValueRange{}, 2537 /*mutableProperties=*/{}); 2538 Fortran::lower::associateMutableBox(converter, loc, pointer, *expr, 2539 /*lbounds*/ mlir::ValueRange{}, 2540 stmtCtx); 2541 caller.placeInput(arg, irBox); 2542 continue; 2543 } 2544 // Passing a POINTER to a POINTER, or an ALLOCATABLE to an ALLOCATABLE. 2545 fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr); 2546 mlir::Value irBox = 2547 fir::factory::getMutableIRBox(builder, loc, mutableBox); 2548 caller.placeInput(arg, irBox); 2549 if (arg.mayBeModifiedByCall()) 2550 mutableModifiedByCall.emplace_back(std::move(mutableBox)); 2551 continue; 2552 } 2553 const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr); 2554 if (arg.passBy == PassBy::BaseAddressValueAttribute) { 2555 mlir::Value temp; 2556 if (isArray(*expr)) { 2557 auto val = genBoxArg(*expr); 2558 if (!actualArgIsVariable) 2559 temp = getBase(val); 2560 else { 2561 ExtValue copy = genArrayTempFromMold(val, ".copy"); 2562 genArrayCopy(copy, val); 2563 temp = fir::getBase(copy); 2564 } 2565 } else { 2566 mlir::Value val = fir::getBase(genval(*expr)); 2567 temp = builder.createTemporary( 2568 loc, val.getType(), 2569 llvm::ArrayRef<mlir::NamedAttribute>{ 2570 Fortran::lower::getAdaptToByRefAttr(builder)}); 2571 builder.create<fir::StoreOp>(loc, val, temp); 2572 } 2573 caller.placeInput(arg, temp); 2574 continue; 2575 } 2576 if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) { 2577 const bool actualIsSimplyContiguous = 2578 !actualArgIsVariable || Fortran::evaluate::IsSimplyContiguous( 2579 *expr, converter.getFoldingContext()); 2580 auto argAddr = [&]() -> ExtValue { 2581 ExtValue baseAddr; 2582 if (actualArgIsVariable && arg.isOptional()) { 2583 if (Fortran::evaluate::IsAllocatableOrPointerObject( 2584 *expr, converter.getFoldingContext())) { 2585 // Fortran 2018 15.5.2.12 point 1: If unallocated/disassociated, 2586 // it is as if the argument was absent. The main care here is to 2587 // not do a copy-in/copy-out because the temp address, even though 2588 // pointing to a null size storage, would not be a nullptr and 2589 // therefore the argument would not be considered absent on the 2590 // callee side. Note: if wholeSymbol is optional, it cannot be 2591 // absent as per 15.5.2.12 point 7. and 8. We rely on this to 2592 // un-conditionally read the allocatable/pointer descriptor here. 2593 if (actualIsSimplyContiguous) 2594 return genBoxArg(*expr); 2595 fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr); 2596 mlir::Value isAssociated = 2597 fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, 2598 mutableBox); 2599 fir::ExtendedValue actualExv = 2600 fir::factory::genMutableBoxRead(builder, loc, mutableBox); 2601 return genCopyIn(actualExv, arg, copyOutPairs, isAssociated); 2602 } 2603 if (const Fortran::semantics::Symbol *wholeSymbol = 2604 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef( 2605 *expr)) 2606 if (Fortran::semantics::IsOptional(*wholeSymbol)) { 2607 ExtValue actualArg = gen(*expr); 2608 mlir::Value actualArgBase = fir::getBase(actualArg); 2609 if (!actualArgBase.getType().isa<fir::BoxType>()) 2610 return actualArg; 2611 // Do not read wholeSymbol descriptor that may be a nullptr in 2612 // case wholeSymbol is absent. 2613 // Absent descriptor cannot be read. To avoid any issue in 2614 // copy-in/copy-out, and when retrieving the address/length 2615 // create an descriptor pointing to a null address here if the 2616 // fir.box is absent. 2617 mlir::Value isPresent = builder.create<fir::IsPresentOp>( 2618 loc, builder.getI1Type(), actualArgBase); 2619 mlir::Type boxType = actualArgBase.getType(); 2620 mlir::Value emptyBox = fir::factory::createUnallocatedBox( 2621 builder, loc, boxType, llvm::None); 2622 auto safeToReadBox = builder.create<mlir::arith::SelectOp>( 2623 loc, isPresent, actualArgBase, emptyBox); 2624 fir::ExtendedValue safeToReadExv = 2625 fir::substBase(actualArg, safeToReadBox); 2626 if (actualIsSimplyContiguous) 2627 return safeToReadExv; 2628 return genCopyIn(safeToReadExv, arg, copyOutPairs, isPresent); 2629 } 2630 // Fall through: The actual argument can safely be 2631 // copied-in/copied-out without any care if needed. 2632 } 2633 if (actualArgIsVariable && expr->Rank() > 0) { 2634 ExtValue box = genBoxArg(*expr); 2635 if (!actualIsSimplyContiguous) 2636 return genCopyIn(box, arg, copyOutPairs, 2637 /*restrictCopyAtRuntime=*/llvm::None); 2638 // Contiguous: just use the box we created above! 2639 // This gets "unboxed" below, if needed. 2640 return box; 2641 } 2642 // Actual argument is a non optional/non pointer/non allocatable 2643 // scalar. 2644 if (actualArgIsVariable) 2645 return genExtAddr(*expr); 2646 // Actual argument is not a variable. Make sure a variable address is 2647 // not passed. 2648 return genTempExtAddr(*expr); 2649 }(); 2650 // Scalar and contiguous expressions may be lowered to a fir.box, 2651 // either to account for potential polymorphism, or because lowering 2652 // did not account for some contiguity hints. 2653 // Here, polymorphism does not matter (an entity of the declared type 2654 // is passed, not one of the dynamic type), and the expr is known to 2655 // be simply contiguous, so it is safe to unbox it and pass the 2656 // address without making a copy. 2657 argAddr = readIfBoxValue(argAddr); 2658 2659 if (arg.passBy == PassBy::BaseAddress) { 2660 caller.placeInput(arg, fir::getBase(argAddr)); 2661 } else { 2662 assert(arg.passBy == PassBy::BoxChar); 2663 auto helper = fir::factory::CharacterExprHelper{builder, loc}; 2664 auto boxChar = argAddr.match( 2665 [&](const fir::CharBoxValue &x) { return helper.createEmbox(x); }, 2666 [&](const fir::CharArrayBoxValue &x) { 2667 return helper.createEmbox(x); 2668 }, 2669 [&](const auto &x) -> mlir::Value { 2670 // Fortran allows an actual argument of a completely different 2671 // type to be passed to a procedure expecting a CHARACTER in the 2672 // dummy argument position. When this happens, the data pointer 2673 // argument is simply assumed to point to CHARACTER data and the 2674 // LEN argument used is garbage. Simulate this behavior by 2675 // free-casting the base address to be a !fir.char reference and 2676 // setting the LEN argument to undefined. What could go wrong? 2677 auto dataPtr = fir::getBase(x); 2678 assert(!dataPtr.getType().template isa<fir::BoxType>()); 2679 return builder.convertWithSemantics( 2680 loc, argTy, dataPtr, 2681 /*allowCharacterConversion=*/true); 2682 }); 2683 caller.placeInput(arg, boxChar); 2684 } 2685 } else if (arg.passBy == PassBy::Box) { 2686 // Before lowering to an address, handle the allocatable/pointer actual 2687 // argument to optional fir.box dummy. It is legal to pass 2688 // unallocated/disassociated entity to an optional. In this case, an 2689 // absent fir.box must be created instead of a fir.box with a null value 2690 // (Fortran 2018 15.5.2.12 point 1). 2691 if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject( 2692 *expr, converter.getFoldingContext())) { 2693 // Note that passing an absent allocatable to a non-allocatable 2694 // optional dummy argument is illegal (15.5.2.12 point 3 (8)). So 2695 // nothing has to be done to generate an absent argument in this case, 2696 // and it is OK to unconditionally read the mutable box here. 2697 fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr); 2698 mlir::Value isAllocated = 2699 fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, 2700 mutableBox); 2701 auto absent = builder.create<fir::AbsentOp>(loc, argTy); 2702 /// For now, assume it is not OK to pass the allocatable/pointer 2703 /// descriptor to a non pointer/allocatable dummy. That is a strict 2704 /// interpretation of 18.3.6 point 4 that stipulates the descriptor 2705 /// has the dummy attributes in BIND(C) contexts. 2706 mlir::Value box = builder.createBox( 2707 loc, fir::factory::genMutableBoxRead(builder, loc, mutableBox)); 2708 // Need the box types to be exactly similar for the selectOp. 2709 mlir::Value convertedBox = builder.createConvert(loc, argTy, box); 2710 caller.placeInput(arg, builder.create<mlir::arith::SelectOp>( 2711 loc, isAllocated, convertedBox, absent)); 2712 } else { 2713 // Make sure a variable address is only passed if the expression is 2714 // actually a variable. 2715 mlir::Value box = 2716 actualArgIsVariable 2717 ? builder.createBox(loc, genBoxArg(*expr)) 2718 : builder.createBox(getLoc(), genTempExtAddr(*expr)); 2719 caller.placeInput(arg, box); 2720 } 2721 } else if (arg.passBy == PassBy::AddressAndLength) { 2722 ExtValue argRef = genExtAddr(*expr); 2723 caller.placeAddressAndLengthInput(arg, fir::getBase(argRef), 2724 fir::getLen(argRef)); 2725 } else if (arg.passBy == PassBy::CharProcTuple) { 2726 ExtValue argRef = genExtAddr(*expr); 2727 mlir::Value tuple = createBoxProcCharTuple( 2728 converter, argTy, fir::getBase(argRef), fir::getLen(argRef)); 2729 caller.placeInput(arg, tuple); 2730 } else { 2731 TODO(loc, "pass by value in non elemental function call"); 2732 } 2733 } 2734 2735 ExtValue result = genCallOpAndResult(caller, callSiteType, resultType); 2736 2737 // Sync pointers and allocatables that may have been modified during the 2738 // call. 2739 for (const auto &mutableBox : mutableModifiedByCall) 2740 fir::factory::syncMutableBoxFromIRBox(builder, loc, mutableBox); 2741 // Handle case where result was passed as argument 2742 2743 // Copy-out temps that were created for non contiguous variable arguments if 2744 // needed. 2745 for (const auto ©OutPair : copyOutPairs) 2746 genCopyOut(copyOutPair); 2747 2748 return result; 2749 } 2750 2751 template <typename A> 2752 ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) { 2753 ExtValue result = genFunctionRef(funcRef); 2754 if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType())) 2755 return genLoad(result); 2756 return result; 2757 } 2758 2759 ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) { 2760 llvm::Optional<mlir::Type> resTy; 2761 if (procRef.hasAlternateReturns()) 2762 resTy = builder.getIndexType(); 2763 return genProcedureRef(procRef, resTy); 2764 } 2765 2766 /// Helper to lower intrinsic arguments for inquiry intrinsic. 2767 ExtValue 2768 lowerIntrinsicArgumentAsInquired(const Fortran::lower::SomeExpr &expr) { 2769 if (Fortran::evaluate::IsAllocatableOrPointerObject( 2770 expr, converter.getFoldingContext())) 2771 return genMutableBoxValue(expr); 2772 return gen(expr); 2773 } 2774 2775 /// Helper to lower intrinsic arguments to a fir::BoxValue. 2776 /// It preserves all the non default lower bounds/non deferred length 2777 /// parameter information. 2778 ExtValue lowerIntrinsicArgumentAsBox(const Fortran::lower::SomeExpr &expr) { 2779 mlir::Location loc = getLoc(); 2780 ExtValue exv = genBoxArg(expr); 2781 mlir::Value box = builder.createBox(loc, exv); 2782 return fir::BoxValue( 2783 box, fir::factory::getNonDefaultLowerBounds(builder, loc, exv), 2784 fir::factory::getNonDeferredLengthParams(exv)); 2785 } 2786 2787 /// Generate a call to an intrinsic function. 2788 ExtValue 2789 genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef, 2790 const Fortran::evaluate::SpecificIntrinsic &intrinsic, 2791 llvm::Optional<mlir::Type> resultType) { 2792 llvm::SmallVector<ExtValue> operands; 2793 2794 llvm::StringRef name = intrinsic.name; 2795 mlir::Location loc = getLoc(); 2796 if (Fortran::lower::intrinsicRequiresCustomOptionalHandling( 2797 procRef, intrinsic, converter)) { 2798 using ExvAndPresence = std::pair<ExtValue, llvm::Optional<mlir::Value>>; 2799 llvm::SmallVector<ExvAndPresence, 4> operands; 2800 auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) { 2801 ExtValue optionalArg = lowerIntrinsicArgumentAsInquired(expr); 2802 mlir::Value isPresent = 2803 genActualIsPresentTest(builder, loc, optionalArg); 2804 operands.emplace_back(optionalArg, isPresent); 2805 }; 2806 auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) { 2807 operands.emplace_back(genval(expr), llvm::None); 2808 }; 2809 Fortran::lower::prepareCustomIntrinsicArgument( 2810 procRef, intrinsic, resultType, prepareOptionalArg, prepareOtherArg, 2811 converter); 2812 2813 auto getArgument = [&](std::size_t i) -> ExtValue { 2814 if (fir::conformsWithPassByRef( 2815 fir::getBase(operands[i].first).getType())) 2816 return genLoad(operands[i].first); 2817 return operands[i].first; 2818 }; 2819 auto isPresent = [&](std::size_t i) -> llvm::Optional<mlir::Value> { 2820 return operands[i].second; 2821 }; 2822 return Fortran::lower::lowerCustomIntrinsic( 2823 builder, loc, name, resultType, isPresent, getArgument, 2824 operands.size(), stmtCtx); 2825 } 2826 2827 const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering = 2828 Fortran::lower::getIntrinsicArgumentLowering(name); 2829 for (const auto &[arg, dummy] : 2830 llvm::zip(procRef.arguments(), 2831 intrinsic.characteristics.value().dummyArguments)) { 2832 auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg); 2833 if (!expr) { 2834 // Absent optional. 2835 operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument()); 2836 continue; 2837 } 2838 if (!argLowering) { 2839 // No argument lowering instruction, lower by value. 2840 operands.emplace_back(genval(*expr)); 2841 continue; 2842 } 2843 // Ad-hoc argument lowering handling. 2844 Fortran::lower::ArgLoweringRule argRules = 2845 Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering, 2846 dummy.name); 2847 if (argRules.handleDynamicOptional && 2848 Fortran::evaluate::MayBePassedAsAbsentOptional( 2849 *expr, converter.getFoldingContext())) { 2850 ExtValue optional = lowerIntrinsicArgumentAsInquired(*expr); 2851 mlir::Value isPresent = genActualIsPresentTest(builder, loc, optional); 2852 switch (argRules.lowerAs) { 2853 case Fortran::lower::LowerIntrinsicArgAs::Value: 2854 operands.emplace_back( 2855 genOptionalValue(builder, loc, optional, isPresent)); 2856 continue; 2857 case Fortran::lower::LowerIntrinsicArgAs::Addr: 2858 operands.emplace_back( 2859 genOptionalAddr(builder, loc, optional, isPresent)); 2860 continue; 2861 case Fortran::lower::LowerIntrinsicArgAs::Box: 2862 operands.emplace_back( 2863 genOptionalBox(builder, loc, optional, isPresent)); 2864 continue; 2865 case Fortran::lower::LowerIntrinsicArgAs::Inquired: 2866 operands.emplace_back(optional); 2867 continue; 2868 } 2869 llvm_unreachable("bad switch"); 2870 } 2871 switch (argRules.lowerAs) { 2872 case Fortran::lower::LowerIntrinsicArgAs::Value: 2873 operands.emplace_back(genval(*expr)); 2874 continue; 2875 case Fortran::lower::LowerIntrinsicArgAs::Addr: 2876 operands.emplace_back(gen(*expr)); 2877 continue; 2878 case Fortran::lower::LowerIntrinsicArgAs::Box: 2879 operands.emplace_back(lowerIntrinsicArgumentAsBox(*expr)); 2880 continue; 2881 case Fortran::lower::LowerIntrinsicArgAs::Inquired: 2882 operands.emplace_back(lowerIntrinsicArgumentAsInquired(*expr)); 2883 continue; 2884 } 2885 llvm_unreachable("bad switch"); 2886 } 2887 // Let the intrinsic library lower the intrinsic procedure call 2888 return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType, 2889 operands, stmtCtx); 2890 } 2891 2892 template <typename A> 2893 bool isScalar(const A &x) { 2894 return x.Rank() == 0; 2895 } 2896 2897 /// Helper to detect Transformational function reference. 2898 template <typename T> 2899 bool isTransformationalRef(const T &) { 2900 return false; 2901 } 2902 template <typename T> 2903 bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) { 2904 return !funcRef.IsElemental() && funcRef.Rank(); 2905 } 2906 template <typename T> 2907 bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) { 2908 return std::visit([&](const auto &e) { return isTransformationalRef(e); }, 2909 expr.u); 2910 } 2911 2912 template <typename A> 2913 ExtValue asArray(const A &x) { 2914 return Fortran::lower::createSomeArrayTempValue(converter, toEvExpr(x), 2915 symMap, stmtCtx); 2916 } 2917 2918 /// Lower an array value as an argument. This argument can be passed as a box 2919 /// value, so it may be possible to avoid making a temporary. 2920 template <typename A> 2921 ExtValue asArrayArg(const Fortran::evaluate::Expr<A> &x) { 2922 return std::visit([&](const auto &e) { return asArrayArg(e, x); }, x.u); 2923 } 2924 template <typename A, typename B> 2925 ExtValue asArrayArg(const Fortran::evaluate::Expr<A> &x, const B &y) { 2926 return std::visit([&](const auto &e) { return asArrayArg(e, y); }, x.u); 2927 } 2928 template <typename A, typename B> 2929 ExtValue asArrayArg(const Fortran::evaluate::Designator<A> &, const B &x) { 2930 // Designator is being passed as an argument to a procedure. Lower the 2931 // expression to a boxed value. 2932 auto someExpr = toEvExpr(x); 2933 return Fortran::lower::createBoxValue(getLoc(), converter, someExpr, symMap, 2934 stmtCtx); 2935 } 2936 template <typename A, typename B> 2937 ExtValue asArrayArg(const A &, const B &x) { 2938 // If the expression to pass as an argument is not a designator, then create 2939 // an array temp. 2940 return asArray(x); 2941 } 2942 2943 template <typename A> 2944 ExtValue gen(const Fortran::evaluate::Expr<A> &x) { 2945 // Whole array symbols or components, and results of transformational 2946 // functions already have a storage and the scalar expression lowering path 2947 // is used to not create a new temporary storage. 2948 if (isScalar(x) || 2949 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) || 2950 isTransformationalRef(x)) 2951 return std::visit([&](const auto &e) { return genref(e); }, x.u); 2952 if (useBoxArg) 2953 return asArrayArg(x); 2954 return asArray(x); 2955 } 2956 template <typename A> 2957 ExtValue genval(const Fortran::evaluate::Expr<A> &x) { 2958 if (isScalar(x) || Fortran::evaluate::UnwrapWholeSymbolDataRef(x) || 2959 inInitializer) 2960 return std::visit([&](const auto &e) { return genval(e); }, x.u); 2961 return asArray(x); 2962 } 2963 2964 template <int KIND> 2965 ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type< 2966 Fortran::common::TypeCategory::Logical, KIND>> &exp) { 2967 return std::visit([&](const auto &e) { return genval(e); }, exp.u); 2968 } 2969 2970 using RefSet = 2971 std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring, 2972 Fortran::evaluate::DataRef, Fortran::evaluate::Component, 2973 Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef, 2974 Fortran::semantics::SymbolRef>; 2975 template <typename A> 2976 static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>; 2977 2978 template <typename A, typename = std::enable_if_t<inRefSet<A>>> 2979 ExtValue genref(const A &a) { 2980 return gen(a); 2981 } 2982 template <typename A> 2983 ExtValue genref(const A &a) { 2984 if (inInitializer) { 2985 // Initialization expressions can never allocate memory. 2986 return genval(a); 2987 } 2988 mlir::Type storageType = converter.genType(toEvExpr(a)); 2989 return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType); 2990 } 2991 2992 template <typename A, template <typename> typename T, 2993 typename B = std::decay_t<T<A>>, 2994 std::enable_if_t< 2995 std::is_same_v<B, Fortran::evaluate::Expr<A>> || 2996 std::is_same_v<B, Fortran::evaluate::Designator<A>> || 2997 std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>, 2998 bool> = true> 2999 ExtValue genref(const T<A> &x) { 3000 return gen(x); 3001 } 3002 3003 private: 3004 mlir::Location location; 3005 Fortran::lower::AbstractConverter &converter; 3006 fir::FirOpBuilder &builder; 3007 Fortran::lower::StatementContext &stmtCtx; 3008 Fortran::lower::SymMap &symMap; 3009 InitializerData *inInitializer = nullptr; 3010 bool useBoxArg = false; // expression lowered as argument 3011 }; 3012 } // namespace 3013 3014 // Helper for changing the semantics in a given context. Preserves the current 3015 // semantics which is resumed when the "push" goes out of scope. 3016 #define PushSemantics(PushVal) \ 3017 [[maybe_unused]] auto pushSemanticsLocalVariable##__LINE__ = \ 3018 Fortran::common::ScopedSet(semant, PushVal); 3019 3020 static bool isAdjustedArrayElementType(mlir::Type t) { 3021 return fir::isa_char(t) || fir::isa_derived(t) || t.isa<fir::SequenceType>(); 3022 } 3023 static bool elementTypeWasAdjusted(mlir::Type t) { 3024 if (auto ty = t.dyn_cast<fir::ReferenceType>()) 3025 return isAdjustedArrayElementType(ty.getEleTy()); 3026 return false; 3027 } 3028 3029 /// Build an ExtendedValue from a fir.array<?x...?xT> without actually setting 3030 /// the actual extents and lengths. This is only to allow their propagation as 3031 /// ExtendedValue without triggering verifier failures when propagating 3032 /// character/arrays as unboxed values. Only the base of the resulting 3033 /// ExtendedValue should be used, it is undefined to use the length or extents 3034 /// of the extended value returned, 3035 inline static fir::ExtendedValue 3036 convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder, 3037 mlir::Value val, mlir::Value len) { 3038 mlir::Type ty = fir::unwrapRefType(val.getType()); 3039 mlir::IndexType idxTy = builder.getIndexType(); 3040 auto seqTy = ty.cast<fir::SequenceType>(); 3041 auto undef = builder.create<fir::UndefOp>(loc, idxTy); 3042 llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), undef); 3043 if (fir::isa_char(seqTy.getEleTy())) 3044 return fir::CharArrayBoxValue(val, len ? len : undef, extents); 3045 return fir::ArrayBoxValue(val, extents); 3046 } 3047 3048 /// Helper to generate calls to scalar user defined assignment procedures. 3049 static void genScalarUserDefinedAssignmentCall(fir::FirOpBuilder &builder, 3050 mlir::Location loc, 3051 mlir::FuncOp func, 3052 const fir::ExtendedValue &lhs, 3053 const fir::ExtendedValue &rhs) { 3054 auto prepareUserDefinedArg = 3055 [](fir::FirOpBuilder &builder, mlir::Location loc, 3056 const fir::ExtendedValue &value, mlir::Type argType) -> mlir::Value { 3057 if (argType.isa<fir::BoxCharType>()) { 3058 const fir::CharBoxValue *charBox = value.getCharBox(); 3059 assert(charBox && "argument type mismatch in elemental user assignment"); 3060 return fir::factory::CharacterExprHelper{builder, loc}.createEmbox( 3061 *charBox); 3062 } 3063 if (argType.isa<fir::BoxType>()) { 3064 mlir::Value box = builder.createBox(loc, value); 3065 return builder.createConvert(loc, argType, box); 3066 } 3067 // Simple pass by address. 3068 mlir::Type argBaseType = fir::unwrapRefType(argType); 3069 assert(!fir::hasDynamicSize(argBaseType)); 3070 mlir::Value from = fir::getBase(value); 3071 if (argBaseType != fir::unwrapRefType(from.getType())) { 3072 // With logicals, it is possible that from is i1 here. 3073 if (fir::isa_ref_type(from.getType())) 3074 from = builder.create<fir::LoadOp>(loc, from); 3075 from = builder.createConvert(loc, argBaseType, from); 3076 } 3077 if (!fir::isa_ref_type(from.getType())) { 3078 mlir::Value temp = builder.createTemporary(loc, argBaseType); 3079 builder.create<fir::StoreOp>(loc, from, temp); 3080 from = temp; 3081 } 3082 return builder.createConvert(loc, argType, from); 3083 }; 3084 assert(func.getNumArguments() == 2); 3085 mlir::Type lhsType = func.getFunctionType().getInput(0); 3086 mlir::Type rhsType = func.getFunctionType().getInput(1); 3087 mlir::Value lhsArg = prepareUserDefinedArg(builder, loc, lhs, lhsType); 3088 mlir::Value rhsArg = prepareUserDefinedArg(builder, loc, rhs, rhsType); 3089 builder.create<fir::CallOp>(loc, func, mlir::ValueRange{lhsArg, rhsArg}); 3090 } 3091 3092 /// Convert the result of a fir.array_modify to an ExtendedValue given the 3093 /// related fir.array_load. 3094 static fir::ExtendedValue arrayModifyToExv(fir::FirOpBuilder &builder, 3095 mlir::Location loc, 3096 fir::ArrayLoadOp load, 3097 mlir::Value elementAddr) { 3098 mlir::Type eleTy = fir::unwrapPassByRefType(elementAddr.getType()); 3099 if (fir::isa_char(eleTy)) { 3100 auto len = fir::factory::CharacterExprHelper{builder, loc}.getLength( 3101 load.getMemref()); 3102 if (!len) { 3103 assert(load.getTypeparams().size() == 1 && 3104 "length must be in array_load"); 3105 len = load.getTypeparams()[0]; 3106 } 3107 return fir::CharBoxValue{elementAddr, len}; 3108 } 3109 return elementAddr; 3110 } 3111 3112 //===----------------------------------------------------------------------===// 3113 // 3114 // Lowering of scalar expressions in an explicit iteration space context. 3115 // 3116 //===----------------------------------------------------------------------===// 3117 3118 // Shared code for creating a copy of a derived type element. This function is 3119 // called from a continuation. 3120 inline static fir::ArrayAmendOp 3121 createDerivedArrayAmend(mlir::Location loc, fir::ArrayLoadOp destLoad, 3122 fir::FirOpBuilder &builder, fir::ArrayAccessOp destAcc, 3123 const fir::ExtendedValue &elementExv, mlir::Type eleTy, 3124 mlir::Value innerArg) { 3125 if (destLoad.getTypeparams().empty()) { 3126 fir::factory::genRecordAssignment(builder, loc, destAcc, elementExv); 3127 } else { 3128 auto boxTy = fir::BoxType::get(eleTy); 3129 auto toBox = builder.create<fir::EmboxOp>(loc, boxTy, destAcc.getResult(), 3130 mlir::Value{}, mlir::Value{}, 3131 destLoad.getTypeparams()); 3132 auto fromBox = builder.create<fir::EmboxOp>( 3133 loc, boxTy, fir::getBase(elementExv), mlir::Value{}, mlir::Value{}, 3134 destLoad.getTypeparams()); 3135 fir::factory::genRecordAssignment(builder, loc, fir::BoxValue(toBox), 3136 fir::BoxValue(fromBox)); 3137 } 3138 return builder.create<fir::ArrayAmendOp>(loc, innerArg.getType(), innerArg, 3139 destAcc); 3140 } 3141 3142 inline static fir::ArrayAmendOp 3143 createCharArrayAmend(mlir::Location loc, fir::FirOpBuilder &builder, 3144 fir::ArrayAccessOp dstOp, mlir::Value &dstLen, 3145 const fir::ExtendedValue &srcExv, mlir::Value innerArg, 3146 llvm::ArrayRef<mlir::Value> bounds) { 3147 fir::CharBoxValue dstChar(dstOp, dstLen); 3148 fir::factory::CharacterExprHelper helper{builder, loc}; 3149 if (!bounds.empty()) { 3150 dstChar = helper.createSubstring(dstChar, bounds); 3151 fir::factory::genCharacterCopy(fir::getBase(srcExv), fir::getLen(srcExv), 3152 dstChar.getAddr(), dstChar.getLen(), builder, 3153 loc); 3154 // Update the LEN to the substring's LEN. 3155 dstLen = dstChar.getLen(); 3156 } 3157 // For a CHARACTER, we generate the element assignment loops inline. 3158 helper.createAssign(fir::ExtendedValue{dstChar}, srcExv); 3159 // Mark this array element as amended. 3160 mlir::Type ty = innerArg.getType(); 3161 auto amend = builder.create<fir::ArrayAmendOp>(loc, ty, innerArg, dstOp); 3162 return amend; 3163 } 3164 3165 //===----------------------------------------------------------------------===// 3166 // 3167 // Lowering of array expressions. 3168 // 3169 //===----------------------------------------------------------------------===// 3170 3171 namespace { 3172 class ArrayExprLowering { 3173 using ExtValue = fir::ExtendedValue; 3174 3175 /// Structure to keep track of lowered array operands in the 3176 /// array expression. Useful to later deduce the shape of the 3177 /// array expression. 3178 struct ArrayOperand { 3179 /// Array base (can be a fir.box). 3180 mlir::Value memref; 3181 /// ShapeOp, ShapeShiftOp or ShiftOp 3182 mlir::Value shape; 3183 /// SliceOp 3184 mlir::Value slice; 3185 /// Can this operand be absent ? 3186 bool mayBeAbsent = false; 3187 }; 3188 3189 using ImplicitSubscripts = Fortran::lower::details::ImplicitSubscripts; 3190 using PathComponent = Fortran::lower::PathComponent; 3191 3192 /// Active iteration space. 3193 using IterationSpace = Fortran::lower::IterationSpace; 3194 using IterSpace = const Fortran::lower::IterationSpace &; 3195 3196 /// Current continuation. Function that will generate IR for a single 3197 /// iteration of the pending iterative loop structure. 3198 using CC = Fortran::lower::GenerateElementalArrayFunc; 3199 3200 /// Projection continuation. Function that will project one iteration space 3201 /// into another. 3202 using PC = std::function<IterationSpace(IterSpace)>; 3203 using ArrayBaseTy = 3204 std::variant<std::monostate, const Fortran::evaluate::ArrayRef *, 3205 const Fortran::evaluate::DataRef *>; 3206 using ComponentPath = Fortran::lower::ComponentPath; 3207 3208 public: 3209 //===--------------------------------------------------------------------===// 3210 // Regular array assignment 3211 //===--------------------------------------------------------------------===// 3212 3213 /// Entry point for array assignments. Both the left-hand and right-hand sides 3214 /// can either be ExtendedValue or evaluate::Expr. 3215 template <typename TL, typename TR> 3216 static void lowerArrayAssignment(Fortran::lower::AbstractConverter &converter, 3217 Fortran::lower::SymMap &symMap, 3218 Fortran::lower::StatementContext &stmtCtx, 3219 const TL &lhs, const TR &rhs) { 3220 ArrayExprLowering ael{converter, stmtCtx, symMap, 3221 ConstituentSemantics::CopyInCopyOut}; 3222 ael.lowerArrayAssignment(lhs, rhs); 3223 } 3224 3225 template <typename TL, typename TR> 3226 void lowerArrayAssignment(const TL &lhs, const TR &rhs) { 3227 mlir::Location loc = getLoc(); 3228 /// Here the target subspace is not necessarily contiguous. The ArrayUpdate 3229 /// continuation is implicitly returned in `ccStoreToDest` and the ArrayLoad 3230 /// in `destination`. 3231 PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut); 3232 ccStoreToDest = genarr(lhs); 3233 determineShapeOfDest(lhs); 3234 semant = ConstituentSemantics::RefTransparent; 3235 ExtValue exv = lowerArrayExpression(rhs); 3236 if (explicitSpaceIsActive()) { 3237 explicitSpace->finalizeContext(); 3238 builder.create<fir::ResultOp>(loc, fir::getBase(exv)); 3239 } else { 3240 builder.create<fir::ArrayMergeStoreOp>( 3241 loc, destination, fir::getBase(exv), destination.getMemref(), 3242 destination.getSlice(), destination.getTypeparams()); 3243 } 3244 } 3245 3246 //===--------------------------------------------------------------------===// 3247 // WHERE array assignment, FORALL assignment, and FORALL+WHERE array 3248 // assignment 3249 //===--------------------------------------------------------------------===// 3250 3251 /// Entry point for array assignment when the iteration space is explicitly 3252 /// defined (Fortran's FORALL) with or without masks, and/or the implied 3253 /// iteration space involves masks (Fortran's WHERE). Both contexts (explicit 3254 /// space and implicit space with masks) may be present. 3255 static void lowerAnyMaskedArrayAssignment( 3256 Fortran::lower::AbstractConverter &converter, 3257 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 3258 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 3259 Fortran::lower::ExplicitIterSpace &explicitSpace, 3260 Fortran::lower::ImplicitIterSpace &implicitSpace) { 3261 if (explicitSpace.isActive() && lhs.Rank() == 0) { 3262 // Scalar assignment expression in a FORALL context. 3263 ArrayExprLowering ael(converter, stmtCtx, symMap, 3264 ConstituentSemantics::RefTransparent, 3265 &explicitSpace, &implicitSpace); 3266 ael.lowerScalarAssignment(lhs, rhs); 3267 return; 3268 } 3269 // Array assignment expression in a FORALL and/or WHERE context. 3270 ArrayExprLowering ael(converter, stmtCtx, symMap, 3271 ConstituentSemantics::CopyInCopyOut, &explicitSpace, 3272 &implicitSpace); 3273 ael.lowerArrayAssignment(lhs, rhs); 3274 } 3275 3276 //===--------------------------------------------------------------------===// 3277 // Array assignment to allocatable array 3278 //===--------------------------------------------------------------------===// 3279 3280 /// Entry point for assignment to allocatable array. 3281 static void lowerAllocatableArrayAssignment( 3282 Fortran::lower::AbstractConverter &converter, 3283 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 3284 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 3285 Fortran::lower::ExplicitIterSpace &explicitSpace, 3286 Fortran::lower::ImplicitIterSpace &implicitSpace) { 3287 ArrayExprLowering ael(converter, stmtCtx, symMap, 3288 ConstituentSemantics::CopyInCopyOut, &explicitSpace, 3289 &implicitSpace); 3290 ael.lowerAllocatableArrayAssignment(lhs, rhs); 3291 } 3292 3293 /// Assignment to allocatable array. 3294 /// 3295 /// The semantics are reverse that of a "regular" array assignment. The rhs 3296 /// defines the iteration space of the computation and the lhs is 3297 /// resized/reallocated to fit if necessary. 3298 void lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr &lhs, 3299 const Fortran::lower::SomeExpr &rhs) { 3300 // With assignment to allocatable, we want to lower the rhs first and use 3301 // its shape to determine if we need to reallocate, etc. 3302 mlir::Location loc = getLoc(); 3303 // FIXME: If the lhs is in an explicit iteration space, the assignment may 3304 // be to an array of allocatable arrays rather than a single allocatable 3305 // array. 3306 fir::MutableBoxValue mutableBox = 3307 createMutableBox(loc, converter, lhs, symMap); 3308 mlir::Type resultTy = converter.genType(rhs); 3309 if (rhs.Rank() > 0) 3310 determineShapeOfDest(rhs); 3311 auto rhsCC = [&]() { 3312 PushSemantics(ConstituentSemantics::RefTransparent); 3313 return genarr(rhs); 3314 }(); 3315 3316 llvm::SmallVector<mlir::Value> lengthParams; 3317 // Currently no safe way to gather length from rhs (at least for 3318 // character, it cannot be taken from array_loads since it may be 3319 // changed by concatenations). 3320 if ((mutableBox.isCharacter() && !mutableBox.hasNonDeferredLenParams()) || 3321 mutableBox.isDerivedWithLengthParameters()) 3322 TODO(loc, "gather rhs length parameters in assignment to allocatable"); 3323 3324 // The allocatable must take lower bounds from the expr if it is 3325 // reallocated and the right hand side is not a scalar. 3326 const bool takeLboundsIfRealloc = rhs.Rank() > 0; 3327 llvm::SmallVector<mlir::Value> lbounds; 3328 // When the reallocated LHS takes its lower bounds from the RHS, 3329 // they will be non default only if the RHS is a whole array 3330 // variable. Otherwise, lbounds is left empty and default lower bounds 3331 // will be used. 3332 if (takeLboundsIfRealloc && 3333 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs)) { 3334 assert(arrayOperands.size() == 1 && 3335 "lbounds can only come from one array"); 3336 std::vector<mlir::Value> lbs = 3337 fir::factory::getOrigins(arrayOperands[0].shape); 3338 lbounds.append(lbs.begin(), lbs.end()); 3339 } 3340 fir::factory::MutableBoxReallocation realloc = 3341 fir::factory::genReallocIfNeeded(builder, loc, mutableBox, destShape, 3342 lengthParams); 3343 // Create ArrayLoad for the mutable box and save it into `destination`. 3344 PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut); 3345 ccStoreToDest = genarr(realloc.newValue); 3346 // If the rhs is scalar, get shape from the allocatable ArrayLoad. 3347 if (destShape.empty()) 3348 destShape = getShape(destination); 3349 // Finish lowering the loop nest. 3350 assert(destination && "destination must have been set"); 3351 ExtValue exv = lowerArrayExpression(rhsCC, resultTy); 3352 if (explicitSpaceIsActive()) { 3353 explicitSpace->finalizeContext(); 3354 builder.create<fir::ResultOp>(loc, fir::getBase(exv)); 3355 } else { 3356 builder.create<fir::ArrayMergeStoreOp>( 3357 loc, destination, fir::getBase(exv), destination.getMemref(), 3358 destination.getSlice(), destination.getTypeparams()); 3359 } 3360 fir::factory::finalizeRealloc(builder, loc, mutableBox, lbounds, 3361 takeLboundsIfRealloc, realloc); 3362 } 3363 3364 /// Entry point for when an array expression appears in a context where the 3365 /// result must be boxed. (BoxValue semantics.) 3366 static ExtValue 3367 lowerBoxedArrayExpression(Fortran::lower::AbstractConverter &converter, 3368 Fortran::lower::SymMap &symMap, 3369 Fortran::lower::StatementContext &stmtCtx, 3370 const Fortran::lower::SomeExpr &expr) { 3371 ArrayExprLowering ael{converter, stmtCtx, symMap, 3372 ConstituentSemantics::BoxValue}; 3373 return ael.lowerBoxedArrayExpr(expr); 3374 } 3375 3376 ExtValue lowerBoxedArrayExpr(const Fortran::lower::SomeExpr &exp) { 3377 return std::visit( 3378 [&](const auto &e) { 3379 auto f = genarr(e); 3380 ExtValue exv = f(IterationSpace{}); 3381 if (fir::getBase(exv).getType().template isa<fir::BoxType>()) 3382 return exv; 3383 fir::emitFatalError(getLoc(), "array must be emboxed"); 3384 }, 3385 exp.u); 3386 } 3387 3388 /// Entry point into lowering an expression with rank. This entry point is for 3389 /// lowering a rhs expression, for example. (RefTransparent semantics.) 3390 static ExtValue 3391 lowerNewArrayExpression(Fortran::lower::AbstractConverter &converter, 3392 Fortran::lower::SymMap &symMap, 3393 Fortran::lower::StatementContext &stmtCtx, 3394 const Fortran::lower::SomeExpr &expr) { 3395 ArrayExprLowering ael{converter, stmtCtx, symMap}; 3396 ael.determineShapeOfDest(expr); 3397 ExtValue loopRes = ael.lowerArrayExpression(expr); 3398 fir::ArrayLoadOp dest = ael.destination; 3399 mlir::Value tempRes = dest.getMemref(); 3400 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 3401 mlir::Location loc = converter.getCurrentLocation(); 3402 builder.create<fir::ArrayMergeStoreOp>(loc, dest, fir::getBase(loopRes), 3403 tempRes, dest.getSlice(), 3404 dest.getTypeparams()); 3405 3406 auto arrTy = 3407 fir::dyn_cast_ptrEleTy(tempRes.getType()).cast<fir::SequenceType>(); 3408 if (auto charTy = 3409 arrTy.getEleTy().template dyn_cast<fir::CharacterType>()) { 3410 if (fir::characterWithDynamicLen(charTy)) 3411 TODO(loc, "CHARACTER does not have constant LEN"); 3412 mlir::Value len = builder.createIntegerConstant( 3413 loc, builder.getCharacterLengthType(), charTy.getLen()); 3414 return fir::CharArrayBoxValue(tempRes, len, dest.getExtents()); 3415 } 3416 return fir::ArrayBoxValue(tempRes, dest.getExtents()); 3417 } 3418 3419 static void lowerLazyArrayExpression( 3420 Fortran::lower::AbstractConverter &converter, 3421 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 3422 const Fortran::lower::SomeExpr &expr, mlir::Value raggedHeader) { 3423 ArrayExprLowering ael(converter, stmtCtx, symMap); 3424 ael.lowerLazyArrayExpression(expr, raggedHeader); 3425 } 3426 3427 /// Lower the expression \p expr into a buffer that is created on demand. The 3428 /// variable containing the pointer to the buffer is \p var and the variable 3429 /// containing the shape of the buffer is \p shapeBuffer. 3430 void lowerLazyArrayExpression(const Fortran::lower::SomeExpr &expr, 3431 mlir::Value header) { 3432 mlir::Location loc = getLoc(); 3433 mlir::TupleType hdrTy = fir::factory::getRaggedArrayHeaderType(builder); 3434 mlir::IntegerType i32Ty = builder.getIntegerType(32); 3435 3436 // Once the loop extents have been computed, which may require being inside 3437 // some explicit loops, lazily allocate the expression on the heap. The 3438 // following continuation creates the buffer as needed. 3439 ccPrelude = [=](llvm::ArrayRef<mlir::Value> shape) { 3440 mlir::IntegerType i64Ty = builder.getIntegerType(64); 3441 mlir::Value byteSize = builder.createIntegerConstant(loc, i64Ty, 1); 3442 fir::runtime::genRaggedArrayAllocate( 3443 loc, builder, header, /*asHeaders=*/false, byteSize, shape); 3444 }; 3445 3446 // Create a dummy array_load before the loop. We're storing to a lazy 3447 // temporary, so there will be no conflict and no copy-in. TODO: skip this 3448 // as there isn't any necessity for it. 3449 ccLoadDest = [=](llvm::ArrayRef<mlir::Value> shape) -> fir::ArrayLoadOp { 3450 mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1); 3451 auto var = builder.create<fir::CoordinateOp>( 3452 loc, builder.getRefType(hdrTy.getType(1)), header, one); 3453 auto load = builder.create<fir::LoadOp>(loc, var); 3454 mlir::Type eleTy = 3455 fir::unwrapSequenceType(fir::unwrapRefType(load.getType())); 3456 auto seqTy = fir::SequenceType::get(eleTy, shape.size()); 3457 mlir::Value castTo = 3458 builder.createConvert(loc, fir::HeapType::get(seqTy), load); 3459 mlir::Value shapeOp = builder.genShape(loc, shape); 3460 return builder.create<fir::ArrayLoadOp>( 3461 loc, seqTy, castTo, shapeOp, /*slice=*/mlir::Value{}, llvm::None); 3462 }; 3463 // Custom lowering of the element store to deal with the extra indirection 3464 // to the lazy allocated buffer. 3465 ccStoreToDest = [=](IterSpace iters) { 3466 mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1); 3467 auto var = builder.create<fir::CoordinateOp>( 3468 loc, builder.getRefType(hdrTy.getType(1)), header, one); 3469 auto load = builder.create<fir::LoadOp>(loc, var); 3470 mlir::Type eleTy = 3471 fir::unwrapSequenceType(fir::unwrapRefType(load.getType())); 3472 auto seqTy = fir::SequenceType::get(eleTy, iters.iterVec().size()); 3473 auto toTy = fir::HeapType::get(seqTy); 3474 mlir::Value castTo = builder.createConvert(loc, toTy, load); 3475 mlir::Value shape = builder.genShape(loc, genIterationShape()); 3476 llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices( 3477 loc, builder, castTo.getType(), shape, iters.iterVec()); 3478 auto eleAddr = builder.create<fir::ArrayCoorOp>( 3479 loc, builder.getRefType(eleTy), castTo, shape, 3480 /*slice=*/mlir::Value{}, indices, destination.getTypeparams()); 3481 mlir::Value eleVal = 3482 builder.createConvert(loc, eleTy, iters.getElement()); 3483 builder.create<fir::StoreOp>(loc, eleVal, eleAddr); 3484 return iters.innerArgument(); 3485 }; 3486 3487 // Lower the array expression now. Clean-up any temps that may have 3488 // been generated when lowering `expr` right after the lowered value 3489 // was stored to the ragged array temporary. The local temps will not 3490 // be needed afterwards. 3491 stmtCtx.pushScope(); 3492 [[maybe_unused]] ExtValue loopRes = lowerArrayExpression(expr); 3493 stmtCtx.finalize(/*popScope=*/true); 3494 assert(fir::getBase(loopRes)); 3495 } 3496 3497 static void 3498 lowerElementalUserAssignment(Fortran::lower::AbstractConverter &converter, 3499 Fortran::lower::SymMap &symMap, 3500 Fortran::lower::StatementContext &stmtCtx, 3501 Fortran::lower::ExplicitIterSpace &explicitSpace, 3502 Fortran::lower::ImplicitIterSpace &implicitSpace, 3503 const Fortran::evaluate::ProcedureRef &procRef) { 3504 ArrayExprLowering ael(converter, stmtCtx, symMap, 3505 ConstituentSemantics::CustomCopyInCopyOut, 3506 &explicitSpace, &implicitSpace); 3507 assert(procRef.arguments().size() == 2); 3508 const auto *lhs = procRef.arguments()[0].value().UnwrapExpr(); 3509 const auto *rhs = procRef.arguments()[1].value().UnwrapExpr(); 3510 assert(lhs && rhs && 3511 "user defined assignment arguments must be expressions"); 3512 mlir::FuncOp func = 3513 Fortran::lower::CallerInterface(procRef, converter).getFuncOp(); 3514 ael.lowerElementalUserAssignment(func, *lhs, *rhs); 3515 } 3516 3517 void lowerElementalUserAssignment(mlir::FuncOp userAssignment, 3518 const Fortran::lower::SomeExpr &lhs, 3519 const Fortran::lower::SomeExpr &rhs) { 3520 mlir::Location loc = getLoc(); 3521 PushSemantics(ConstituentSemantics::CustomCopyInCopyOut); 3522 auto genArrayModify = genarr(lhs); 3523 ccStoreToDest = [=](IterSpace iters) -> ExtValue { 3524 auto modifiedArray = genArrayModify(iters); 3525 auto arrayModify = mlir::dyn_cast_or_null<fir::ArrayModifyOp>( 3526 fir::getBase(modifiedArray).getDefiningOp()); 3527 assert(arrayModify && "must be created by ArrayModifyOp"); 3528 fir::ExtendedValue lhs = 3529 arrayModifyToExv(builder, loc, destination, arrayModify.getResult(0)); 3530 genScalarUserDefinedAssignmentCall(builder, loc, userAssignment, lhs, 3531 iters.elementExv()); 3532 return modifiedArray; 3533 }; 3534 determineShapeOfDest(lhs); 3535 semant = ConstituentSemantics::RefTransparent; 3536 auto exv = lowerArrayExpression(rhs); 3537 if (explicitSpaceIsActive()) { 3538 explicitSpace->finalizeContext(); 3539 builder.create<fir::ResultOp>(loc, fir::getBase(exv)); 3540 } else { 3541 builder.create<fir::ArrayMergeStoreOp>( 3542 loc, destination, fir::getBase(exv), destination.getMemref(), 3543 destination.getSlice(), destination.getTypeparams()); 3544 } 3545 } 3546 3547 /// Lower an elemental subroutine call with at least one array argument. 3548 /// An elemental subroutine is an exception and does not have copy-in/copy-out 3549 /// semantics. See 15.8.3. 3550 /// Do NOT use this for user defined assignments. 3551 static void 3552 lowerElementalSubroutine(Fortran::lower::AbstractConverter &converter, 3553 Fortran::lower::SymMap &symMap, 3554 Fortran::lower::StatementContext &stmtCtx, 3555 const Fortran::lower::SomeExpr &call) { 3556 ArrayExprLowering ael(converter, stmtCtx, symMap, 3557 ConstituentSemantics::RefTransparent); 3558 ael.lowerElementalSubroutine(call); 3559 } 3560 3561 // TODO: See the comment in genarr(const Fortran::lower::Parentheses<T>&). 3562 // This is skipping generation of copy-in/copy-out code for analysis that is 3563 // required when arguments are in parentheses. 3564 void lowerElementalSubroutine(const Fortran::lower::SomeExpr &call) { 3565 auto f = genarr(call); 3566 llvm::SmallVector<mlir::Value> shape = genIterationShape(); 3567 auto [iterSpace, insPt] = genImplicitLoops(shape, /*innerArg=*/{}); 3568 f(iterSpace); 3569 finalizeElementCtx(); 3570 builder.restoreInsertionPoint(insPt); 3571 } 3572 3573 template <typename A, typename B> 3574 ExtValue lowerScalarAssignment(const A &lhs, const B &rhs) { 3575 // 1) Lower the rhs expression with array_fetch op(s). 3576 IterationSpace iters; 3577 iters.setElement(genarr(rhs)(iters)); 3578 fir::ExtendedValue elementalExv = iters.elementExv(); 3579 // 2) Lower the lhs expression to an array_update. 3580 semant = ConstituentSemantics::ProjectedCopyInCopyOut; 3581 auto lexv = genarr(lhs)(iters); 3582 // 3) Finalize the inner context. 3583 explicitSpace->finalizeContext(); 3584 // 4) Thread the array value updated forward. Note: the lhs might be 3585 // ill-formed (performing scalar assignment in an array context), 3586 // in which case there is no array to thread. 3587 auto createResult = [&](auto op) { 3588 mlir::Value oldInnerArg = op.getSequence(); 3589 std::size_t offset = explicitSpace->argPosition(oldInnerArg); 3590 explicitSpace->setInnerArg(offset, fir::getBase(lexv)); 3591 builder.create<fir::ResultOp>(getLoc(), fir::getBase(lexv)); 3592 }; 3593 if (auto updateOp = mlir::dyn_cast<fir::ArrayUpdateOp>( 3594 fir::getBase(lexv).getDefiningOp())) 3595 createResult(updateOp); 3596 else if (auto amend = mlir::dyn_cast<fir::ArrayAmendOp>( 3597 fir::getBase(lexv).getDefiningOp())) 3598 createResult(amend); 3599 else if (auto modifyOp = mlir::dyn_cast<fir::ArrayModifyOp>( 3600 fir::getBase(lexv).getDefiningOp())) 3601 createResult(modifyOp); 3602 return lexv; 3603 } 3604 3605 static ExtValue lowerScalarUserAssignment( 3606 Fortran::lower::AbstractConverter &converter, 3607 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 3608 Fortran::lower::ExplicitIterSpace &explicitIterSpace, 3609 mlir::FuncOp userAssignmentFunction, const Fortran::lower::SomeExpr &lhs, 3610 const Fortran::lower::SomeExpr &rhs) { 3611 Fortran::lower::ImplicitIterSpace implicit; 3612 ArrayExprLowering ael(converter, stmtCtx, symMap, 3613 ConstituentSemantics::RefTransparent, 3614 &explicitIterSpace, &implicit); 3615 return ael.lowerScalarUserAssignment(userAssignmentFunction, lhs, rhs); 3616 } 3617 3618 ExtValue lowerScalarUserAssignment(mlir::FuncOp userAssignment, 3619 const Fortran::lower::SomeExpr &lhs, 3620 const Fortran::lower::SomeExpr &rhs) { 3621 mlir::Location loc = getLoc(); 3622 if (rhs.Rank() > 0) 3623 TODO(loc, "user-defined elemental assigment from expression with rank"); 3624 // 1) Lower the rhs expression with array_fetch op(s). 3625 IterationSpace iters; 3626 iters.setElement(genarr(rhs)(iters)); 3627 fir::ExtendedValue elementalExv = iters.elementExv(); 3628 // 2) Lower the lhs expression to an array_modify. 3629 semant = ConstituentSemantics::CustomCopyInCopyOut; 3630 auto lexv = genarr(lhs)(iters); 3631 bool isIllFormedLHS = false; 3632 // 3) Insert the call 3633 if (auto modifyOp = mlir::dyn_cast<fir::ArrayModifyOp>( 3634 fir::getBase(lexv).getDefiningOp())) { 3635 mlir::Value oldInnerArg = modifyOp.getSequence(); 3636 std::size_t offset = explicitSpace->argPosition(oldInnerArg); 3637 explicitSpace->setInnerArg(offset, fir::getBase(lexv)); 3638 fir::ExtendedValue exv = arrayModifyToExv( 3639 builder, loc, explicitSpace->getLhsLoad(0).getValue(), 3640 modifyOp.getResult(0)); 3641 genScalarUserDefinedAssignmentCall(builder, loc, userAssignment, exv, 3642 elementalExv); 3643 } else { 3644 // LHS is ill formed, it is a scalar with no references to FORALL 3645 // subscripts, so there is actually no array assignment here. The user 3646 // code is probably bad, but still insert user assignment call since it 3647 // was not rejected by semantics (a warning was emitted). 3648 isIllFormedLHS = true; 3649 genScalarUserDefinedAssignmentCall(builder, getLoc(), userAssignment, 3650 lexv, elementalExv); 3651 } 3652 // 4) Finalize the inner context. 3653 explicitSpace->finalizeContext(); 3654 // 5). Thread the array value updated forward. 3655 if (!isIllFormedLHS) 3656 builder.create<fir::ResultOp>(getLoc(), fir::getBase(lexv)); 3657 return lexv; 3658 } 3659 3660 bool explicitSpaceIsActive() const { 3661 return explicitSpace && explicitSpace->isActive(); 3662 } 3663 3664 bool implicitSpaceHasMasks() const { 3665 return implicitSpace && !implicitSpace->empty(); 3666 } 3667 3668 CC genMaskAccess(mlir::Value tmp, mlir::Value shape) { 3669 mlir::Location loc = getLoc(); 3670 return [=, builder = &converter.getFirOpBuilder()](IterSpace iters) { 3671 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(tmp.getType()); 3672 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy(); 3673 mlir::Type eleRefTy = builder->getRefType(eleTy); 3674 mlir::IntegerType i1Ty = builder->getI1Type(); 3675 // Adjust indices for any shift of the origin of the array. 3676 llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices( 3677 loc, *builder, tmp.getType(), shape, iters.iterVec()); 3678 auto addr = builder->create<fir::ArrayCoorOp>( 3679 loc, eleRefTy, tmp, shape, /*slice=*/mlir::Value{}, indices, 3680 /*typeParams=*/llvm::None); 3681 auto load = builder->create<fir::LoadOp>(loc, addr); 3682 return builder->createConvert(loc, i1Ty, load); 3683 }; 3684 } 3685 3686 /// Construct the incremental instantiations of the ragged array structure. 3687 /// Rebind the lazy buffer variable, etc. as we go. 3688 template <bool withAllocation = false> 3689 mlir::Value prepareRaggedArrays(Fortran::lower::FrontEndExpr expr) { 3690 assert(explicitSpaceIsActive()); 3691 mlir::Location loc = getLoc(); 3692 mlir::TupleType raggedTy = fir::factory::getRaggedArrayHeaderType(builder); 3693 llvm::SmallVector<llvm::SmallVector<fir::DoLoopOp>> loopStack = 3694 explicitSpace->getLoopStack(); 3695 const std::size_t depth = loopStack.size(); 3696 mlir::IntegerType i64Ty = builder.getIntegerType(64); 3697 [[maybe_unused]] mlir::Value byteSize = 3698 builder.createIntegerConstant(loc, i64Ty, 1); 3699 mlir::Value header = implicitSpace->lookupMaskHeader(expr); 3700 for (std::remove_const_t<decltype(depth)> i = 0; i < depth; ++i) { 3701 auto insPt = builder.saveInsertionPoint(); 3702 if (i < depth - 1) 3703 builder.setInsertionPoint(loopStack[i + 1][0]); 3704 3705 // Compute and gather the extents. 3706 llvm::SmallVector<mlir::Value> extents; 3707 for (auto doLoop : loopStack[i]) 3708 extents.push_back(builder.genExtentFromTriplet( 3709 loc, doLoop.getLowerBound(), doLoop.getUpperBound(), 3710 doLoop.getStep(), i64Ty)); 3711 if constexpr (withAllocation) { 3712 fir::runtime::genRaggedArrayAllocate( 3713 loc, builder, header, /*asHeader=*/true, byteSize, extents); 3714 } 3715 3716 // Compute the dynamic position into the header. 3717 llvm::SmallVector<mlir::Value> offsets; 3718 for (auto doLoop : loopStack[i]) { 3719 auto m = builder.create<mlir::arith::SubIOp>( 3720 loc, doLoop.getInductionVar(), doLoop.getLowerBound()); 3721 auto n = builder.create<mlir::arith::DivSIOp>(loc, m, doLoop.getStep()); 3722 mlir::Value one = builder.createIntegerConstant(loc, n.getType(), 1); 3723 offsets.push_back(builder.create<mlir::arith::AddIOp>(loc, n, one)); 3724 } 3725 mlir::IntegerType i32Ty = builder.getIntegerType(32); 3726 mlir::Value uno = builder.createIntegerConstant(loc, i32Ty, 1); 3727 mlir::Type coorTy = builder.getRefType(raggedTy.getType(1)); 3728 auto hdOff = builder.create<fir::CoordinateOp>(loc, coorTy, header, uno); 3729 auto toTy = fir::SequenceType::get(raggedTy, offsets.size()); 3730 mlir::Type toRefTy = builder.getRefType(toTy); 3731 auto ldHdr = builder.create<fir::LoadOp>(loc, hdOff); 3732 mlir::Value hdArr = builder.createConvert(loc, toRefTy, ldHdr); 3733 auto shapeOp = builder.genShape(loc, extents); 3734 header = builder.create<fir::ArrayCoorOp>( 3735 loc, builder.getRefType(raggedTy), hdArr, shapeOp, 3736 /*slice=*/mlir::Value{}, offsets, 3737 /*typeparams=*/mlir::ValueRange{}); 3738 auto hdrVar = builder.create<fir::CoordinateOp>(loc, coorTy, header, uno); 3739 auto inVar = builder.create<fir::LoadOp>(loc, hdrVar); 3740 mlir::Value two = builder.createIntegerConstant(loc, i32Ty, 2); 3741 mlir::Type coorTy2 = builder.getRefType(raggedTy.getType(2)); 3742 auto hdrSh = builder.create<fir::CoordinateOp>(loc, coorTy2, header, two); 3743 auto shapePtr = builder.create<fir::LoadOp>(loc, hdrSh); 3744 // Replace the binding. 3745 implicitSpace->rebind(expr, genMaskAccess(inVar, shapePtr)); 3746 if (i < depth - 1) 3747 builder.restoreInsertionPoint(insPt); 3748 } 3749 return header; 3750 } 3751 3752 /// Lower mask expressions with implied iteration spaces from the variants of 3753 /// WHERE syntax. Since it is legal for mask expressions to have side-effects 3754 /// and modify values that will be used for the lhs, rhs, or both of 3755 /// subsequent assignments, the mask must be evaluated before the assignment 3756 /// is processed. 3757 /// Mask expressions are array expressions too. 3758 void genMasks() { 3759 // Lower the mask expressions, if any. 3760 if (implicitSpaceHasMasks()) { 3761 mlir::Location loc = getLoc(); 3762 // Mask expressions are array expressions too. 3763 for (const auto *e : implicitSpace->getExprs()) 3764 if (e && !implicitSpace->isLowered(e)) { 3765 if (mlir::Value var = implicitSpace->lookupMaskVariable(e)) { 3766 // Allocate the mask buffer lazily. 3767 assert(explicitSpaceIsActive()); 3768 mlir::Value header = 3769 prepareRaggedArrays</*withAllocations=*/true>(e); 3770 Fortran::lower::createLazyArrayTempValue(converter, *e, header, 3771 symMap, stmtCtx); 3772 // Close the explicit loops. 3773 builder.create<fir::ResultOp>(loc, explicitSpace->getInnerArgs()); 3774 builder.setInsertionPointAfter(explicitSpace->getOuterLoop()); 3775 // Open a new copy of the explicit loop nest. 3776 explicitSpace->genLoopNest(); 3777 continue; 3778 } 3779 fir::ExtendedValue tmp = Fortran::lower::createSomeArrayTempValue( 3780 converter, *e, symMap, stmtCtx); 3781 mlir::Value shape = builder.createShape(loc, tmp); 3782 implicitSpace->bind(e, genMaskAccess(fir::getBase(tmp), shape)); 3783 } 3784 3785 // Set buffer from the header. 3786 for (const auto *e : implicitSpace->getExprs()) { 3787 if (!e) 3788 continue; 3789 if (implicitSpace->lookupMaskVariable(e)) { 3790 // Index into the ragged buffer to retrieve cached results. 3791 const int rank = e->Rank(); 3792 assert(destShape.empty() || 3793 static_cast<std::size_t>(rank) == destShape.size()); 3794 mlir::Value header = prepareRaggedArrays(e); 3795 mlir::TupleType raggedTy = 3796 fir::factory::getRaggedArrayHeaderType(builder); 3797 mlir::IntegerType i32Ty = builder.getIntegerType(32); 3798 mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1); 3799 auto coor1 = builder.create<fir::CoordinateOp>( 3800 loc, builder.getRefType(raggedTy.getType(1)), header, one); 3801 auto db = builder.create<fir::LoadOp>(loc, coor1); 3802 mlir::Type eleTy = 3803 fir::unwrapSequenceType(fir::unwrapRefType(db.getType())); 3804 mlir::Type buffTy = 3805 builder.getRefType(fir::SequenceType::get(eleTy, rank)); 3806 // Address of ragged buffer data. 3807 mlir::Value buff = builder.createConvert(loc, buffTy, db); 3808 3809 mlir::Value two = builder.createIntegerConstant(loc, i32Ty, 2); 3810 auto coor2 = builder.create<fir::CoordinateOp>( 3811 loc, builder.getRefType(raggedTy.getType(2)), header, two); 3812 auto shBuff = builder.create<fir::LoadOp>(loc, coor2); 3813 mlir::IntegerType i64Ty = builder.getIntegerType(64); 3814 mlir::IndexType idxTy = builder.getIndexType(); 3815 llvm::SmallVector<mlir::Value> extents; 3816 for (std::remove_const_t<decltype(rank)> i = 0; i < rank; ++i) { 3817 mlir::Value off = builder.createIntegerConstant(loc, i32Ty, i); 3818 auto coor = builder.create<fir::CoordinateOp>( 3819 loc, builder.getRefType(i64Ty), shBuff, off); 3820 auto ldExt = builder.create<fir::LoadOp>(loc, coor); 3821 extents.push_back(builder.createConvert(loc, idxTy, ldExt)); 3822 } 3823 if (destShape.empty()) 3824 destShape = extents; 3825 // Construct shape of buffer. 3826 mlir::Value shapeOp = builder.genShape(loc, extents); 3827 3828 // Replace binding with the local result. 3829 implicitSpace->rebind(e, genMaskAccess(buff, shapeOp)); 3830 } 3831 } 3832 } 3833 } 3834 3835 // FIXME: should take multiple inner arguments. 3836 std::pair<IterationSpace, mlir::OpBuilder::InsertPoint> 3837 genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) { 3838 mlir::Location loc = getLoc(); 3839 mlir::IndexType idxTy = builder.getIndexType(); 3840 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); 3841 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); 3842 llvm::SmallVector<mlir::Value> loopUppers; 3843 3844 // Convert any implied shape to closed interval form. The fir.do_loop will 3845 // run from 0 to `extent - 1` inclusive. 3846 for (auto extent : shape) 3847 loopUppers.push_back( 3848 builder.create<mlir::arith::SubIOp>(loc, extent, one)); 3849 3850 // Iteration space is created with outermost columns, innermost rows 3851 llvm::SmallVector<fir::DoLoopOp> loops; 3852 3853 const std::size_t loopDepth = loopUppers.size(); 3854 llvm::SmallVector<mlir::Value> ivars; 3855 3856 for (auto i : llvm::enumerate(llvm::reverse(loopUppers))) { 3857 if (i.index() > 0) { 3858 assert(!loops.empty()); 3859 builder.setInsertionPointToStart(loops.back().getBody()); 3860 } 3861 fir::DoLoopOp loop; 3862 if (innerArg) { 3863 loop = builder.create<fir::DoLoopOp>( 3864 loc, zero, i.value(), one, isUnordered(), 3865 /*finalCount=*/false, mlir::ValueRange{innerArg}); 3866 innerArg = loop.getRegionIterArgs().front(); 3867 if (explicitSpaceIsActive()) 3868 explicitSpace->setInnerArg(0, innerArg); 3869 } else { 3870 loop = builder.create<fir::DoLoopOp>(loc, zero, i.value(), one, 3871 isUnordered(), 3872 /*finalCount=*/false); 3873 } 3874 ivars.push_back(loop.getInductionVar()); 3875 loops.push_back(loop); 3876 } 3877 3878 if (innerArg) 3879 for (std::remove_const_t<decltype(loopDepth)> i = 0; i + 1 < loopDepth; 3880 ++i) { 3881 builder.setInsertionPointToEnd(loops[i].getBody()); 3882 builder.create<fir::ResultOp>(loc, loops[i + 1].getResult(0)); 3883 } 3884 3885 // Move insertion point to the start of the innermost loop in the nest. 3886 builder.setInsertionPointToStart(loops.back().getBody()); 3887 // Set `afterLoopNest` to just after the entire loop nest. 3888 auto currPt = builder.saveInsertionPoint(); 3889 builder.setInsertionPointAfter(loops[0]); 3890 auto afterLoopNest = builder.saveInsertionPoint(); 3891 builder.restoreInsertionPoint(currPt); 3892 3893 // Put the implicit loop variables in row to column order to match FIR's 3894 // Ops. (The loops were constructed from outermost column to innermost 3895 // row.) 3896 mlir::Value outerRes = loops[0].getResult(0); 3897 return {IterationSpace(innerArg, outerRes, llvm::reverse(ivars)), 3898 afterLoopNest}; 3899 } 3900 3901 /// Build the iteration space into which the array expression will be 3902 /// lowered. The resultType is used to create a temporary, if needed. 3903 std::pair<IterationSpace, mlir::OpBuilder::InsertPoint> 3904 genIterSpace(mlir::Type resultType) { 3905 mlir::Location loc = getLoc(); 3906 llvm::SmallVector<mlir::Value> shape = genIterationShape(); 3907 if (!destination) { 3908 // Allocate storage for the result if it is not already provided. 3909 destination = createAndLoadSomeArrayTemp(resultType, shape); 3910 } 3911 3912 // Generate the lazy mask allocation, if one was given. 3913 if (ccPrelude.hasValue()) 3914 ccPrelude.getValue()(shape); 3915 3916 // Now handle the implicit loops. 3917 mlir::Value inner = explicitSpaceIsActive() 3918 ? explicitSpace->getInnerArgs().front() 3919 : destination.getResult(); 3920 auto [iters, afterLoopNest] = genImplicitLoops(shape, inner); 3921 mlir::Value innerArg = iters.innerArgument(); 3922 3923 // Generate the mask conditional structure, if there are masks. Unlike the 3924 // explicit masks, which are interleaved, these mask expression appear in 3925 // the innermost loop. 3926 if (implicitSpaceHasMasks()) { 3927 // Recover the cached condition from the mask buffer. 3928 auto genCond = [&](Fortran::lower::FrontEndExpr e, IterSpace iters) { 3929 return implicitSpace->getBoundClosure(e)(iters); 3930 }; 3931 3932 // Handle the negated conditions in topological order of the WHERE 3933 // clauses. See 10.2.3.2p4 as to why this control structure is produced. 3934 for (llvm::SmallVector<Fortran::lower::FrontEndExpr> maskExprs : 3935 implicitSpace->getMasks()) { 3936 const std::size_t size = maskExprs.size() - 1; 3937 auto genFalseBlock = [&](const auto *e, auto &&cond) { 3938 auto ifOp = builder.create<fir::IfOp>( 3939 loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond), 3940 /*withElseRegion=*/true); 3941 builder.create<fir::ResultOp>(loc, ifOp.getResult(0)); 3942 builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); 3943 builder.create<fir::ResultOp>(loc, innerArg); 3944 builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 3945 }; 3946 auto genTrueBlock = [&](const auto *e, auto &&cond) { 3947 auto ifOp = builder.create<fir::IfOp>( 3948 loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond), 3949 /*withElseRegion=*/true); 3950 builder.create<fir::ResultOp>(loc, ifOp.getResult(0)); 3951 builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 3952 builder.create<fir::ResultOp>(loc, innerArg); 3953 builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); 3954 }; 3955 for (std::size_t i = 0; i < size; ++i) 3956 if (const auto *e = maskExprs[i]) 3957 genFalseBlock(e, genCond(e, iters)); 3958 3959 // The last condition is either non-negated or unconditionally negated. 3960 if (const auto *e = maskExprs[size]) 3961 genTrueBlock(e, genCond(e, iters)); 3962 } 3963 } 3964 3965 // We're ready to lower the body (an assignment statement) for this context 3966 // of loop nests at this point. 3967 return {iters, afterLoopNest}; 3968 } 3969 3970 fir::ArrayLoadOp 3971 createAndLoadSomeArrayTemp(mlir::Type type, 3972 llvm::ArrayRef<mlir::Value> shape) { 3973 if (ccLoadDest.hasValue()) 3974 return ccLoadDest.getValue()(shape); 3975 auto seqTy = type.dyn_cast<fir::SequenceType>(); 3976 assert(seqTy && "must be an array"); 3977 mlir::Location loc = getLoc(); 3978 // TODO: Need to thread the length parameters here. For character, they may 3979 // differ from the operands length (e.g concatenation). So the array loads 3980 // type parameters are not enough. 3981 if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>()) 3982 if (charTy.hasDynamicLen()) 3983 TODO(loc, "character array expression temp with dynamic length"); 3984 if (auto recTy = seqTy.getEleTy().dyn_cast<fir::RecordType>()) 3985 if (recTy.getNumLenParams() > 0) 3986 TODO(loc, "derived type array expression temp with length parameters"); 3987 mlir::Value temp = seqTy.hasConstantShape() 3988 ? builder.create<fir::AllocMemOp>(loc, type) 3989 : builder.create<fir::AllocMemOp>( 3990 loc, type, ".array.expr", llvm::None, shape); 3991 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 3992 stmtCtx.attachCleanup( 3993 [bldr, loc, temp]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 3994 mlir::Value shapeOp = genShapeOp(shape); 3995 return builder.create<fir::ArrayLoadOp>(loc, seqTy, temp, shapeOp, 3996 /*slice=*/mlir::Value{}, 3997 llvm::None); 3998 } 3999 4000 static fir::ShapeOp genShapeOp(mlir::Location loc, fir::FirOpBuilder &builder, 4001 llvm::ArrayRef<mlir::Value> shape) { 4002 mlir::IndexType idxTy = builder.getIndexType(); 4003 llvm::SmallVector<mlir::Value> idxShape; 4004 for (auto s : shape) 4005 idxShape.push_back(builder.createConvert(loc, idxTy, s)); 4006 auto shapeTy = fir::ShapeType::get(builder.getContext(), idxShape.size()); 4007 return builder.create<fir::ShapeOp>(loc, shapeTy, idxShape); 4008 } 4009 4010 fir::ShapeOp genShapeOp(llvm::ArrayRef<mlir::Value> shape) { 4011 return genShapeOp(getLoc(), builder, shape); 4012 } 4013 4014 //===--------------------------------------------------------------------===// 4015 // Expression traversal and lowering. 4016 //===--------------------------------------------------------------------===// 4017 4018 /// Lower the expression, \p x, in a scalar context. 4019 template <typename A> 4020 ExtValue asScalar(const A &x) { 4021 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.genval(x); 4022 } 4023 4024 /// Lower the expression, \p x, in a scalar context. If this is an explicit 4025 /// space, the expression may be scalar and refer to an array. We want to 4026 /// raise the array access to array operations in FIR to analyze potential 4027 /// conflicts even when the result is a scalar element. 4028 template <typename A> 4029 ExtValue asScalarArray(const A &x) { 4030 return explicitSpaceIsActive() ? genarr(x)(IterationSpace{}) : asScalar(x); 4031 } 4032 4033 /// Lower the expression in a scalar context to a memory reference. 4034 template <typename A> 4035 ExtValue asScalarRef(const A &x) { 4036 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.gen(x); 4037 } 4038 4039 /// Lower an expression without dereferencing any indirection that may be 4040 /// a nullptr (because this is an absent optional or unallocated/disassociated 4041 /// descriptor). The returned expression cannot be addressed directly, it is 4042 /// meant to inquire about its status before addressing the related entity. 4043 template <typename A> 4044 ExtValue asInquired(const A &x) { 4045 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx} 4046 .lowerIntrinsicArgumentAsInquired(x); 4047 } 4048 4049 // An expression with non-zero rank is an array expression. 4050 template <typename A> 4051 bool isArray(const A &x) const { 4052 return x.Rank() != 0; 4053 } 4054 4055 /// Some temporaries are allocated on an element-by-element basis during the 4056 /// array expression evaluation. Collect the cleanups here so the resources 4057 /// can be freed before the next loop iteration, avoiding memory leaks. etc. 4058 Fortran::lower::StatementContext &getElementCtx() { 4059 if (!elementCtx) { 4060 stmtCtx.pushScope(); 4061 elementCtx = true; 4062 } 4063 return stmtCtx; 4064 } 4065 4066 /// If there were temporaries created for this element evaluation, finalize 4067 /// and deallocate the resources now. This should be done just prior the the 4068 /// fir::ResultOp at the end of the innermost loop. 4069 void finalizeElementCtx() { 4070 if (elementCtx) { 4071 stmtCtx.finalize(/*popScope=*/true); 4072 elementCtx = false; 4073 } 4074 } 4075 4076 /// Lower an elemental function array argument. This ensures array 4077 /// sub-expressions that are not variables and must be passed by address 4078 /// are lowered by value and placed in memory. 4079 template <typename A> 4080 CC genElementalArgument(const A &x) { 4081 // Ensure the returned element is in memory if this is what was requested. 4082 if ((semant == ConstituentSemantics::RefOpaque || 4083 semant == ConstituentSemantics::DataAddr || 4084 semant == ConstituentSemantics::ByValueArg)) { 4085 if (!Fortran::evaluate::IsVariable(x)) { 4086 PushSemantics(ConstituentSemantics::DataValue); 4087 CC cc = genarr(x); 4088 mlir::Location loc = getLoc(); 4089 if (isParenthesizedVariable(x)) { 4090 // Parenthesised variables are lowered to a reference to the variable 4091 // storage. When passing it as an argument, a copy must be passed. 4092 return [=](IterSpace iters) -> ExtValue { 4093 return createInMemoryScalarCopy(builder, loc, cc(iters)); 4094 }; 4095 } 4096 mlir::Type storageType = 4097 fir::unwrapSequenceType(converter.genType(toEvExpr(x))); 4098 return [=](IterSpace iters) -> ExtValue { 4099 return placeScalarValueInMemory(builder, loc, cc(iters), storageType); 4100 }; 4101 } 4102 } 4103 return genarr(x); 4104 } 4105 4106 // A procedure reference to a Fortran elemental intrinsic procedure. 4107 CC genElementalIntrinsicProcRef( 4108 const Fortran::evaluate::ProcedureRef &procRef, 4109 llvm::Optional<mlir::Type> retTy, 4110 const Fortran::evaluate::SpecificIntrinsic &intrinsic) { 4111 llvm::SmallVector<CC> operands; 4112 llvm::StringRef name = intrinsic.name; 4113 const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering = 4114 Fortran::lower::getIntrinsicArgumentLowering(name); 4115 mlir::Location loc = getLoc(); 4116 if (Fortran::lower::intrinsicRequiresCustomOptionalHandling( 4117 procRef, intrinsic, converter)) { 4118 using CcPairT = std::pair<CC, llvm::Optional<mlir::Value>>; 4119 llvm::SmallVector<CcPairT> operands; 4120 auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) { 4121 if (expr.Rank() == 0) { 4122 ExtValue optionalArg = this->asInquired(expr); 4123 mlir::Value isPresent = 4124 genActualIsPresentTest(builder, loc, optionalArg); 4125 operands.emplace_back( 4126 [=](IterSpace iters) -> ExtValue { 4127 return genLoad(builder, loc, optionalArg); 4128 }, 4129 isPresent); 4130 } else { 4131 auto [cc, isPresent, _] = this->genOptionalArrayFetch(expr); 4132 operands.emplace_back(cc, isPresent); 4133 } 4134 }; 4135 auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) { 4136 PushSemantics(ConstituentSemantics::RefTransparent); 4137 operands.emplace_back(genElementalArgument(expr), llvm::None); 4138 }; 4139 Fortran::lower::prepareCustomIntrinsicArgument( 4140 procRef, intrinsic, retTy, prepareOptionalArg, prepareOtherArg, 4141 converter); 4142 4143 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 4144 llvm::StringRef name = intrinsic.name; 4145 return [=](IterSpace iters) -> ExtValue { 4146 auto getArgument = [&](std::size_t i) -> ExtValue { 4147 return operands[i].first(iters); 4148 }; 4149 auto isPresent = [&](std::size_t i) -> llvm::Optional<mlir::Value> { 4150 return operands[i].second; 4151 }; 4152 return Fortran::lower::lowerCustomIntrinsic( 4153 *bldr, loc, name, retTy, isPresent, getArgument, operands.size(), 4154 getElementCtx()); 4155 }; 4156 } 4157 /// Otherwise, pre-lower arguments and use intrinsic lowering utility. 4158 for (const auto &[arg, dummy] : 4159 llvm::zip(procRef.arguments(), 4160 intrinsic.characteristics.value().dummyArguments)) { 4161 const auto *expr = 4162 Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg); 4163 if (!expr) { 4164 // Absent optional. 4165 operands.emplace_back([=](IterSpace) { return mlir::Value{}; }); 4166 } else if (!argLowering) { 4167 // No argument lowering instruction, lower by value. 4168 PushSemantics(ConstituentSemantics::RefTransparent); 4169 operands.emplace_back(genElementalArgument(*expr)); 4170 } else { 4171 // Ad-hoc argument lowering handling. 4172 Fortran::lower::ArgLoweringRule argRules = 4173 Fortran::lower::lowerIntrinsicArgumentAs(getLoc(), *argLowering, 4174 dummy.name); 4175 if (argRules.handleDynamicOptional && 4176 Fortran::evaluate::MayBePassedAsAbsentOptional( 4177 *expr, converter.getFoldingContext())) { 4178 // Currently, there is not elemental intrinsic that requires lowering 4179 // a potentially absent argument to something else than a value (apart 4180 // from character MAX/MIN that are handled elsewhere.) 4181 if (argRules.lowerAs != Fortran::lower::LowerIntrinsicArgAs::Value) 4182 TODO(loc, "lowering non trivial optional elemental intrinsic array " 4183 "argument"); 4184 PushSemantics(ConstituentSemantics::RefTransparent); 4185 operands.emplace_back(genarrForwardOptionalArgumentToCall(*expr)); 4186 continue; 4187 } 4188 switch (argRules.lowerAs) { 4189 case Fortran::lower::LowerIntrinsicArgAs::Value: { 4190 PushSemantics(ConstituentSemantics::RefTransparent); 4191 operands.emplace_back(genElementalArgument(*expr)); 4192 } break; 4193 case Fortran::lower::LowerIntrinsicArgAs::Addr: { 4194 // Note: assume does not have Fortran VALUE attribute semantics. 4195 PushSemantics(ConstituentSemantics::RefOpaque); 4196 operands.emplace_back(genElementalArgument(*expr)); 4197 } break; 4198 case Fortran::lower::LowerIntrinsicArgAs::Box: { 4199 PushSemantics(ConstituentSemantics::RefOpaque); 4200 auto lambda = genElementalArgument(*expr); 4201 operands.emplace_back([=](IterSpace iters) { 4202 return builder.createBox(loc, lambda(iters)); 4203 }); 4204 } break; 4205 case Fortran::lower::LowerIntrinsicArgAs::Inquired: 4206 TODO(loc, "intrinsic function with inquired argument"); 4207 break; 4208 } 4209 } 4210 } 4211 4212 // Let the intrinsic library lower the intrinsic procedure call 4213 return [=](IterSpace iters) { 4214 llvm::SmallVector<ExtValue> args; 4215 for (const auto &cc : operands) 4216 args.push_back(cc(iters)); 4217 return Fortran::lower::genIntrinsicCall(builder, loc, name, retTy, args, 4218 getElementCtx()); 4219 }; 4220 } 4221 4222 /// Lower a procedure reference to a user-defined elemental procedure. 4223 CC genElementalUserDefinedProcRef( 4224 const Fortran::evaluate::ProcedureRef &procRef, 4225 llvm::Optional<mlir::Type> retTy) { 4226 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 4227 4228 // 10.1.4 p5. Impure elemental procedures must be called in element order. 4229 if (const Fortran::semantics::Symbol *procSym = procRef.proc().GetSymbol()) 4230 if (!Fortran::semantics::IsPureProcedure(*procSym)) 4231 setUnordered(false); 4232 4233 Fortran::lower::CallerInterface caller(procRef, converter); 4234 llvm::SmallVector<CC> operands; 4235 operands.reserve(caller.getPassedArguments().size()); 4236 mlir::Location loc = getLoc(); 4237 mlir::FunctionType callSiteType = caller.genFunctionType(); 4238 for (const Fortran::lower::CallInterface< 4239 Fortran::lower::CallerInterface>::PassedEntity &arg : 4240 caller.getPassedArguments()) { 4241 // 15.8.3 p1. Elemental procedure with intent(out)/intent(inout) 4242 // arguments must be called in element order. 4243 if (arg.mayBeModifiedByCall()) 4244 setUnordered(false); 4245 const auto *actual = arg.entity; 4246 mlir::Type argTy = callSiteType.getInput(arg.firArgument); 4247 if (!actual) { 4248 // Optional dummy argument for which there is no actual argument. 4249 auto absent = builder.create<fir::AbsentOp>(loc, argTy); 4250 operands.emplace_back([=](IterSpace) { return absent; }); 4251 continue; 4252 } 4253 const auto *expr = actual->UnwrapExpr(); 4254 if (!expr) 4255 TODO(loc, "assumed type actual argument lowering"); 4256 4257 LLVM_DEBUG(expr->AsFortran(llvm::dbgs() 4258 << "argument: " << arg.firArgument << " = [") 4259 << "]\n"); 4260 if (arg.isOptional() && Fortran::evaluate::MayBePassedAsAbsentOptional( 4261 *expr, converter.getFoldingContext())) 4262 TODO(loc, 4263 "passing dynamically optional argument to elemental procedures"); 4264 switch (arg.passBy) { 4265 case PassBy::Value: { 4266 // True pass-by-value semantics. 4267 PushSemantics(ConstituentSemantics::RefTransparent); 4268 operands.emplace_back(genElementalArgument(*expr)); 4269 } break; 4270 case PassBy::BaseAddressValueAttribute: { 4271 // VALUE attribute or pass-by-reference to a copy semantics. (byval*) 4272 if (isArray(*expr)) { 4273 PushSemantics(ConstituentSemantics::ByValueArg); 4274 operands.emplace_back(genElementalArgument(*expr)); 4275 } else { 4276 // Store scalar value in a temp to fulfill VALUE attribute. 4277 mlir::Value val = fir::getBase(asScalar(*expr)); 4278 mlir::Value temp = builder.createTemporary( 4279 loc, val.getType(), 4280 llvm::ArrayRef<mlir::NamedAttribute>{ 4281 Fortran::lower::getAdaptToByRefAttr(builder)}); 4282 builder.create<fir::StoreOp>(loc, val, temp); 4283 operands.emplace_back( 4284 [=](IterSpace iters) -> ExtValue { return temp; }); 4285 } 4286 } break; 4287 case PassBy::BaseAddress: { 4288 if (isArray(*expr)) { 4289 PushSemantics(ConstituentSemantics::RefOpaque); 4290 operands.emplace_back(genElementalArgument(*expr)); 4291 } else { 4292 ExtValue exv = asScalarRef(*expr); 4293 operands.emplace_back([=](IterSpace iters) { return exv; }); 4294 } 4295 } break; 4296 case PassBy::CharBoxValueAttribute: { 4297 if (isArray(*expr)) { 4298 PushSemantics(ConstituentSemantics::DataValue); 4299 auto lambda = genElementalArgument(*expr); 4300 operands.emplace_back([=](IterSpace iters) { 4301 return fir::factory::CharacterExprHelper{builder, loc} 4302 .createTempFrom(lambda(iters)); 4303 }); 4304 } else { 4305 fir::factory::CharacterExprHelper helper(builder, loc); 4306 fir::CharBoxValue argVal = helper.createTempFrom(asScalarRef(*expr)); 4307 operands.emplace_back( 4308 [=](IterSpace iters) -> ExtValue { return argVal; }); 4309 } 4310 } break; 4311 case PassBy::BoxChar: { 4312 PushSemantics(ConstituentSemantics::RefOpaque); 4313 operands.emplace_back(genElementalArgument(*expr)); 4314 } break; 4315 case PassBy::AddressAndLength: 4316 // PassBy::AddressAndLength is only used for character results. Results 4317 // are not handled here. 4318 fir::emitFatalError( 4319 loc, "unexpected PassBy::AddressAndLength in elemental call"); 4320 break; 4321 case PassBy::CharProcTuple: { 4322 ExtValue argRef = asScalarRef(*expr); 4323 mlir::Value tuple = createBoxProcCharTuple( 4324 converter, argTy, fir::getBase(argRef), fir::getLen(argRef)); 4325 operands.emplace_back( 4326 [=](IterSpace iters) -> ExtValue { return tuple; }); 4327 } break; 4328 case PassBy::Box: 4329 case PassBy::MutableBox: 4330 // See C15100 and C15101 4331 fir::emitFatalError(loc, "cannot be POINTER, ALLOCATABLE"); 4332 } 4333 } 4334 4335 if (caller.getIfIndirectCallSymbol()) 4336 fir::emitFatalError(loc, "cannot be indirect call"); 4337 4338 // The lambda is mutable so that `caller` copy can be modified inside it. 4339 return 4340 [=, caller = std::move(caller)](IterSpace iters) mutable -> ExtValue { 4341 for (const auto &[cc, argIface] : 4342 llvm::zip(operands, caller.getPassedArguments())) { 4343 auto exv = cc(iters); 4344 auto arg = exv.match( 4345 [&](const fir::CharBoxValue &cb) -> mlir::Value { 4346 return fir::factory::CharacterExprHelper{builder, loc} 4347 .createEmbox(cb); 4348 }, 4349 [&](const auto &) { return fir::getBase(exv); }); 4350 caller.placeInput(argIface, arg); 4351 } 4352 return ScalarExprLowering{loc, converter, symMap, getElementCtx()} 4353 .genCallOpAndResult(caller, callSiteType, retTy); 4354 }; 4355 } 4356 4357 /// Generate a procedure reference. This code is shared for both functions and 4358 /// subroutines, the difference being reflected by `retTy`. 4359 CC genProcRef(const Fortran::evaluate::ProcedureRef &procRef, 4360 llvm::Optional<mlir::Type> retTy) { 4361 mlir::Location loc = getLoc(); 4362 if (procRef.IsElemental()) { 4363 if (const Fortran::evaluate::SpecificIntrinsic *intrin = 4364 procRef.proc().GetSpecificIntrinsic()) { 4365 // All elemental intrinsic functions are pure and cannot modify their 4366 // arguments. The only elemental subroutine, MVBITS has an Intent(inout) 4367 // argument. So for this last one, loops must be in element order 4368 // according to 15.8.3 p1. 4369 if (!retTy) 4370 setUnordered(false); 4371 4372 // Elemental intrinsic call. 4373 // The intrinsic procedure is called once per element of the array. 4374 return genElementalIntrinsicProcRef(procRef, retTy, *intrin); 4375 } 4376 if (ScalarExprLowering::isStatementFunctionCall(procRef)) 4377 fir::emitFatalError(loc, "statement function cannot be elemental"); 4378 4379 // Elemental call. 4380 // The procedure is called once per element of the array argument(s). 4381 return genElementalUserDefinedProcRef(procRef, retTy); 4382 } 4383 4384 // Transformational call. 4385 // The procedure is called once and produces a value of rank > 0. 4386 if (const Fortran::evaluate::SpecificIntrinsic *intrinsic = 4387 procRef.proc().GetSpecificIntrinsic()) { 4388 if (explicitSpaceIsActive() && procRef.Rank() == 0) { 4389 // Elide any implicit loop iters. 4390 return [=, &procRef](IterSpace) { 4391 return ScalarExprLowering{loc, converter, symMap, stmtCtx} 4392 .genIntrinsicRef(procRef, *intrinsic, retTy); 4393 }; 4394 } 4395 return genarr( 4396 ScalarExprLowering{loc, converter, symMap, stmtCtx}.genIntrinsicRef( 4397 procRef, *intrinsic, retTy)); 4398 } 4399 4400 if (explicitSpaceIsActive() && procRef.Rank() == 0) { 4401 // Elide any implicit loop iters. 4402 return [=, &procRef](IterSpace) { 4403 return ScalarExprLowering{loc, converter, symMap, stmtCtx} 4404 .genProcedureRef(procRef, retTy); 4405 }; 4406 } 4407 // In the default case, the call can be hoisted out of the loop nest. Apply 4408 // the iterations to the result, which may be an array value. 4409 return genarr( 4410 ScalarExprLowering{loc, converter, symMap, stmtCtx}.genProcedureRef( 4411 procRef, retTy)); 4412 } 4413 4414 template <typename A> 4415 CC genScalarAndForwardValue(const A &x) { 4416 ExtValue result = asScalar(x); 4417 return [=](IterSpace) { return result; }; 4418 } 4419 4420 template <typename A, typename = std::enable_if_t<Fortran::common::HasMember< 4421 A, Fortran::evaluate::TypelessExpression>>> 4422 CC genarr(const A &x) { 4423 return genScalarAndForwardValue(x); 4424 } 4425 4426 template <typename A> 4427 CC genarr(const Fortran::evaluate::Expr<A> &x) { 4428 LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(llvm::dbgs(), x)); 4429 if (isArray(x) || explicitSpaceIsActive() || 4430 isElementalProcWithArrayArgs(x)) 4431 return std::visit([&](const auto &e) { return genarr(e); }, x.u); 4432 return genScalarAndForwardValue(x); 4433 } 4434 4435 // Converting a value of memory bound type requires creating a temp and 4436 // copying the value. 4437 static ExtValue convertAdjustedType(fir::FirOpBuilder &builder, 4438 mlir::Location loc, mlir::Type toType, 4439 const ExtValue &exv) { 4440 return exv.match( 4441 [&](const fir::CharBoxValue &cb) -> ExtValue { 4442 mlir::Value len = cb.getLen(); 4443 auto mem = 4444 builder.create<fir::AllocaOp>(loc, toType, mlir::ValueRange{len}); 4445 fir::CharBoxValue result(mem, len); 4446 fir::factory::CharacterExprHelper{builder, loc}.createAssign( 4447 ExtValue{result}, exv); 4448 return result; 4449 }, 4450 [&](const auto &) -> ExtValue { 4451 fir::emitFatalError(loc, "convert on adjusted extended value"); 4452 }); 4453 } 4454 template <Fortran::common::TypeCategory TC1, int KIND, 4455 Fortran::common::TypeCategory TC2> 4456 CC genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, 4457 TC2> &x) { 4458 mlir::Location loc = getLoc(); 4459 auto lambda = genarr(x.left()); 4460 mlir::Type ty = converter.genType(TC1, KIND); 4461 return [=](IterSpace iters) -> ExtValue { 4462 auto exv = lambda(iters); 4463 mlir::Value val = fir::getBase(exv); 4464 auto valTy = val.getType(); 4465 if (elementTypeWasAdjusted(valTy) && 4466 !(fir::isa_ref_type(valTy) && fir::isa_integer(ty))) 4467 return convertAdjustedType(builder, loc, ty, exv); 4468 return builder.createConvert(loc, ty, val); 4469 }; 4470 } 4471 4472 template <int KIND> 4473 CC genarr(const Fortran::evaluate::ComplexComponent<KIND> &x) { 4474 TODO(getLoc(), "ComplexComponent<KIND>"); 4475 } 4476 4477 template <typename T> 4478 CC genarr(const Fortran::evaluate::Parentheses<T> &x) { 4479 mlir::Location loc = getLoc(); 4480 if (isReferentiallyOpaque()) { 4481 // Context is a call argument in, for example, an elemental procedure 4482 // call. TODO: all array arguments should use array_load, array_access, 4483 // array_amend, and INTENT(OUT), INTENT(INOUT) arguments should have 4484 // array_merge_store ops. 4485 TODO(loc, "parentheses on argument in elemental call"); 4486 } 4487 auto f = genarr(x.left()); 4488 return [=](IterSpace iters) -> ExtValue { 4489 auto val = f(iters); 4490 mlir::Value base = fir::getBase(val); 4491 auto newBase = 4492 builder.create<fir::NoReassocOp>(loc, base.getType(), base); 4493 return fir::substBase(val, newBase); 4494 }; 4495 } 4496 template <int KIND> 4497 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 4498 Fortran::common::TypeCategory::Integer, KIND>> &x) { 4499 mlir::Location loc = getLoc(); 4500 auto f = genarr(x.left()); 4501 return [=](IterSpace iters) -> ExtValue { 4502 mlir::Value val = fir::getBase(f(iters)); 4503 mlir::Type ty = 4504 converter.genType(Fortran::common::TypeCategory::Integer, KIND); 4505 mlir::Value zero = builder.createIntegerConstant(loc, ty, 0); 4506 return builder.create<mlir::arith::SubIOp>(loc, zero, val); 4507 }; 4508 } 4509 template <int KIND> 4510 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 4511 Fortran::common::TypeCategory::Real, KIND>> &x) { 4512 mlir::Location loc = getLoc(); 4513 auto f = genarr(x.left()); 4514 return [=](IterSpace iters) -> ExtValue { 4515 return builder.create<mlir::arith::NegFOp>(loc, fir::getBase(f(iters))); 4516 }; 4517 } 4518 template <int KIND> 4519 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 4520 Fortran::common::TypeCategory::Complex, KIND>> &x) { 4521 mlir::Location loc = getLoc(); 4522 auto f = genarr(x.left()); 4523 return [=](IterSpace iters) -> ExtValue { 4524 return builder.create<fir::NegcOp>(loc, fir::getBase(f(iters))); 4525 }; 4526 } 4527 4528 //===--------------------------------------------------------------------===// 4529 // Binary elemental ops 4530 //===--------------------------------------------------------------------===// 4531 4532 template <typename OP, typename A> 4533 CC createBinaryOp(const A &evEx) { 4534 mlir::Location loc = getLoc(); 4535 auto lambda = genarr(evEx.left()); 4536 auto rf = genarr(evEx.right()); 4537 return [=](IterSpace iters) -> ExtValue { 4538 mlir::Value left = fir::getBase(lambda(iters)); 4539 mlir::Value right = fir::getBase(rf(iters)); 4540 return builder.create<OP>(loc, left, right); 4541 }; 4542 } 4543 4544 #undef GENBIN 4545 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \ 4546 template <int KIND> \ 4547 CC genarr(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \ 4548 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \ 4549 return createBinaryOp<GenBinFirOp>(x); \ 4550 } 4551 4552 GENBIN(Add, Integer, mlir::arith::AddIOp) 4553 GENBIN(Add, Real, mlir::arith::AddFOp) 4554 GENBIN(Add, Complex, fir::AddcOp) 4555 GENBIN(Subtract, Integer, mlir::arith::SubIOp) 4556 GENBIN(Subtract, Real, mlir::arith::SubFOp) 4557 GENBIN(Subtract, Complex, fir::SubcOp) 4558 GENBIN(Multiply, Integer, mlir::arith::MulIOp) 4559 GENBIN(Multiply, Real, mlir::arith::MulFOp) 4560 GENBIN(Multiply, Complex, fir::MulcOp) 4561 GENBIN(Divide, Integer, mlir::arith::DivSIOp) 4562 GENBIN(Divide, Real, mlir::arith::DivFOp) 4563 GENBIN(Divide, Complex, fir::DivcOp) 4564 4565 template <Fortran::common::TypeCategory TC, int KIND> 4566 CC genarr( 4567 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) { 4568 mlir::Location loc = getLoc(); 4569 mlir::Type ty = converter.genType(TC, KIND); 4570 auto lf = genarr(x.left()); 4571 auto rf = genarr(x.right()); 4572 return [=](IterSpace iters) -> ExtValue { 4573 mlir::Value lhs = fir::getBase(lf(iters)); 4574 mlir::Value rhs = fir::getBase(rf(iters)); 4575 return Fortran::lower::genPow(builder, loc, ty, lhs, rhs); 4576 }; 4577 } 4578 template <Fortran::common::TypeCategory TC, int KIND> 4579 CC genarr( 4580 const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) { 4581 TODO(getLoc(), "genarr Extremum<Fortran::evaluate::Type<TC, KIND>>"); 4582 } 4583 template <Fortran::common::TypeCategory TC, int KIND> 4584 CC genarr( 4585 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>> 4586 &x) { 4587 TODO(getLoc(), "genarr RealToIntPower<Fortran::evaluate::Type<TC, KIND>>"); 4588 } 4589 template <int KIND> 4590 CC genarr(const Fortran::evaluate::ComplexConstructor<KIND> &x) { 4591 TODO(getLoc(), "genarr ComplexConstructor<KIND>"); 4592 } 4593 4594 /// Fortran's concatenation operator `//`. 4595 template <int KIND> 4596 CC genarr(const Fortran::evaluate::Concat<KIND> &x) { 4597 mlir::Location loc = getLoc(); 4598 auto lf = genarr(x.left()); 4599 auto rf = genarr(x.right()); 4600 return [=](IterSpace iters) -> ExtValue { 4601 auto lhs = lf(iters); 4602 auto rhs = rf(iters); 4603 const fir::CharBoxValue *lchr = lhs.getCharBox(); 4604 const fir::CharBoxValue *rchr = rhs.getCharBox(); 4605 if (lchr && rchr) { 4606 return fir::factory::CharacterExprHelper{builder, loc} 4607 .createConcatenate(*lchr, *rchr); 4608 } 4609 TODO(loc, "concat on unexpected extended values"); 4610 return mlir::Value{}; 4611 }; 4612 } 4613 4614 template <int KIND> 4615 CC genarr(const Fortran::evaluate::SetLength<KIND> &x) { 4616 auto lf = genarr(x.left()); 4617 mlir::Value rhs = fir::getBase(asScalar(x.right())); 4618 return [=](IterSpace iters) -> ExtValue { 4619 mlir::Value lhs = fir::getBase(lf(iters)); 4620 return fir::CharBoxValue{lhs, rhs}; 4621 }; 4622 } 4623 4624 template <typename A> 4625 CC genarr(const Fortran::evaluate::Constant<A> &x) { 4626 if (/*explicitSpaceIsActive() &&*/ x.Rank() == 0) 4627 return genScalarAndForwardValue(x); 4628 mlir::Location loc = getLoc(); 4629 mlir::IndexType idxTy = builder.getIndexType(); 4630 mlir::Type arrTy = converter.genType(toEvExpr(x)); 4631 std::string globalName = Fortran::lower::mangle::mangleArrayLiteral(x); 4632 fir::GlobalOp global = builder.getNamedGlobal(globalName); 4633 if (!global) { 4634 mlir::Type symTy = arrTy; 4635 mlir::Type eleTy = symTy.cast<fir::SequenceType>().getEleTy(); 4636 // If we have a rank-1 array of integer, real, or logical, then we can 4637 // create a global array with the dense attribute. 4638 // 4639 // The mlir tensor type can only handle integer, real, or logical. It 4640 // does not currently support nested structures which is required for 4641 // complex. 4642 // 4643 // Also, we currently handle just rank-1 since tensor type assumes 4644 // row major array ordering. We will need to reorder the dimensions 4645 // in the tensor type to support Fortran's column major array ordering. 4646 // How to create this tensor type is to be determined. 4647 if (x.Rank() == 1 && 4648 eleTy.isa<fir::LogicalType, mlir::IntegerType, mlir::FloatType>()) 4649 global = Fortran::lower::createDenseGlobal( 4650 loc, arrTy, globalName, builder.createInternalLinkage(), true, 4651 toEvExpr(x), converter); 4652 // Note: If call to createDenseGlobal() returns 0, then call 4653 // createGlobalConstant() below. 4654 if (!global) 4655 global = builder.createGlobalConstant( 4656 loc, arrTy, globalName, 4657 [&](fir::FirOpBuilder &builder) { 4658 Fortran::lower::StatementContext stmtCtx( 4659 /*cleanupProhibited=*/true); 4660 fir::ExtendedValue result = 4661 Fortran::lower::createSomeInitializerExpression( 4662 loc, converter, toEvExpr(x), symMap, stmtCtx); 4663 mlir::Value castTo = 4664 builder.createConvert(loc, arrTy, fir::getBase(result)); 4665 builder.create<fir::HasValueOp>(loc, castTo); 4666 }, 4667 builder.createInternalLinkage()); 4668 } 4669 auto addr = builder.create<fir::AddrOfOp>(getLoc(), global.resultType(), 4670 global.getSymbol()); 4671 auto seqTy = global.getType().cast<fir::SequenceType>(); 4672 llvm::SmallVector<mlir::Value> extents; 4673 for (auto extent : seqTy.getShape()) 4674 extents.push_back(builder.createIntegerConstant(loc, idxTy, extent)); 4675 if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>()) { 4676 mlir::Value len = builder.createIntegerConstant(loc, builder.getI64Type(), 4677 charTy.getLen()); 4678 return genarr(fir::CharArrayBoxValue{addr, len, extents}); 4679 } 4680 return genarr(fir::ArrayBoxValue{addr, extents}); 4681 } 4682 4683 //===--------------------------------------------------------------------===// 4684 // A vector subscript expression may be wrapped with a cast to INTEGER*8. 4685 // Get rid of it here so the vector can be loaded. Add it back when 4686 // generating the elemental evaluation (inside the loop nest). 4687 4688 static Fortran::lower::SomeExpr 4689 ignoreEvConvert(const Fortran::evaluate::Expr<Fortran::evaluate::Type< 4690 Fortran::common::TypeCategory::Integer, 8>> &x) { 4691 return std::visit([&](const auto &v) { return ignoreEvConvert(v); }, x.u); 4692 } 4693 template <Fortran::common::TypeCategory FROM> 4694 static Fortran::lower::SomeExpr ignoreEvConvert( 4695 const Fortran::evaluate::Convert< 4696 Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 8>, 4697 FROM> &x) { 4698 return toEvExpr(x.left()); 4699 } 4700 template <typename A> 4701 static Fortran::lower::SomeExpr ignoreEvConvert(const A &x) { 4702 return toEvExpr(x); 4703 } 4704 4705 //===--------------------------------------------------------------------===// 4706 // Get the `Se::Symbol*` for the subscript expression, `x`. This symbol can 4707 // be used to determine the lbound, ubound of the vector. 4708 4709 template <typename A> 4710 static const Fortran::semantics::Symbol * 4711 extractSubscriptSymbol(const Fortran::evaluate::Expr<A> &x) { 4712 return std::visit([&](const auto &v) { return extractSubscriptSymbol(v); }, 4713 x.u); 4714 } 4715 template <typename A> 4716 static const Fortran::semantics::Symbol * 4717 extractSubscriptSymbol(const Fortran::evaluate::Designator<A> &x) { 4718 return Fortran::evaluate::UnwrapWholeSymbolDataRef(x); 4719 } 4720 template <typename A> 4721 static const Fortran::semantics::Symbol *extractSubscriptSymbol(const A &x) { 4722 return nullptr; 4723 } 4724 4725 //===--------------------------------------------------------------------===// 4726 4727 /// Get the declared lower bound value of the array `x` in dimension `dim`. 4728 /// The argument `one` must be an ssa-value for the constant 1. 4729 mlir::Value getLBound(const ExtValue &x, unsigned dim, mlir::Value one) { 4730 return fir::factory::readLowerBound(builder, getLoc(), x, dim, one); 4731 } 4732 4733 /// Get the declared upper bound value of the array `x` in dimension `dim`. 4734 /// The argument `one` must be an ssa-value for the constant 1. 4735 mlir::Value getUBound(const ExtValue &x, unsigned dim, mlir::Value one) { 4736 mlir::Location loc = getLoc(); 4737 mlir::Value lb = getLBound(x, dim, one); 4738 mlir::Value extent = fir::factory::readExtent(builder, loc, x, dim); 4739 auto add = builder.create<mlir::arith::AddIOp>(loc, lb, extent); 4740 return builder.create<mlir::arith::SubIOp>(loc, add, one); 4741 } 4742 4743 /// Return the extent of the boxed array `x` in dimesion `dim`. 4744 mlir::Value getExtent(const ExtValue &x, unsigned dim) { 4745 return fir::factory::readExtent(builder, getLoc(), x, dim); 4746 } 4747 4748 template <typename A> 4749 ExtValue genArrayBase(const A &base) { 4750 ScalarExprLowering sel{getLoc(), converter, symMap, stmtCtx}; 4751 return base.IsSymbol() ? sel.gen(base.GetFirstSymbol()) 4752 : sel.gen(base.GetComponent()); 4753 } 4754 4755 template <typename A> 4756 bool hasEvArrayRef(const A &x) { 4757 struct HasEvArrayRefHelper 4758 : public Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper> { 4759 HasEvArrayRefHelper() 4760 : Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper>(*this) {} 4761 using Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper>::operator(); 4762 bool operator()(const Fortran::evaluate::ArrayRef &) const { 4763 return true; 4764 } 4765 } helper; 4766 return helper(x); 4767 } 4768 4769 CC genVectorSubscriptArrayFetch(const Fortran::lower::SomeExpr &expr, 4770 std::size_t dim) { 4771 PushSemantics(ConstituentSemantics::RefTransparent); 4772 auto saved = Fortran::common::ScopedSet(explicitSpace, nullptr); 4773 llvm::SmallVector<mlir::Value> savedDestShape = destShape; 4774 destShape.clear(); 4775 auto result = genarr(expr); 4776 if (destShape.empty()) 4777 TODO(getLoc(), "expected vector to have an extent"); 4778 assert(destShape.size() == 1 && "vector has rank > 1"); 4779 if (destShape[0] != savedDestShape[dim]) { 4780 // Not the same, so choose the smaller value. 4781 mlir::Location loc = getLoc(); 4782 auto cmp = builder.create<mlir::arith::CmpIOp>( 4783 loc, mlir::arith::CmpIPredicate::sgt, destShape[0], 4784 savedDestShape[dim]); 4785 auto sel = builder.create<mlir::arith::SelectOp>( 4786 loc, cmp, savedDestShape[dim], destShape[0]); 4787 savedDestShape[dim] = sel; 4788 destShape = savedDestShape; 4789 } 4790 return result; 4791 } 4792 4793 /// Generate an access by vector subscript using the index in the iteration 4794 /// vector at `dim`. 4795 mlir::Value genAccessByVector(mlir::Location loc, CC genArrFetch, 4796 IterSpace iters, std::size_t dim) { 4797 IterationSpace vecIters(iters, 4798 llvm::ArrayRef<mlir::Value>{iters.iterValue(dim)}); 4799 fir::ExtendedValue fetch = genArrFetch(vecIters); 4800 mlir::IndexType idxTy = builder.getIndexType(); 4801 return builder.createConvert(loc, idxTy, fir::getBase(fetch)); 4802 } 4803 4804 /// When we have an array reference, the expressions specified in each 4805 /// dimension may be slice operations (e.g. `i:j:k`), vectors, or simple 4806 /// (loop-invarianet) scalar expressions. This returns the base entity, the 4807 /// resulting type, and a continuation to adjust the default iteration space. 4808 void genSliceIndices(ComponentPath &cmptData, const ExtValue &arrayExv, 4809 const Fortran::evaluate::ArrayRef &x, bool atBase) { 4810 mlir::Location loc = getLoc(); 4811 mlir::IndexType idxTy = builder.getIndexType(); 4812 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); 4813 llvm::SmallVector<mlir::Value> &trips = cmptData.trips; 4814 LLVM_DEBUG(llvm::dbgs() << "array: " << arrayExv << '\n'); 4815 auto &pc = cmptData.pc; 4816 const bool useTripsForSlice = !explicitSpaceIsActive(); 4817 const bool createDestShape = destShape.empty(); 4818 bool useSlice = false; 4819 std::size_t shapeIndex = 0; 4820 for (auto sub : llvm::enumerate(x.subscript())) { 4821 const std::size_t subsIndex = sub.index(); 4822 std::visit( 4823 Fortran::common::visitors{ 4824 [&](const Fortran::evaluate::Triplet &t) { 4825 mlir::Value lowerBound; 4826 if (auto optLo = t.lower()) 4827 lowerBound = fir::getBase(asScalar(*optLo)); 4828 else 4829 lowerBound = getLBound(arrayExv, subsIndex, one); 4830 lowerBound = builder.createConvert(loc, idxTy, lowerBound); 4831 mlir::Value stride = fir::getBase(asScalar(t.stride())); 4832 stride = builder.createConvert(loc, idxTy, stride); 4833 if (useTripsForSlice || createDestShape) { 4834 // Generate a slice operation for the triplet. The first and 4835 // second position of the triplet may be omitted, and the 4836 // declared lbound and/or ubound expression values, 4837 // respectively, should be used instead. 4838 trips.push_back(lowerBound); 4839 mlir::Value upperBound; 4840 if (auto optUp = t.upper()) 4841 upperBound = fir::getBase(asScalar(*optUp)); 4842 else 4843 upperBound = getUBound(arrayExv, subsIndex, one); 4844 upperBound = builder.createConvert(loc, idxTy, upperBound); 4845 trips.push_back(upperBound); 4846 trips.push_back(stride); 4847 if (createDestShape) { 4848 auto extent = builder.genExtentFromTriplet( 4849 loc, lowerBound, upperBound, stride, idxTy); 4850 destShape.push_back(extent); 4851 } 4852 useSlice = true; 4853 } 4854 if (!useTripsForSlice) { 4855 auto currentPC = pc; 4856 pc = [=](IterSpace iters) { 4857 IterationSpace newIters = currentPC(iters); 4858 mlir::Value impliedIter = newIters.iterValue(subsIndex); 4859 // FIXME: must use the lower bound of this component. 4860 auto arrLowerBound = 4861 atBase ? getLBound(arrayExv, subsIndex, one) : one; 4862 auto initial = builder.create<mlir::arith::SubIOp>( 4863 loc, lowerBound, arrLowerBound); 4864 auto prod = builder.create<mlir::arith::MulIOp>( 4865 loc, impliedIter, stride); 4866 auto result = 4867 builder.create<mlir::arith::AddIOp>(loc, initial, prod); 4868 newIters.setIndexValue(subsIndex, result); 4869 return newIters; 4870 }; 4871 } 4872 shapeIndex++; 4873 }, 4874 [&](const Fortran::evaluate::IndirectSubscriptIntegerExpr &ie) { 4875 const auto &e = ie.value(); // dereference 4876 if (isArray(e)) { 4877 // This is a vector subscript. Use the index values as read 4878 // from a vector to determine the temporary array value. 4879 // Note: 9.5.3.3.3(3) specifies undefined behavior for 4880 // multiple updates to any specific array element through a 4881 // vector subscript with replicated values. 4882 assert(!isBoxValue() && 4883 "fir.box cannot be created with vector subscripts"); 4884 auto arrExpr = ignoreEvConvert(e); 4885 if (createDestShape) { 4886 destShape.push_back(fir::getExtentAtDimension( 4887 arrayExv, builder, loc, subsIndex)); 4888 } 4889 auto genArrFetch = 4890 genVectorSubscriptArrayFetch(arrExpr, shapeIndex); 4891 auto currentPC = pc; 4892 pc = [=](IterSpace iters) { 4893 IterationSpace newIters = currentPC(iters); 4894 auto val = genAccessByVector(loc, genArrFetch, newIters, 4895 subsIndex); 4896 // Value read from vector subscript array and normalized 4897 // using the base array's lower bound value. 4898 mlir::Value lb = fir::factory::readLowerBound( 4899 builder, loc, arrayExv, subsIndex, one); 4900 auto origin = builder.create<mlir::arith::SubIOp>( 4901 loc, idxTy, val, lb); 4902 newIters.setIndexValue(subsIndex, origin); 4903 return newIters; 4904 }; 4905 if (useTripsForSlice) { 4906 LLVM_ATTRIBUTE_UNUSED auto vectorSubscriptShape = 4907 getShape(arrayOperands.back()); 4908 auto undef = builder.create<fir::UndefOp>(loc, idxTy); 4909 trips.push_back(undef); 4910 trips.push_back(undef); 4911 trips.push_back(undef); 4912 } 4913 shapeIndex++; 4914 } else { 4915 // This is a regular scalar subscript. 4916 if (useTripsForSlice) { 4917 // A regular scalar index, which does not yield an array 4918 // section. Use a degenerate slice operation 4919 // `(e:undef:undef)` in this dimension as a placeholder. 4920 // This does not necessarily change the rank of the original 4921 // array, so the iteration space must also be extended to 4922 // include this expression in this dimension to adjust to 4923 // the array's declared rank. 4924 mlir::Value v = fir::getBase(asScalar(e)); 4925 trips.push_back(v); 4926 auto undef = builder.create<fir::UndefOp>(loc, idxTy); 4927 trips.push_back(undef); 4928 trips.push_back(undef); 4929 auto currentPC = pc; 4930 // Cast `e` to index type. 4931 mlir::Value iv = builder.createConvert(loc, idxTy, v); 4932 // Normalize `e` by subtracting the declared lbound. 4933 mlir::Value lb = fir::factory::readLowerBound( 4934 builder, loc, arrayExv, subsIndex, one); 4935 mlir::Value ivAdj = 4936 builder.create<mlir::arith::SubIOp>(loc, idxTy, iv, lb); 4937 // Add lbound adjusted value of `e` to the iteration vector 4938 // (except when creating a box because the iteration vector 4939 // is empty). 4940 if (!isBoxValue()) 4941 pc = [=](IterSpace iters) { 4942 IterationSpace newIters = currentPC(iters); 4943 newIters.insertIndexValue(subsIndex, ivAdj); 4944 return newIters; 4945 }; 4946 } else { 4947 auto currentPC = pc; 4948 mlir::Value newValue = fir::getBase(asScalarArray(e)); 4949 mlir::Value result = 4950 builder.createConvert(loc, idxTy, newValue); 4951 mlir::Value lb = fir::factory::readLowerBound( 4952 builder, loc, arrayExv, subsIndex, one); 4953 result = builder.create<mlir::arith::SubIOp>(loc, idxTy, 4954 result, lb); 4955 pc = [=](IterSpace iters) { 4956 IterationSpace newIters = currentPC(iters); 4957 newIters.insertIndexValue(subsIndex, result); 4958 return newIters; 4959 }; 4960 } 4961 } 4962 }}, 4963 sub.value().u); 4964 } 4965 if (!useSlice) 4966 trips.clear(); 4967 } 4968 4969 CC genarr(const Fortran::semantics::SymbolRef &sym, 4970 ComponentPath &components) { 4971 return genarr(sym.get(), components); 4972 } 4973 4974 ExtValue abstractArrayExtValue(mlir::Value val, mlir::Value len = {}) { 4975 return convertToArrayBoxValue(getLoc(), builder, val, len); 4976 } 4977 4978 CC genarr(const ExtValue &extMemref) { 4979 ComponentPath dummy(/*isImplicit=*/true); 4980 return genarr(extMemref, dummy); 4981 } 4982 4983 //===--------------------------------------------------------------------===// 4984 // Array construction 4985 //===--------------------------------------------------------------------===// 4986 4987 /// Target agnostic computation of the size of an element in the array. 4988 /// Returns the size in bytes with type `index` or a null Value if the element 4989 /// size is not constant. 4990 mlir::Value computeElementSize(const ExtValue &exv, mlir::Type eleTy, 4991 mlir::Type resTy) { 4992 mlir::Location loc = getLoc(); 4993 mlir::IndexType idxTy = builder.getIndexType(); 4994 mlir::Value multiplier = builder.createIntegerConstant(loc, idxTy, 1); 4995 if (fir::hasDynamicSize(eleTy)) { 4996 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 4997 // Array of char with dynamic length parameter. Downcast to an array 4998 // of singleton char, and scale by the len type parameter from 4999 // `exv`. 5000 exv.match( 5001 [&](const fir::CharBoxValue &cb) { multiplier = cb.getLen(); }, 5002 [&](const fir::CharArrayBoxValue &cb) { multiplier = cb.getLen(); }, 5003 [&](const fir::BoxValue &box) { 5004 multiplier = fir::factory::CharacterExprHelper(builder, loc) 5005 .readLengthFromBox(box.getAddr()); 5006 }, 5007 [&](const fir::MutableBoxValue &box) { 5008 multiplier = fir::factory::CharacterExprHelper(builder, loc) 5009 .readLengthFromBox(box.getAddr()); 5010 }, 5011 [&](const auto &) { 5012 fir::emitFatalError(loc, 5013 "array constructor element has unknown size"); 5014 }); 5015 fir::CharacterType newEleTy = fir::CharacterType::getSingleton( 5016 eleTy.getContext(), charTy.getFKind()); 5017 if (auto seqTy = resTy.dyn_cast<fir::SequenceType>()) { 5018 assert(eleTy == seqTy.getEleTy()); 5019 resTy = fir::SequenceType::get(seqTy.getShape(), newEleTy); 5020 } 5021 eleTy = newEleTy; 5022 } else { 5023 TODO(loc, "dynamic sized type"); 5024 } 5025 } 5026 mlir::Type eleRefTy = builder.getRefType(eleTy); 5027 mlir::Type resRefTy = builder.getRefType(resTy); 5028 mlir::Value nullPtr = builder.createNullConstant(loc, resRefTy); 5029 auto offset = builder.create<fir::CoordinateOp>( 5030 loc, eleRefTy, nullPtr, mlir::ValueRange{multiplier}); 5031 return builder.createConvert(loc, idxTy, offset); 5032 } 5033 5034 /// Get the function signature of the LLVM memcpy intrinsic. 5035 mlir::FunctionType memcpyType() { 5036 return fir::factory::getLlvmMemcpy(builder).getFunctionType(); 5037 } 5038 5039 /// Create a call to the LLVM memcpy intrinsic. 5040 void createCallMemcpy(llvm::ArrayRef<mlir::Value> args) { 5041 mlir::Location loc = getLoc(); 5042 mlir::FuncOp memcpyFunc = fir::factory::getLlvmMemcpy(builder); 5043 mlir::SymbolRefAttr funcSymAttr = 5044 builder.getSymbolRefAttr(memcpyFunc.getName()); 5045 mlir::FunctionType funcTy = memcpyFunc.getFunctionType(); 5046 builder.create<fir::CallOp>(loc, funcTy.getResults(), funcSymAttr, args); 5047 } 5048 5049 // Construct code to check for a buffer overrun and realloc the buffer when 5050 // space is depleted. This is done between each item in the ac-value-list. 5051 mlir::Value growBuffer(mlir::Value mem, mlir::Value needed, 5052 mlir::Value bufferSize, mlir::Value buffSize, 5053 mlir::Value eleSz) { 5054 mlir::Location loc = getLoc(); 5055 mlir::FuncOp reallocFunc = fir::factory::getRealloc(builder); 5056 auto cond = builder.create<mlir::arith::CmpIOp>( 5057 loc, mlir::arith::CmpIPredicate::sle, bufferSize, needed); 5058 auto ifOp = builder.create<fir::IfOp>(loc, mem.getType(), cond, 5059 /*withElseRegion=*/true); 5060 auto insPt = builder.saveInsertionPoint(); 5061 builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); 5062 // Not enough space, resize the buffer. 5063 mlir::IndexType idxTy = builder.getIndexType(); 5064 mlir::Value two = builder.createIntegerConstant(loc, idxTy, 2); 5065 auto newSz = builder.create<mlir::arith::MulIOp>(loc, needed, two); 5066 builder.create<fir::StoreOp>(loc, newSz, buffSize); 5067 mlir::Value byteSz = builder.create<mlir::arith::MulIOp>(loc, newSz, eleSz); 5068 mlir::SymbolRefAttr funcSymAttr = 5069 builder.getSymbolRefAttr(reallocFunc.getName()); 5070 mlir::FunctionType funcTy = reallocFunc.getFunctionType(); 5071 auto newMem = builder.create<fir::CallOp>( 5072 loc, funcTy.getResults(), funcSymAttr, 5073 llvm::ArrayRef<mlir::Value>{ 5074 builder.createConvert(loc, funcTy.getInputs()[0], mem), 5075 builder.createConvert(loc, funcTy.getInputs()[1], byteSz)}); 5076 mlir::Value castNewMem = 5077 builder.createConvert(loc, mem.getType(), newMem.getResult(0)); 5078 builder.create<fir::ResultOp>(loc, castNewMem); 5079 builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 5080 // Otherwise, just forward the buffer. 5081 builder.create<fir::ResultOp>(loc, mem); 5082 builder.restoreInsertionPoint(insPt); 5083 return ifOp.getResult(0); 5084 } 5085 5086 /// Copy the next value (or vector of values) into the array being 5087 /// constructed. 5088 mlir::Value copyNextArrayCtorSection(const ExtValue &exv, mlir::Value buffPos, 5089 mlir::Value buffSize, mlir::Value mem, 5090 mlir::Value eleSz, mlir::Type eleTy, 5091 mlir::Type eleRefTy, mlir::Type resTy) { 5092 mlir::Location loc = getLoc(); 5093 auto off = builder.create<fir::LoadOp>(loc, buffPos); 5094 auto limit = builder.create<fir::LoadOp>(loc, buffSize); 5095 mlir::IndexType idxTy = builder.getIndexType(); 5096 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); 5097 5098 if (fir::isRecordWithAllocatableMember(eleTy)) 5099 TODO(loc, "deep copy on allocatable members"); 5100 5101 if (!eleSz) { 5102 // Compute the element size at runtime. 5103 assert(fir::hasDynamicSize(eleTy)); 5104 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 5105 auto charBytes = 5106 builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8; 5107 mlir::Value bytes = 5108 builder.createIntegerConstant(loc, idxTy, charBytes); 5109 mlir::Value length = fir::getLen(exv); 5110 if (!length) 5111 fir::emitFatalError(loc, "result is not boxed character"); 5112 eleSz = builder.create<mlir::arith::MulIOp>(loc, bytes, length); 5113 } else { 5114 TODO(loc, "PDT size"); 5115 // Will call the PDT's size function with the type parameters. 5116 } 5117 } 5118 5119 // Compute the coordinate using `fir.coordinate_of`, or, if the type has 5120 // dynamic size, generating the pointer arithmetic. 5121 auto computeCoordinate = [&](mlir::Value buff, mlir::Value off) { 5122 mlir::Type refTy = eleRefTy; 5123 if (fir::hasDynamicSize(eleTy)) { 5124 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 5125 // Scale a simple pointer using dynamic length and offset values. 5126 auto chTy = fir::CharacterType::getSingleton(charTy.getContext(), 5127 charTy.getFKind()); 5128 refTy = builder.getRefType(chTy); 5129 mlir::Type toTy = builder.getRefType(builder.getVarLenSeqTy(chTy)); 5130 buff = builder.createConvert(loc, toTy, buff); 5131 off = builder.create<mlir::arith::MulIOp>(loc, off, eleSz); 5132 } else { 5133 TODO(loc, "PDT offset"); 5134 } 5135 } 5136 auto coor = builder.create<fir::CoordinateOp>(loc, refTy, buff, 5137 mlir::ValueRange{off}); 5138 return builder.createConvert(loc, eleRefTy, coor); 5139 }; 5140 5141 // Lambda to lower an abstract array box value. 5142 auto doAbstractArray = [&](const auto &v) { 5143 // Compute the array size. 5144 mlir::Value arrSz = one; 5145 for (auto ext : v.getExtents()) 5146 arrSz = builder.create<mlir::arith::MulIOp>(loc, arrSz, ext); 5147 5148 // Grow the buffer as needed. 5149 auto endOff = builder.create<mlir::arith::AddIOp>(loc, off, arrSz); 5150 mem = growBuffer(mem, endOff, limit, buffSize, eleSz); 5151 5152 // Copy the elements to the buffer. 5153 mlir::Value byteSz = 5154 builder.create<mlir::arith::MulIOp>(loc, arrSz, eleSz); 5155 auto buff = builder.createConvert(loc, fir::HeapType::get(resTy), mem); 5156 mlir::Value buffi = computeCoordinate(buff, off); 5157 llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( 5158 builder, loc, memcpyType(), buffi, v.getAddr(), byteSz, 5159 /*volatile=*/builder.createBool(loc, false)); 5160 createCallMemcpy(args); 5161 5162 // Save the incremented buffer position. 5163 builder.create<fir::StoreOp>(loc, endOff, buffPos); 5164 }; 5165 5166 // Copy a trivial scalar value into the buffer. 5167 auto doTrivialScalar = [&](const ExtValue &v, mlir::Value len = {}) { 5168 // Increment the buffer position. 5169 auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one); 5170 5171 // Grow the buffer as needed. 5172 mem = growBuffer(mem, plusOne, limit, buffSize, eleSz); 5173 5174 // Store the element in the buffer. 5175 mlir::Value buff = 5176 builder.createConvert(loc, fir::HeapType::get(resTy), mem); 5177 auto buffi = builder.create<fir::CoordinateOp>(loc, eleRefTy, buff, 5178 mlir::ValueRange{off}); 5179 fir::factory::genScalarAssignment( 5180 builder, loc, 5181 [&]() -> ExtValue { 5182 if (len) 5183 return fir::CharBoxValue(buffi, len); 5184 return buffi; 5185 }(), 5186 v); 5187 builder.create<fir::StoreOp>(loc, plusOne, buffPos); 5188 }; 5189 5190 // Copy the value. 5191 exv.match( 5192 [&](mlir::Value) { doTrivialScalar(exv); }, 5193 [&](const fir::CharBoxValue &v) { 5194 auto buffer = v.getBuffer(); 5195 if (fir::isa_char(buffer.getType())) { 5196 doTrivialScalar(exv, eleSz); 5197 } else { 5198 // Increment the buffer position. 5199 auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one); 5200 5201 // Grow the buffer as needed. 5202 mem = growBuffer(mem, plusOne, limit, buffSize, eleSz); 5203 5204 // Store the element in the buffer. 5205 mlir::Value buff = 5206 builder.createConvert(loc, fir::HeapType::get(resTy), mem); 5207 mlir::Value buffi = computeCoordinate(buff, off); 5208 llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( 5209 builder, loc, memcpyType(), buffi, v.getAddr(), eleSz, 5210 /*volatile=*/builder.createBool(loc, false)); 5211 createCallMemcpy(args); 5212 5213 builder.create<fir::StoreOp>(loc, plusOne, buffPos); 5214 } 5215 }, 5216 [&](const fir::ArrayBoxValue &v) { doAbstractArray(v); }, 5217 [&](const fir::CharArrayBoxValue &v) { doAbstractArray(v); }, 5218 [&](const auto &) { 5219 TODO(loc, "unhandled array constructor expression"); 5220 }); 5221 return mem; 5222 } 5223 5224 // Lower the expr cases in an ac-value-list. 5225 template <typename A> 5226 std::pair<ExtValue, bool> 5227 genArrayCtorInitializer(const Fortran::evaluate::Expr<A> &x, mlir::Type, 5228 mlir::Value, mlir::Value, mlir::Value, 5229 Fortran::lower::StatementContext &stmtCtx) { 5230 if (isArray(x)) 5231 return {lowerNewArrayExpression(converter, symMap, stmtCtx, toEvExpr(x)), 5232 /*needCopy=*/true}; 5233 return {asScalar(x), /*needCopy=*/true}; 5234 } 5235 5236 // Lower an ac-implied-do in an ac-value-list. 5237 template <typename A> 5238 std::pair<ExtValue, bool> 5239 genArrayCtorInitializer(const Fortran::evaluate::ImpliedDo<A> &x, 5240 mlir::Type resTy, mlir::Value mem, 5241 mlir::Value buffPos, mlir::Value buffSize, 5242 Fortran::lower::StatementContext &) { 5243 mlir::Location loc = getLoc(); 5244 mlir::IndexType idxTy = builder.getIndexType(); 5245 mlir::Value lo = 5246 builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.lower()))); 5247 mlir::Value up = 5248 builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.upper()))); 5249 mlir::Value step = 5250 builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.stride()))); 5251 auto seqTy = resTy.template cast<fir::SequenceType>(); 5252 mlir::Type eleTy = fir::unwrapSequenceType(seqTy); 5253 auto loop = 5254 builder.create<fir::DoLoopOp>(loc, lo, up, step, /*unordered=*/false, 5255 /*finalCount=*/false, mem); 5256 // create a new binding for x.name(), to ac-do-variable, to the iteration 5257 // value. 5258 symMap.pushImpliedDoBinding(toStringRef(x.name()), loop.getInductionVar()); 5259 auto insPt = builder.saveInsertionPoint(); 5260 builder.setInsertionPointToStart(loop.getBody()); 5261 // Thread mem inside the loop via loop argument. 5262 mem = loop.getRegionIterArgs()[0]; 5263 5264 mlir::Type eleRefTy = builder.getRefType(eleTy); 5265 5266 // Any temps created in the loop body must be freed inside the loop body. 5267 stmtCtx.pushScope(); 5268 llvm::Optional<mlir::Value> charLen; 5269 for (const Fortran::evaluate::ArrayConstructorValue<A> &acv : x.values()) { 5270 auto [exv, copyNeeded] = std::visit( 5271 [&](const auto &v) { 5272 return genArrayCtorInitializer(v, resTy, mem, buffPos, buffSize, 5273 stmtCtx); 5274 }, 5275 acv.u); 5276 mlir::Value eleSz = computeElementSize(exv, eleTy, resTy); 5277 mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem, 5278 eleSz, eleTy, eleRefTy, resTy) 5279 : fir::getBase(exv); 5280 if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) { 5281 charLen = builder.createTemporary(loc, builder.getI64Type()); 5282 mlir::Value castLen = 5283 builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv)); 5284 builder.create<fir::StoreOp>(loc, castLen, charLen.getValue()); 5285 } 5286 } 5287 stmtCtx.finalize(/*popScope=*/true); 5288 5289 builder.create<fir::ResultOp>(loc, mem); 5290 builder.restoreInsertionPoint(insPt); 5291 mem = loop.getResult(0); 5292 symMap.popImpliedDoBinding(); 5293 llvm::SmallVector<mlir::Value> extents = { 5294 builder.create<fir::LoadOp>(loc, buffPos).getResult()}; 5295 5296 // Convert to extended value. 5297 if (fir::isa_char(seqTy.getEleTy())) { 5298 auto len = builder.create<fir::LoadOp>(loc, charLen.getValue()); 5299 return {fir::CharArrayBoxValue{mem, len, extents}, /*needCopy=*/false}; 5300 } 5301 return {fir::ArrayBoxValue{mem, extents}, /*needCopy=*/false}; 5302 } 5303 5304 // To simplify the handling and interaction between the various cases, array 5305 // constructors are always lowered to the incremental construction code 5306 // pattern, even if the extent of the array value is constant. After the 5307 // MemToReg pass and constant folding, the optimizer should be able to 5308 // determine that all the buffer overrun tests are false when the 5309 // incremental construction wasn't actually required. 5310 template <typename A> 5311 CC genarr(const Fortran::evaluate::ArrayConstructor<A> &x) { 5312 mlir::Location loc = getLoc(); 5313 auto evExpr = toEvExpr(x); 5314 mlir::Type resTy = translateSomeExprToFIRType(converter, evExpr); 5315 mlir::IndexType idxTy = builder.getIndexType(); 5316 auto seqTy = resTy.template cast<fir::SequenceType>(); 5317 mlir::Type eleTy = fir::unwrapSequenceType(resTy); 5318 mlir::Value buffSize = builder.createTemporary(loc, idxTy, ".buff.size"); 5319 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); 5320 mlir::Value buffPos = builder.createTemporary(loc, idxTy, ".buff.pos"); 5321 builder.create<fir::StoreOp>(loc, zero, buffPos); 5322 // Allocate space for the array to be constructed. 5323 mlir::Value mem; 5324 if (fir::hasDynamicSize(resTy)) { 5325 if (fir::hasDynamicSize(eleTy)) { 5326 // The size of each element may depend on a general expression. Defer 5327 // creating the buffer until after the expression is evaluated. 5328 mem = builder.createNullConstant(loc, builder.getRefType(eleTy)); 5329 builder.create<fir::StoreOp>(loc, zero, buffSize); 5330 } else { 5331 mlir::Value initBuffSz = 5332 builder.createIntegerConstant(loc, idxTy, clInitialBufferSize); 5333 mem = builder.create<fir::AllocMemOp>( 5334 loc, eleTy, /*typeparams=*/llvm::None, initBuffSz); 5335 builder.create<fir::StoreOp>(loc, initBuffSz, buffSize); 5336 } 5337 } else { 5338 mem = builder.create<fir::AllocMemOp>(loc, resTy); 5339 int64_t buffSz = 1; 5340 for (auto extent : seqTy.getShape()) 5341 buffSz *= extent; 5342 mlir::Value initBuffSz = 5343 builder.createIntegerConstant(loc, idxTy, buffSz); 5344 builder.create<fir::StoreOp>(loc, initBuffSz, buffSize); 5345 } 5346 // Compute size of element 5347 mlir::Type eleRefTy = builder.getRefType(eleTy); 5348 5349 // Populate the buffer with the elements, growing as necessary. 5350 llvm::Optional<mlir::Value> charLen; 5351 for (const auto &expr : x) { 5352 auto [exv, copyNeeded] = std::visit( 5353 [&](const auto &e) { 5354 return genArrayCtorInitializer(e, resTy, mem, buffPos, buffSize, 5355 stmtCtx); 5356 }, 5357 expr.u); 5358 mlir::Value eleSz = computeElementSize(exv, eleTy, resTy); 5359 mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem, 5360 eleSz, eleTy, eleRefTy, resTy) 5361 : fir::getBase(exv); 5362 if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) { 5363 charLen = builder.createTemporary(loc, builder.getI64Type()); 5364 mlir::Value castLen = 5365 builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv)); 5366 builder.create<fir::StoreOp>(loc, castLen, charLen.getValue()); 5367 } 5368 } 5369 mem = builder.createConvert(loc, fir::HeapType::get(resTy), mem); 5370 llvm::SmallVector<mlir::Value> extents = { 5371 builder.create<fir::LoadOp>(loc, buffPos)}; 5372 5373 // Cleanup the temporary. 5374 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 5375 stmtCtx.attachCleanup( 5376 [bldr, loc, mem]() { bldr->create<fir::FreeMemOp>(loc, mem); }); 5377 5378 // Return the continuation. 5379 if (fir::isa_char(seqTy.getEleTy())) { 5380 if (charLen.hasValue()) { 5381 auto len = builder.create<fir::LoadOp>(loc, charLen.getValue()); 5382 return genarr(fir::CharArrayBoxValue{mem, len, extents}); 5383 } 5384 return genarr(fir::CharArrayBoxValue{mem, zero, extents}); 5385 } 5386 return genarr(fir::ArrayBoxValue{mem, extents}); 5387 } 5388 5389 CC genarr(const Fortran::evaluate::ImpliedDoIndex &) { 5390 TODO(getLoc(), "genarr ImpliedDoIndex"); 5391 } 5392 5393 CC genarr(const Fortran::evaluate::TypeParamInquiry &x) { 5394 TODO(getLoc(), "genarr TypeParamInquiry"); 5395 } 5396 5397 CC genarr(const Fortran::evaluate::DescriptorInquiry &x) { 5398 TODO(getLoc(), "genarr DescriptorInquiry"); 5399 } 5400 5401 CC genarr(const Fortran::evaluate::StructureConstructor &x) { 5402 TODO(getLoc(), "genarr StructureConstructor"); 5403 } 5404 5405 //===--------------------------------------------------------------------===// 5406 // LOCICAL operators (.NOT., .AND., .EQV., etc.) 5407 //===--------------------------------------------------------------------===// 5408 5409 template <int KIND> 5410 CC genarr(const Fortran::evaluate::Not<KIND> &x) { 5411 mlir::Location loc = getLoc(); 5412 mlir::IntegerType i1Ty = builder.getI1Type(); 5413 auto lambda = genarr(x.left()); 5414 mlir::Value truth = builder.createBool(loc, true); 5415 return [=](IterSpace iters) -> ExtValue { 5416 mlir::Value logical = fir::getBase(lambda(iters)); 5417 mlir::Value val = builder.createConvert(loc, i1Ty, logical); 5418 return builder.create<mlir::arith::XOrIOp>(loc, val, truth); 5419 }; 5420 } 5421 template <typename OP, typename A> 5422 CC createBinaryBoolOp(const A &x) { 5423 mlir::Location loc = getLoc(); 5424 mlir::IntegerType i1Ty = builder.getI1Type(); 5425 auto lf = genarr(x.left()); 5426 auto rf = genarr(x.right()); 5427 return [=](IterSpace iters) -> ExtValue { 5428 mlir::Value left = fir::getBase(lf(iters)); 5429 mlir::Value right = fir::getBase(rf(iters)); 5430 mlir::Value lhs = builder.createConvert(loc, i1Ty, left); 5431 mlir::Value rhs = builder.createConvert(loc, i1Ty, right); 5432 return builder.create<OP>(loc, lhs, rhs); 5433 }; 5434 } 5435 template <typename OP, typename A> 5436 CC createCompareBoolOp(mlir::arith::CmpIPredicate pred, const A &x) { 5437 mlir::Location loc = getLoc(); 5438 mlir::IntegerType i1Ty = builder.getI1Type(); 5439 auto lf = genarr(x.left()); 5440 auto rf = genarr(x.right()); 5441 return [=](IterSpace iters) -> ExtValue { 5442 mlir::Value left = fir::getBase(lf(iters)); 5443 mlir::Value right = fir::getBase(rf(iters)); 5444 mlir::Value lhs = builder.createConvert(loc, i1Ty, left); 5445 mlir::Value rhs = builder.createConvert(loc, i1Ty, right); 5446 return builder.create<OP>(loc, pred, lhs, rhs); 5447 }; 5448 } 5449 template <int KIND> 5450 CC genarr(const Fortran::evaluate::LogicalOperation<KIND> &x) { 5451 switch (x.logicalOperator) { 5452 case Fortran::evaluate::LogicalOperator::And: 5453 return createBinaryBoolOp<mlir::arith::AndIOp>(x); 5454 case Fortran::evaluate::LogicalOperator::Or: 5455 return createBinaryBoolOp<mlir::arith::OrIOp>(x); 5456 case Fortran::evaluate::LogicalOperator::Eqv: 5457 return createCompareBoolOp<mlir::arith::CmpIOp>( 5458 mlir::arith::CmpIPredicate::eq, x); 5459 case Fortran::evaluate::LogicalOperator::Neqv: 5460 return createCompareBoolOp<mlir::arith::CmpIOp>( 5461 mlir::arith::CmpIPredicate::ne, x); 5462 case Fortran::evaluate::LogicalOperator::Not: 5463 llvm_unreachable(".NOT. handled elsewhere"); 5464 } 5465 llvm_unreachable("unhandled case"); 5466 } 5467 5468 //===--------------------------------------------------------------------===// 5469 // Relational operators (<, <=, ==, etc.) 5470 //===--------------------------------------------------------------------===// 5471 5472 template <typename OP, typename PRED, typename A> 5473 CC createCompareOp(PRED pred, const A &x) { 5474 mlir::Location loc = getLoc(); 5475 auto lf = genarr(x.left()); 5476 auto rf = genarr(x.right()); 5477 return [=](IterSpace iters) -> ExtValue { 5478 mlir::Value lhs = fir::getBase(lf(iters)); 5479 mlir::Value rhs = fir::getBase(rf(iters)); 5480 return builder.create<OP>(loc, pred, lhs, rhs); 5481 }; 5482 } 5483 template <typename A> 5484 CC createCompareCharOp(mlir::arith::CmpIPredicate pred, const A &x) { 5485 mlir::Location loc = getLoc(); 5486 auto lf = genarr(x.left()); 5487 auto rf = genarr(x.right()); 5488 return [=](IterSpace iters) -> ExtValue { 5489 auto lhs = lf(iters); 5490 auto rhs = rf(iters); 5491 return fir::runtime::genCharCompare(builder, loc, pred, lhs, rhs); 5492 }; 5493 } 5494 template <int KIND> 5495 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 5496 Fortran::common::TypeCategory::Integer, KIND>> &x) { 5497 return createCompareOp<mlir::arith::CmpIOp>(translateRelational(x.opr), x); 5498 } 5499 template <int KIND> 5500 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 5501 Fortran::common::TypeCategory::Character, KIND>> &x) { 5502 return createCompareCharOp(translateRelational(x.opr), x); 5503 } 5504 template <int KIND> 5505 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 5506 Fortran::common::TypeCategory::Real, KIND>> &x) { 5507 return createCompareOp<mlir::arith::CmpFOp>(translateFloatRelational(x.opr), 5508 x); 5509 } 5510 template <int KIND> 5511 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 5512 Fortran::common::TypeCategory::Complex, KIND>> &x) { 5513 return createCompareOp<fir::CmpcOp>(translateFloatRelational(x.opr), x); 5514 } 5515 CC genarr( 5516 const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) { 5517 return std::visit([&](const auto &x) { return genarr(x); }, r.u); 5518 } 5519 5520 template <typename A> 5521 CC genarr(const Fortran::evaluate::Designator<A> &des) { 5522 ComponentPath components(des.Rank() > 0); 5523 return std::visit([&](const auto &x) { return genarr(x, components); }, 5524 des.u); 5525 } 5526 5527 template <typename T> 5528 CC genarr(const Fortran::evaluate::FunctionRef<T> &funRef) { 5529 // Note that it's possible that the function being called returns either an 5530 // array or a scalar. In the first case, use the element type of the array. 5531 return genProcRef( 5532 funRef, fir::unwrapSequenceType(converter.genType(toEvExpr(funRef)))); 5533 } 5534 5535 //===-------------------------------------------------------------------===// 5536 // Array data references in an explicit iteration space. 5537 // 5538 // Use the base array that was loaded before the loop nest. 5539 //===-------------------------------------------------------------------===// 5540 5541 /// Lower the path (`revPath`, in reverse) to be appended to an array_fetch or 5542 /// array_update op. \p ty is the initial type of the array 5543 /// (reference). Returns the type of the element after application of the 5544 /// path in \p components. 5545 /// 5546 /// TODO: This needs to deal with array's with initial bounds other than 1. 5547 /// TODO: Thread type parameters correctly. 5548 mlir::Type lowerPath(const ExtValue &arrayExv, ComponentPath &components) { 5549 mlir::Location loc = getLoc(); 5550 mlir::Type ty = fir::getBase(arrayExv).getType(); 5551 auto &revPath = components.reversePath; 5552 ty = fir::unwrapPassByRefType(ty); 5553 bool prefix = true; 5554 auto addComponent = [&](mlir::Value v) { 5555 if (prefix) 5556 components.prefixComponents.push_back(v); 5557 else 5558 components.suffixComponents.push_back(v); 5559 }; 5560 mlir::IndexType idxTy = builder.getIndexType(); 5561 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); 5562 bool atBase = true; 5563 auto saveSemant = semant; 5564 if (isProjectedCopyInCopyOut()) 5565 semant = ConstituentSemantics::RefTransparent; 5566 for (const auto &v : llvm::reverse(revPath)) { 5567 std::visit( 5568 Fortran::common::visitors{ 5569 [&](const ImplicitSubscripts &) { 5570 prefix = false; 5571 ty = fir::unwrapSequenceType(ty); 5572 }, 5573 [&](const Fortran::evaluate::ComplexPart *x) { 5574 assert(!prefix && "complex part must be at end"); 5575 mlir::Value offset = builder.createIntegerConstant( 5576 loc, builder.getI32Type(), 5577 x->part() == Fortran::evaluate::ComplexPart::Part::RE ? 0 5578 : 1); 5579 components.suffixComponents.push_back(offset); 5580 ty = fir::applyPathToType(ty, mlir::ValueRange{offset}); 5581 }, 5582 [&](const Fortran::evaluate::ArrayRef *x) { 5583 if (Fortran::lower::isRankedArrayAccess(*x)) { 5584 genSliceIndices(components, arrayExv, *x, atBase); 5585 } else { 5586 // Array access where the expressions are scalar and cannot 5587 // depend upon the implied iteration space. 5588 unsigned ssIndex = 0u; 5589 for (const auto &ss : x->subscript()) { 5590 std::visit( 5591 Fortran::common::visitors{ 5592 [&](const Fortran::evaluate:: 5593 IndirectSubscriptIntegerExpr &ie) { 5594 const auto &e = ie.value(); 5595 if (isArray(e)) 5596 fir::emitFatalError( 5597 loc, 5598 "multiple components along single path " 5599 "generating array subexpressions"); 5600 // Lower scalar index expression, append it to 5601 // subs. 5602 mlir::Value subscriptVal = 5603 fir::getBase(asScalarArray(e)); 5604 // arrayExv is the base array. It needs to reflect 5605 // the current array component instead. 5606 // FIXME: must use lower bound of this component, 5607 // not just the constant 1. 5608 mlir::Value lb = 5609 atBase ? fir::factory::readLowerBound( 5610 builder, loc, arrayExv, ssIndex, 5611 one) 5612 : one; 5613 mlir::Value val = builder.createConvert( 5614 loc, idxTy, subscriptVal); 5615 mlir::Value ivAdj = 5616 builder.create<mlir::arith::SubIOp>( 5617 loc, idxTy, val, lb); 5618 addComponent( 5619 builder.createConvert(loc, idxTy, ivAdj)); 5620 }, 5621 [&](const auto &) { 5622 fir::emitFatalError( 5623 loc, "multiple components along single path " 5624 "generating array subexpressions"); 5625 }}, 5626 ss.u); 5627 ssIndex++; 5628 } 5629 } 5630 ty = fir::unwrapSequenceType(ty); 5631 }, 5632 [&](const Fortran::evaluate::Component *x) { 5633 auto fieldTy = fir::FieldType::get(builder.getContext()); 5634 llvm::StringRef name = toStringRef(getLastSym(*x).name()); 5635 auto recTy = ty.cast<fir::RecordType>(); 5636 ty = recTy.getType(name); 5637 auto fld = builder.create<fir::FieldIndexOp>( 5638 loc, fieldTy, name, recTy, fir::getTypeParams(arrayExv)); 5639 addComponent(fld); 5640 }}, 5641 v); 5642 atBase = false; 5643 } 5644 semant = saveSemant; 5645 ty = fir::unwrapSequenceType(ty); 5646 components.applied = true; 5647 return ty; 5648 } 5649 5650 llvm::SmallVector<mlir::Value> genSubstringBounds(ComponentPath &components) { 5651 llvm::SmallVector<mlir::Value> result; 5652 if (components.substring) 5653 populateBounds(result, components.substring); 5654 return result; 5655 } 5656 5657 CC applyPathToArrayLoad(fir::ArrayLoadOp load, ComponentPath &components) { 5658 mlir::Location loc = getLoc(); 5659 auto revPath = components.reversePath; 5660 fir::ExtendedValue arrayExv = 5661 arrayLoadExtValue(builder, loc, load, {}, load); 5662 mlir::Type eleTy = lowerPath(arrayExv, components); 5663 auto currentPC = components.pc; 5664 auto pc = [=, prefix = components.prefixComponents, 5665 suffix = components.suffixComponents](IterSpace iters) { 5666 IterationSpace newIters = currentPC(iters); 5667 // Add path prefix and suffix. 5668 IterationSpace addIters(newIters, prefix, suffix); 5669 return addIters; 5670 }; 5671 components.pc = [=](IterSpace iters) { return iters; }; 5672 llvm::SmallVector<mlir::Value> substringBounds = 5673 genSubstringBounds(components); 5674 if (isProjectedCopyInCopyOut()) { 5675 destination = load; 5676 auto lambda = [=, esp = this->explicitSpace](IterSpace iters) mutable { 5677 mlir::Value innerArg = esp->findArgumentOfLoad(load); 5678 if (isAdjustedArrayElementType(eleTy)) { 5679 mlir::Type eleRefTy = builder.getRefType(eleTy); 5680 auto arrayOp = builder.create<fir::ArrayAccessOp>( 5681 loc, eleRefTy, innerArg, iters.iterVec(), load.getTypeparams()); 5682 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 5683 mlir::Value dstLen = fir::factory::genLenOfCharacter( 5684 builder, loc, load, iters.iterVec(), substringBounds); 5685 fir::ArrayAmendOp amend = createCharArrayAmend( 5686 loc, builder, arrayOp, dstLen, iters.elementExv(), innerArg, 5687 substringBounds); 5688 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), amend, 5689 dstLen); 5690 } else if (fir::isa_derived(eleTy)) { 5691 fir::ArrayAmendOp amend = 5692 createDerivedArrayAmend(loc, load, builder, arrayOp, 5693 iters.elementExv(), eleTy, innerArg); 5694 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), 5695 amend); 5696 } 5697 assert(eleTy.isa<fir::SequenceType>()); 5698 TODO(loc, "array (as element) assignment"); 5699 } 5700 mlir::Value castedElement = 5701 builder.createConvert(loc, eleTy, iters.getElement()); 5702 auto update = builder.create<fir::ArrayUpdateOp>( 5703 loc, innerArg.getType(), innerArg, castedElement, iters.iterVec(), 5704 load.getTypeparams()); 5705 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), update); 5706 }; 5707 return [=](IterSpace iters) mutable { return lambda(pc(iters)); }; 5708 } 5709 if (isCustomCopyInCopyOut()) { 5710 // Create an array_modify to get the LHS element address and indicate 5711 // the assignment, and create the call to the user defined assignment. 5712 destination = load; 5713 auto lambda = [=](IterSpace iters) mutable { 5714 mlir::Value innerArg = explicitSpace->findArgumentOfLoad(load); 5715 mlir::Type refEleTy = 5716 fir::isa_ref_type(eleTy) ? eleTy : builder.getRefType(eleTy); 5717 auto arrModify = builder.create<fir::ArrayModifyOp>( 5718 loc, mlir::TypeRange{refEleTy, innerArg.getType()}, innerArg, 5719 iters.iterVec(), load.getTypeparams()); 5720 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), 5721 arrModify.getResult(1)); 5722 }; 5723 return [=](IterSpace iters) mutable { return lambda(pc(iters)); }; 5724 } 5725 auto lambda = [=, semant = this->semant](IterSpace iters) mutable { 5726 if (semant == ConstituentSemantics::RefOpaque || 5727 isAdjustedArrayElementType(eleTy)) { 5728 mlir::Type resTy = builder.getRefType(eleTy); 5729 // Use array element reference semantics. 5730 auto access = builder.create<fir::ArrayAccessOp>( 5731 loc, resTy, load, iters.iterVec(), load.getTypeparams()); 5732 mlir::Value newBase = access; 5733 if (fir::isa_char(eleTy)) { 5734 mlir::Value dstLen = fir::factory::genLenOfCharacter( 5735 builder, loc, load, iters.iterVec(), substringBounds); 5736 if (!substringBounds.empty()) { 5737 fir::CharBoxValue charDst{access, dstLen}; 5738 fir::factory::CharacterExprHelper helper{builder, loc}; 5739 charDst = helper.createSubstring(charDst, substringBounds); 5740 newBase = charDst.getAddr(); 5741 } 5742 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), newBase, 5743 dstLen); 5744 } 5745 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), newBase); 5746 } 5747 auto fetch = builder.create<fir::ArrayFetchOp>( 5748 loc, eleTy, load, iters.iterVec(), load.getTypeparams()); 5749 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), fetch); 5750 }; 5751 return [=](IterSpace iters) mutable { 5752 auto newIters = pc(iters); 5753 return lambda(newIters); 5754 }; 5755 } 5756 5757 template <typename A> 5758 CC genImplicitArrayAccess(const A &x, ComponentPath &components) { 5759 components.reversePath.push_back(ImplicitSubscripts{}); 5760 ExtValue exv = asScalarRef(x); 5761 lowerPath(exv, components); 5762 auto lambda = genarr(exv, components); 5763 return [=](IterSpace iters) { return lambda(components.pc(iters)); }; 5764 } 5765 CC genImplicitArrayAccess(const Fortran::evaluate::NamedEntity &x, 5766 ComponentPath &components) { 5767 if (x.IsSymbol()) 5768 return genImplicitArrayAccess(x.GetFirstSymbol(), components); 5769 return genImplicitArrayAccess(x.GetComponent(), components); 5770 } 5771 5772 template <typename A> 5773 CC genAsScalar(const A &x) { 5774 mlir::Location loc = getLoc(); 5775 if (isProjectedCopyInCopyOut()) { 5776 return [=, &x, builder = &converter.getFirOpBuilder()]( 5777 IterSpace iters) -> ExtValue { 5778 ExtValue exv = asScalarRef(x); 5779 mlir::Value val = fir::getBase(exv); 5780 mlir::Type eleTy = fir::unwrapRefType(val.getType()); 5781 if (isAdjustedArrayElementType(eleTy)) { 5782 if (fir::isa_char(eleTy)) { 5783 TODO(getLoc(), "assignment of character type"); 5784 } else if (fir::isa_derived(eleTy)) { 5785 TODO(loc, "assignment of derived type"); 5786 } else { 5787 fir::emitFatalError(loc, "array type not expected in scalar"); 5788 } 5789 } else { 5790 builder->create<fir::StoreOp>(loc, iters.getElement(), val); 5791 } 5792 return exv; 5793 }; 5794 } 5795 return [=, &x](IterSpace) { return asScalar(x); }; 5796 } 5797 5798 CC genarr(const Fortran::semantics::Symbol &x, ComponentPath &components) { 5799 if (explicitSpaceIsActive()) { 5800 if (x.Rank() > 0) 5801 components.reversePath.push_back(ImplicitSubscripts{}); 5802 if (fir::ArrayLoadOp load = explicitSpace->findBinding(&x)) 5803 return applyPathToArrayLoad(load, components); 5804 } else { 5805 return genImplicitArrayAccess(x, components); 5806 } 5807 if (pathIsEmpty(components)) 5808 return genAsScalar(x); 5809 mlir::Location loc = getLoc(); 5810 return [=](IterSpace) -> ExtValue { 5811 fir::emitFatalError(loc, "reached symbol with path"); 5812 }; 5813 } 5814 5815 /// Lower a component path with or without rank. 5816 /// Example: <code>array%baz%qux%waldo</code> 5817 CC genarr(const Fortran::evaluate::Component &x, ComponentPath &components) { 5818 if (explicitSpaceIsActive()) { 5819 if (x.base().Rank() == 0 && x.Rank() > 0) 5820 components.reversePath.push_back(ImplicitSubscripts{}); 5821 if (fir::ArrayLoadOp load = explicitSpace->findBinding(&x)) 5822 return applyPathToArrayLoad(load, components); 5823 } else { 5824 if (x.base().Rank() == 0) 5825 return genImplicitArrayAccess(x, components); 5826 } 5827 bool atEnd = pathIsEmpty(components); 5828 if (!getLastSym(x).test(Fortran::semantics::Symbol::Flag::ParentComp)) 5829 // Skip parent components; their components are placed directly in the 5830 // object. 5831 components.reversePath.push_back(&x); 5832 auto result = genarr(x.base(), components); 5833 if (components.applied) 5834 return result; 5835 if (atEnd) 5836 return genAsScalar(x); 5837 mlir::Location loc = getLoc(); 5838 return [=](IterSpace) -> ExtValue { 5839 fir::emitFatalError(loc, "reached component with path"); 5840 }; 5841 } 5842 5843 /// Array reference with subscripts. If this has rank > 0, this is a form 5844 /// of an array section (slice). 5845 /// 5846 /// There are two "slicing" primitives that may be applied on a dimension by 5847 /// dimension basis: (1) triple notation and (2) vector addressing. Since 5848 /// dimensions can be selectively sliced, some dimensions may contain 5849 /// regular scalar expressions and those dimensions do not participate in 5850 /// the array expression evaluation. 5851 CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) { 5852 if (explicitSpaceIsActive()) { 5853 if (Fortran::lower::isRankedArrayAccess(x)) 5854 components.reversePath.push_back(ImplicitSubscripts{}); 5855 if (fir::ArrayLoadOp load = explicitSpace->findBinding(&x)) { 5856 components.reversePath.push_back(&x); 5857 return applyPathToArrayLoad(load, components); 5858 } 5859 } else { 5860 if (Fortran::lower::isRankedArrayAccess(x)) { 5861 components.reversePath.push_back(&x); 5862 return genImplicitArrayAccess(x.base(), components); 5863 } 5864 } 5865 bool atEnd = pathIsEmpty(components); 5866 components.reversePath.push_back(&x); 5867 auto result = genarr(x.base(), components); 5868 if (components.applied) 5869 return result; 5870 mlir::Location loc = getLoc(); 5871 if (atEnd) { 5872 if (x.Rank() == 0) 5873 return genAsScalar(x); 5874 fir::emitFatalError(loc, "expected scalar"); 5875 } 5876 return [=](IterSpace) -> ExtValue { 5877 fir::emitFatalError(loc, "reached arrayref with path"); 5878 }; 5879 } 5880 5881 CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) { 5882 TODO(getLoc(), "coarray reference"); 5883 } 5884 5885 CC genarr(const Fortran::evaluate::NamedEntity &x, 5886 ComponentPath &components) { 5887 return x.IsSymbol() ? genarr(x.GetFirstSymbol(), components) 5888 : genarr(x.GetComponent(), components); 5889 } 5890 5891 CC genarr(const Fortran::evaluate::DataRef &x, ComponentPath &components) { 5892 return std::visit([&](const auto &v) { return genarr(v, components); }, 5893 x.u); 5894 } 5895 5896 bool pathIsEmpty(const ComponentPath &components) { 5897 return components.reversePath.empty(); 5898 } 5899 5900 /// Given an optional fir.box, returns an fir.box that is the original one if 5901 /// it is present and it otherwise an unallocated box. 5902 /// Absent fir.box are implemented as a null pointer descriptor. Generated 5903 /// code may need to unconditionally read a fir.box that can be absent. 5904 /// This helper allows creating a fir.box that can be read in all cases 5905 /// outside of a fir.if (isPresent) region. However, the usages of the value 5906 /// read from such box should still only be done in a fir.if(isPresent). 5907 static fir::ExtendedValue 5908 absentBoxToUnalllocatedBox(fir::FirOpBuilder &builder, mlir::Location loc, 5909 const fir::ExtendedValue &exv, 5910 mlir::Value isPresent) { 5911 mlir::Value box = fir::getBase(exv); 5912 mlir::Type boxType = box.getType(); 5913 assert(boxType.isa<fir::BoxType>() && "argument must be a fir.box"); 5914 mlir::Value emptyBox = 5915 fir::factory::createUnallocatedBox(builder, loc, boxType, llvm::None); 5916 auto safeToReadBox = 5917 builder.create<mlir::arith::SelectOp>(loc, isPresent, box, emptyBox); 5918 return fir::substBase(exv, safeToReadBox); 5919 } 5920 5921 std::tuple<CC, mlir::Value, mlir::Type> 5922 genOptionalArrayFetch(const Fortran::lower::SomeExpr &expr) { 5923 assert(expr.Rank() > 0 && "expr must be an array"); 5924 mlir::Location loc = getLoc(); 5925 ExtValue optionalArg = asInquired(expr); 5926 mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg); 5927 // Generate an array load and access to an array that may be an absent 5928 // optional or an unallocated optional. 5929 mlir::Value base = getBase(optionalArg); 5930 const bool hasOptionalAttr = 5931 fir::valueHasFirAttribute(base, fir::getOptionalAttrName()); 5932 mlir::Type baseType = fir::unwrapRefType(base.getType()); 5933 const bool isBox = baseType.isa<fir::BoxType>(); 5934 const bool isAllocOrPtr = Fortran::evaluate::IsAllocatableOrPointerObject( 5935 expr, converter.getFoldingContext()); 5936 mlir::Type arrType = fir::unwrapPassByRefType(baseType); 5937 mlir::Type eleType = fir::unwrapSequenceType(arrType); 5938 ExtValue exv = optionalArg; 5939 if (hasOptionalAttr && isBox && !isAllocOrPtr) { 5940 // Elemental argument cannot be allocatable or pointers (C15100). 5941 // Hence, per 15.5.2.12 3 (8) and (9), the provided Allocatable and 5942 // Pointer optional arrays cannot be absent. The only kind of entities 5943 // that can get here are optional assumed shape and polymorphic entities. 5944 exv = absentBoxToUnalllocatedBox(builder, loc, exv, isPresent); 5945 } 5946 // All the properties can be read from any fir.box but the read values may 5947 // be undefined and should only be used inside a fir.if (canBeRead) region. 5948 if (const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>()) 5949 exv = fir::factory::genMutableBoxRead(builder, loc, *mutableBox); 5950 5951 mlir::Value memref = fir::getBase(exv); 5952 mlir::Value shape = builder.createShape(loc, exv); 5953 mlir::Value noSlice; 5954 auto arrLoad = builder.create<fir::ArrayLoadOp>( 5955 loc, arrType, memref, shape, noSlice, fir::getTypeParams(exv)); 5956 mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams(); 5957 mlir::Value arrLd = arrLoad.getResult(); 5958 // Mark the load to tell later passes it is unsafe to use this array_load 5959 // shape unconditionally. 5960 arrLoad->setAttr(fir::getOptionalAttrName(), builder.getUnitAttr()); 5961 5962 // Place the array as optional on the arrayOperands stack so that its 5963 // shape will only be used as a fallback to induce the implicit loop nest 5964 // (that is if there is no non optional array arguments). 5965 arrayOperands.push_back( 5966 ArrayOperand{memref, shape, noSlice, /*mayBeAbsent=*/true}); 5967 5968 // By value semantics. 5969 auto cc = [=](IterSpace iters) -> ExtValue { 5970 auto arrFetch = builder.create<fir::ArrayFetchOp>( 5971 loc, eleType, arrLd, iters.iterVec(), arrLdTypeParams); 5972 return fir::factory::arraySectionElementToExtendedValue( 5973 builder, loc, exv, arrFetch, noSlice); 5974 }; 5975 return {cc, isPresent, eleType}; 5976 } 5977 5978 /// Generate a continuation to pass \p expr to an OPTIONAL argument of an 5979 /// elemental procedure. This is meant to handle the cases where \p expr might 5980 /// be dynamically absent (i.e. when it is a POINTER, an ALLOCATABLE or an 5981 /// OPTIONAL variable). If p\ expr is guaranteed to be present genarr() can 5982 /// directly be called instead. 5983 CC genarrForwardOptionalArgumentToCall(const Fortran::lower::SomeExpr &expr) { 5984 mlir::Location loc = getLoc(); 5985 // Only by-value numerical and logical so far. 5986 if (semant != ConstituentSemantics::RefTransparent) 5987 TODO(loc, "optional arguments in user defined elemental procedures"); 5988 5989 // Handle scalar argument case (the if-then-else is generated outside of the 5990 // implicit loop nest). 5991 if (expr.Rank() == 0) { 5992 ExtValue optionalArg = asInquired(expr); 5993 mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg); 5994 mlir::Value elementValue = 5995 fir::getBase(genOptionalValue(builder, loc, optionalArg, isPresent)); 5996 return [=](IterSpace iters) -> ExtValue { return elementValue; }; 5997 } 5998 5999 CC cc; 6000 mlir::Value isPresent; 6001 mlir::Type eleType; 6002 std::tie(cc, isPresent, eleType) = genOptionalArrayFetch(expr); 6003 return [=](IterSpace iters) -> ExtValue { 6004 mlir::Value elementValue = 6005 builder 6006 .genIfOp(loc, {eleType}, isPresent, 6007 /*withElseRegion=*/true) 6008 .genThen([&]() { 6009 builder.create<fir::ResultOp>(loc, fir::getBase(cc(iters))); 6010 }) 6011 .genElse([&]() { 6012 mlir::Value zero = 6013 fir::factory::createZeroValue(builder, loc, eleType); 6014 builder.create<fir::ResultOp>(loc, zero); 6015 }) 6016 .getResults()[0]; 6017 return elementValue; 6018 }; 6019 } 6020 6021 /// Reduce the rank of a array to be boxed based on the slice's operands. 6022 static mlir::Type reduceRank(mlir::Type arrTy, mlir::Value slice) { 6023 if (slice) { 6024 auto slOp = mlir::dyn_cast<fir::SliceOp>(slice.getDefiningOp()); 6025 assert(slOp && "expected slice op"); 6026 auto seqTy = arrTy.dyn_cast<fir::SequenceType>(); 6027 assert(seqTy && "expected array type"); 6028 mlir::Operation::operand_range triples = slOp.getTriples(); 6029 fir::SequenceType::Shape shape; 6030 // reduce the rank for each invariant dimension 6031 for (unsigned i = 1, end = triples.size(); i < end; i += 3) 6032 if (!mlir::isa_and_nonnull<fir::UndefOp>(triples[i].getDefiningOp())) 6033 shape.push_back(fir::SequenceType::getUnknownExtent()); 6034 return fir::SequenceType::get(shape, seqTy.getEleTy()); 6035 } 6036 // not sliced, so no change in rank 6037 return arrTy; 6038 } 6039 6040 CC genarr(const Fortran::evaluate::ComplexPart &x, 6041 ComponentPath &components) { 6042 components.reversePath.push_back(&x); 6043 return genarr(x.complex(), components); 6044 } 6045 6046 CC genarr(const Fortran::evaluate::StaticDataObject::Pointer &, 6047 ComponentPath &components) { 6048 TODO(getLoc(), "genarr StaticDataObject::Pointer"); 6049 } 6050 6051 /// Substrings (see 9.4.1) 6052 CC genarr(const Fortran::evaluate::Substring &x, ComponentPath &components) { 6053 components.substring = &x; 6054 return std::visit([&](const auto &v) { return genarr(v, components); }, 6055 x.parent()); 6056 } 6057 6058 /// Base case of generating an array reference, 6059 CC genarr(const ExtValue &extMemref, ComponentPath &components) { 6060 mlir::Location loc = getLoc(); 6061 mlir::Value memref = fir::getBase(extMemref); 6062 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(memref.getType()); 6063 assert(arrTy.isa<fir::SequenceType>() && "memory ref must be an array"); 6064 mlir::Value shape = builder.createShape(loc, extMemref); 6065 mlir::Value slice; 6066 if (components.isSlice()) { 6067 if (isBoxValue() && components.substring) { 6068 // Append the substring operator to emboxing Op as it will become an 6069 // interior adjustment (add offset, adjust LEN) to the CHARACTER value 6070 // being referenced in the descriptor. 6071 llvm::SmallVector<mlir::Value> substringBounds; 6072 populateBounds(substringBounds, components.substring); 6073 // Convert to (offset, size) 6074 mlir::Type iTy = substringBounds[0].getType(); 6075 if (substringBounds.size() != 2) { 6076 fir::CharacterType charTy = 6077 fir::factory::CharacterExprHelper::getCharType(arrTy); 6078 if (charTy.hasConstantLen()) { 6079 mlir::IndexType idxTy = builder.getIndexType(); 6080 fir::CharacterType::LenType charLen = charTy.getLen(); 6081 mlir::Value lenValue = 6082 builder.createIntegerConstant(loc, idxTy, charLen); 6083 substringBounds.push_back(lenValue); 6084 } else { 6085 llvm::SmallVector<mlir::Value> typeparams = 6086 fir::getTypeParams(extMemref); 6087 substringBounds.push_back(typeparams.back()); 6088 } 6089 } 6090 // Convert the lower bound to 0-based substring. 6091 mlir::Value one = 6092 builder.createIntegerConstant(loc, substringBounds[0].getType(), 1); 6093 substringBounds[0] = 6094 builder.create<mlir::arith::SubIOp>(loc, substringBounds[0], one); 6095 // Convert the upper bound to a length. 6096 mlir::Value cast = builder.createConvert(loc, iTy, substringBounds[1]); 6097 mlir::Value zero = builder.createIntegerConstant(loc, iTy, 0); 6098 auto size = 6099 builder.create<mlir::arith::SubIOp>(loc, cast, substringBounds[0]); 6100 auto cmp = builder.create<mlir::arith::CmpIOp>( 6101 loc, mlir::arith::CmpIPredicate::sgt, size, zero); 6102 // size = MAX(upper - (lower - 1), 0) 6103 substringBounds[1] = 6104 builder.create<mlir::arith::SelectOp>(loc, cmp, size, zero); 6105 slice = builder.create<fir::SliceOp>(loc, components.trips, 6106 components.suffixComponents, 6107 substringBounds); 6108 } else { 6109 slice = builder.createSlice(loc, extMemref, components.trips, 6110 components.suffixComponents); 6111 } 6112 if (components.hasComponents()) { 6113 auto seqTy = arrTy.cast<fir::SequenceType>(); 6114 mlir::Type eleTy = 6115 fir::applyPathToType(seqTy.getEleTy(), components.suffixComponents); 6116 if (!eleTy) 6117 fir::emitFatalError(loc, "slicing path is ill-formed"); 6118 if (auto realTy = eleTy.dyn_cast<fir::RealType>()) 6119 eleTy = Fortran::lower::convertReal(realTy.getContext(), 6120 realTy.getFKind()); 6121 6122 // create the type of the projected array. 6123 arrTy = fir::SequenceType::get(seqTy.getShape(), eleTy); 6124 LLVM_DEBUG(llvm::dbgs() 6125 << "type of array projection from component slicing: " 6126 << eleTy << ", " << arrTy << '\n'); 6127 } 6128 } 6129 arrayOperands.push_back(ArrayOperand{memref, shape, slice}); 6130 if (destShape.empty()) 6131 destShape = getShape(arrayOperands.back()); 6132 if (isBoxValue()) { 6133 // Semantics are a reference to a boxed array. 6134 // This case just requires that an embox operation be created to box the 6135 // value. The value of the box is forwarded in the continuation. 6136 mlir::Type reduceTy = reduceRank(arrTy, slice); 6137 auto boxTy = fir::BoxType::get(reduceTy); 6138 if (components.substring) { 6139 // Adjust char length to substring size. 6140 fir::CharacterType charTy = 6141 fir::factory::CharacterExprHelper::getCharType(reduceTy); 6142 auto seqTy = reduceTy.cast<fir::SequenceType>(); 6143 // TODO: Use a constant for fir.char LEN if we can compute it. 6144 boxTy = fir::BoxType::get( 6145 fir::SequenceType::get(fir::CharacterType::getUnknownLen( 6146 builder.getContext(), charTy.getFKind()), 6147 seqTy.getDimension())); 6148 } 6149 mlir::Value embox = 6150 memref.getType().isa<fir::BoxType>() 6151 ? builder.create<fir::ReboxOp>(loc, boxTy, memref, shape, slice) 6152 .getResult() 6153 : builder 6154 .create<fir::EmboxOp>(loc, boxTy, memref, shape, slice, 6155 fir::getTypeParams(extMemref)) 6156 .getResult(); 6157 return [=](IterSpace) -> ExtValue { return fir::BoxValue(embox); }; 6158 } 6159 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy(); 6160 if (isReferentiallyOpaque()) { 6161 // Semantics are an opaque reference to an array. 6162 // This case forwards a continuation that will generate the address 6163 // arithmetic to the array element. This does not have copy-in/copy-out 6164 // semantics. No attempt to copy the array value will be made during the 6165 // interpretation of the Fortran statement. 6166 mlir::Type refEleTy = builder.getRefType(eleTy); 6167 return [=](IterSpace iters) -> ExtValue { 6168 // ArrayCoorOp does not expect zero based indices. 6169 llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices( 6170 loc, builder, memref.getType(), shape, iters.iterVec()); 6171 mlir::Value coor = builder.create<fir::ArrayCoorOp>( 6172 loc, refEleTy, memref, shape, slice, indices, 6173 fir::getTypeParams(extMemref)); 6174 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 6175 llvm::SmallVector<mlir::Value> substringBounds; 6176 populateBounds(substringBounds, components.substring); 6177 if (!substringBounds.empty()) { 6178 mlir::Value dstLen = fir::factory::genLenOfCharacter( 6179 builder, loc, arrTy.cast<fir::SequenceType>(), memref, 6180 fir::getTypeParams(extMemref), iters.iterVec(), 6181 substringBounds); 6182 fir::CharBoxValue dstChar(coor, dstLen); 6183 return fir::factory::CharacterExprHelper{builder, loc} 6184 .createSubstring(dstChar, substringBounds); 6185 } 6186 } 6187 return fir::factory::arraySectionElementToExtendedValue( 6188 builder, loc, extMemref, coor, slice); 6189 }; 6190 } 6191 auto arrLoad = builder.create<fir::ArrayLoadOp>( 6192 loc, arrTy, memref, shape, slice, fir::getTypeParams(extMemref)); 6193 mlir::Value arrLd = arrLoad.getResult(); 6194 if (isProjectedCopyInCopyOut()) { 6195 // Semantics are projected copy-in copy-out. 6196 // The backing store of the destination of an array expression may be 6197 // partially modified. These updates are recorded in FIR by forwarding a 6198 // continuation that generates an `array_update` Op. The destination is 6199 // always loaded at the beginning of the statement and merged at the 6200 // end. 6201 destination = arrLoad; 6202 auto lambda = ccStoreToDest.hasValue() 6203 ? ccStoreToDest.getValue() 6204 : defaultStoreToDestination(components.substring); 6205 return [=](IterSpace iters) -> ExtValue { return lambda(iters); }; 6206 } 6207 if (isCustomCopyInCopyOut()) { 6208 // Create an array_modify to get the LHS element address and indicate 6209 // the assignment, the actual assignment must be implemented in 6210 // ccStoreToDest. 6211 destination = arrLoad; 6212 return [=](IterSpace iters) -> ExtValue { 6213 mlir::Value innerArg = iters.innerArgument(); 6214 mlir::Type resTy = innerArg.getType(); 6215 mlir::Type eleTy = fir::applyPathToType(resTy, iters.iterVec()); 6216 mlir::Type refEleTy = 6217 fir::isa_ref_type(eleTy) ? eleTy : builder.getRefType(eleTy); 6218 auto arrModify = builder.create<fir::ArrayModifyOp>( 6219 loc, mlir::TypeRange{refEleTy, resTy}, innerArg, iters.iterVec(), 6220 destination.getTypeparams()); 6221 return abstractArrayExtValue(arrModify.getResult(1)); 6222 }; 6223 } 6224 if (isCopyInCopyOut()) { 6225 // Semantics are copy-in copy-out. 6226 // The continuation simply forwards the result of the `array_load` Op, 6227 // which is the value of the array as it was when loaded. All data 6228 // references with rank > 0 in an array expression typically have 6229 // copy-in copy-out semantics. 6230 return [=](IterSpace) -> ExtValue { return arrLd; }; 6231 } 6232 mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams(); 6233 if (isValueAttribute()) { 6234 // Semantics are value attribute. 6235 // Here the continuation will `array_fetch` a value from an array and 6236 // then store that value in a temporary. One can thus imitate pass by 6237 // value even when the call is pass by reference. 6238 return [=](IterSpace iters) -> ExtValue { 6239 mlir::Value base; 6240 mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec()); 6241 if (isAdjustedArrayElementType(eleTy)) { 6242 mlir::Type eleRefTy = builder.getRefType(eleTy); 6243 base = builder.create<fir::ArrayAccessOp>( 6244 loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams); 6245 } else { 6246 base = builder.create<fir::ArrayFetchOp>( 6247 loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams); 6248 } 6249 mlir::Value temp = builder.createTemporary( 6250 loc, base.getType(), 6251 llvm::ArrayRef<mlir::NamedAttribute>{ 6252 Fortran::lower::getAdaptToByRefAttr(builder)}); 6253 builder.create<fir::StoreOp>(loc, base, temp); 6254 return fir::factory::arraySectionElementToExtendedValue( 6255 builder, loc, extMemref, temp, slice); 6256 }; 6257 } 6258 // In the default case, the array reference forwards an `array_fetch` or 6259 // `array_access` Op in the continuation. 6260 return [=](IterSpace iters) -> ExtValue { 6261 mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec()); 6262 if (isAdjustedArrayElementType(eleTy)) { 6263 mlir::Type eleRefTy = builder.getRefType(eleTy); 6264 mlir::Value arrayOp = builder.create<fir::ArrayAccessOp>( 6265 loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams); 6266 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 6267 llvm::SmallVector<mlir::Value> substringBounds; 6268 populateBounds(substringBounds, components.substring); 6269 if (!substringBounds.empty()) { 6270 mlir::Value dstLen = fir::factory::genLenOfCharacter( 6271 builder, loc, arrLoad, iters.iterVec(), substringBounds); 6272 fir::CharBoxValue dstChar(arrayOp, dstLen); 6273 return fir::factory::CharacterExprHelper{builder, loc} 6274 .createSubstring(dstChar, substringBounds); 6275 } 6276 } 6277 return fir::factory::arraySectionElementToExtendedValue( 6278 builder, loc, extMemref, arrayOp, slice); 6279 } 6280 auto arrFetch = builder.create<fir::ArrayFetchOp>( 6281 loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams); 6282 return fir::factory::arraySectionElementToExtendedValue( 6283 builder, loc, extMemref, arrFetch, slice); 6284 }; 6285 } 6286 6287 private: 6288 void determineShapeOfDest(const fir::ExtendedValue &lhs) { 6289 destShape = fir::factory::getExtents(builder, getLoc(), lhs); 6290 } 6291 6292 void determineShapeOfDest(const Fortran::lower::SomeExpr &lhs) { 6293 if (!destShape.empty()) 6294 return; 6295 if (explicitSpaceIsActive() && determineShapeWithSlice(lhs)) 6296 return; 6297 mlir::Type idxTy = builder.getIndexType(); 6298 mlir::Location loc = getLoc(); 6299 if (std::optional<Fortran::evaluate::ConstantSubscripts> constantShape = 6300 Fortran::evaluate::GetConstantExtents(converter.getFoldingContext(), 6301 lhs)) 6302 for (Fortran::common::ConstantSubscript extent : *constantShape) 6303 destShape.push_back(builder.createIntegerConstant(loc, idxTy, extent)); 6304 } 6305 6306 bool genShapeFromDataRef(const Fortran::semantics::Symbol &x) { 6307 return false; 6308 } 6309 bool genShapeFromDataRef(const Fortran::evaluate::CoarrayRef &) { 6310 TODO(getLoc(), "coarray ref"); 6311 return false; 6312 } 6313 bool genShapeFromDataRef(const Fortran::evaluate::Component &x) { 6314 return x.base().Rank() > 0 ? genShapeFromDataRef(x.base()) : false; 6315 } 6316 bool genShapeFromDataRef(const Fortran::evaluate::ArrayRef &x) { 6317 if (x.Rank() == 0) 6318 return false; 6319 if (x.base().Rank() > 0) 6320 if (genShapeFromDataRef(x.base())) 6321 return true; 6322 // x has rank and x.base did not produce a shape. 6323 ExtValue exv = x.base().IsSymbol() ? asScalarRef(getFirstSym(x.base())) 6324 : asScalarRef(x.base().GetComponent()); 6325 mlir::Location loc = getLoc(); 6326 mlir::IndexType idxTy = builder.getIndexType(); 6327 llvm::SmallVector<mlir::Value> definedShape = 6328 fir::factory::getExtents(builder, loc, exv); 6329 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); 6330 for (auto ss : llvm::enumerate(x.subscript())) { 6331 std::visit(Fortran::common::visitors{ 6332 [&](const Fortran::evaluate::Triplet &trip) { 6333 // For a subscript of triple notation, we compute the 6334 // range of this dimension of the iteration space. 6335 auto lo = [&]() { 6336 if (auto optLo = trip.lower()) 6337 return fir::getBase(asScalar(*optLo)); 6338 return getLBound(exv, ss.index(), one); 6339 }(); 6340 auto hi = [&]() { 6341 if (auto optHi = trip.upper()) 6342 return fir::getBase(asScalar(*optHi)); 6343 return getUBound(exv, ss.index(), one); 6344 }(); 6345 auto step = builder.createConvert( 6346 loc, idxTy, fir::getBase(asScalar(trip.stride()))); 6347 auto extent = builder.genExtentFromTriplet(loc, lo, hi, 6348 step, idxTy); 6349 destShape.push_back(extent); 6350 }, 6351 [&](auto) {}}, 6352 ss.value().u); 6353 } 6354 return true; 6355 } 6356 bool genShapeFromDataRef(const Fortran::evaluate::NamedEntity &x) { 6357 if (x.IsSymbol()) 6358 return genShapeFromDataRef(getFirstSym(x)); 6359 return genShapeFromDataRef(x.GetComponent()); 6360 } 6361 bool genShapeFromDataRef(const Fortran::evaluate::DataRef &x) { 6362 return std::visit([&](const auto &v) { return genShapeFromDataRef(v); }, 6363 x.u); 6364 } 6365 6366 /// When in an explicit space, the ranked component must be evaluated to 6367 /// determine the actual number of iterations when slicing triples are 6368 /// present. Lower these expressions here. 6369 bool determineShapeWithSlice(const Fortran::lower::SomeExpr &lhs) { 6370 LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump( 6371 llvm::dbgs() << "determine shape of:\n", lhs)); 6372 // FIXME: We may not want to use ExtractDataRef here since it doesn't deal 6373 // with substrings, etc. 6374 std::optional<Fortran::evaluate::DataRef> dref = 6375 Fortran::evaluate::ExtractDataRef(lhs); 6376 return dref.has_value() ? genShapeFromDataRef(*dref) : false; 6377 } 6378 6379 ExtValue lowerArrayExpression(const Fortran::lower::SomeExpr &exp) { 6380 mlir::Type resTy = converter.genType(exp); 6381 return std::visit( 6382 [&](const auto &e) { return lowerArrayExpression(genarr(e), resTy); }, 6383 exp.u); 6384 } 6385 ExtValue lowerArrayExpression(const ExtValue &exv) { 6386 assert(!explicitSpace); 6387 mlir::Type resTy = fir::unwrapPassByRefType(fir::getBase(exv).getType()); 6388 return lowerArrayExpression(genarr(exv), resTy); 6389 } 6390 6391 void populateBounds(llvm::SmallVectorImpl<mlir::Value> &bounds, 6392 const Fortran::evaluate::Substring *substring) { 6393 if (!substring) 6394 return; 6395 bounds.push_back(fir::getBase(asScalar(substring->lower()))); 6396 if (auto upper = substring->upper()) 6397 bounds.push_back(fir::getBase(asScalar(*upper))); 6398 } 6399 6400 /// Default store to destination implementation. 6401 /// This implements the default case, which is to assign the value in 6402 /// `iters.element` into the destination array, `iters.innerArgument`. Handles 6403 /// by value and by reference assignment. 6404 CC defaultStoreToDestination(const Fortran::evaluate::Substring *substring) { 6405 return [=](IterSpace iterSpace) -> ExtValue { 6406 mlir::Location loc = getLoc(); 6407 mlir::Value innerArg = iterSpace.innerArgument(); 6408 fir::ExtendedValue exv = iterSpace.elementExv(); 6409 mlir::Type arrTy = innerArg.getType(); 6410 mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec()); 6411 if (isAdjustedArrayElementType(eleTy)) { 6412 // The elemental update is in the memref domain. Under this semantics, 6413 // we must always copy the computed new element from its location in 6414 // memory into the destination array. 6415 mlir::Type resRefTy = builder.getRefType(eleTy); 6416 // Get a reference to the array element to be amended. 6417 auto arrayOp = builder.create<fir::ArrayAccessOp>( 6418 loc, resRefTy, innerArg, iterSpace.iterVec(), 6419 destination.getTypeparams()); 6420 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 6421 llvm::SmallVector<mlir::Value> substringBounds; 6422 populateBounds(substringBounds, substring); 6423 mlir::Value dstLen = fir::factory::genLenOfCharacter( 6424 builder, loc, destination, iterSpace.iterVec(), substringBounds); 6425 fir::ArrayAmendOp amend = createCharArrayAmend( 6426 loc, builder, arrayOp, dstLen, exv, innerArg, substringBounds); 6427 return abstractArrayExtValue(amend, dstLen); 6428 } 6429 if (fir::isa_derived(eleTy)) { 6430 fir::ArrayAmendOp amend = createDerivedArrayAmend( 6431 loc, destination, builder, arrayOp, exv, eleTy, innerArg); 6432 return abstractArrayExtValue(amend /*FIXME: typeparams?*/); 6433 } 6434 assert(eleTy.isa<fir::SequenceType>() && "must be an array"); 6435 TODO(loc, "array (as element) assignment"); 6436 } 6437 // By value semantics. The element is being assigned by value. 6438 mlir::Value ele = builder.createConvert(loc, eleTy, fir::getBase(exv)); 6439 auto update = builder.create<fir::ArrayUpdateOp>( 6440 loc, arrTy, innerArg, ele, iterSpace.iterVec(), 6441 destination.getTypeparams()); 6442 return abstractArrayExtValue(update); 6443 }; 6444 } 6445 6446 /// For an elemental array expression. 6447 /// 1. Lower the scalars and array loads. 6448 /// 2. Create the iteration space. 6449 /// 3. Create the element-by-element computation in the loop. 6450 /// 4. Return the resulting array value. 6451 /// If no destination was set in the array context, a temporary of 6452 /// \p resultTy will be created to hold the evaluated expression. 6453 /// Otherwise, \p resultTy is ignored and the expression is evaluated 6454 /// in the destination. \p f is a continuation built from an 6455 /// evaluate::Expr or an ExtendedValue. 6456 ExtValue lowerArrayExpression(CC f, mlir::Type resultTy) { 6457 mlir::Location loc = getLoc(); 6458 auto [iterSpace, insPt] = genIterSpace(resultTy); 6459 auto exv = f(iterSpace); 6460 iterSpace.setElement(std::move(exv)); 6461 auto lambda = ccStoreToDest.hasValue() 6462 ? ccStoreToDest.getValue() 6463 : defaultStoreToDestination(/*substring=*/nullptr); 6464 mlir::Value updVal = fir::getBase(lambda(iterSpace)); 6465 finalizeElementCtx(); 6466 builder.create<fir::ResultOp>(loc, updVal); 6467 builder.restoreInsertionPoint(insPt); 6468 return abstractArrayExtValue(iterSpace.outerResult()); 6469 } 6470 6471 /// Compute the shape of a slice. 6472 llvm::SmallVector<mlir::Value> computeSliceShape(mlir::Value slice) { 6473 llvm::SmallVector<mlir::Value> slicedShape; 6474 auto slOp = mlir::cast<fir::SliceOp>(slice.getDefiningOp()); 6475 mlir::Operation::operand_range triples = slOp.getTriples(); 6476 mlir::IndexType idxTy = builder.getIndexType(); 6477 mlir::Location loc = getLoc(); 6478 for (unsigned i = 0, end = triples.size(); i < end; i += 3) { 6479 if (!mlir::isa_and_nonnull<fir::UndefOp>( 6480 triples[i + 1].getDefiningOp())) { 6481 // (..., lb:ub:step, ...) case: extent = max((ub-lb+step)/step, 0) 6482 // See Fortran 2018 9.5.3.3.2 section for more details. 6483 mlir::Value res = builder.genExtentFromTriplet( 6484 loc, triples[i], triples[i + 1], triples[i + 2], idxTy); 6485 slicedShape.emplace_back(res); 6486 } else { 6487 // do nothing. `..., i, ...` case, so dimension is dropped. 6488 } 6489 } 6490 return slicedShape; 6491 } 6492 6493 /// Get the shape from an ArrayOperand. The shape of the array is adjusted if 6494 /// the array was sliced. 6495 llvm::SmallVector<mlir::Value> getShape(ArrayOperand array) { 6496 if (array.slice) 6497 return computeSliceShape(array.slice); 6498 if (array.memref.getType().isa<fir::BoxType>()) 6499 return fir::factory::readExtents(builder, getLoc(), 6500 fir::BoxValue{array.memref}); 6501 std::vector<mlir::Value, std::allocator<mlir::Value>> extents = 6502 fir::factory::getExtents(array.shape); 6503 return {extents.begin(), extents.end()}; 6504 } 6505 6506 /// Get the shape from an ArrayLoad. 6507 llvm::SmallVector<mlir::Value> getShape(fir::ArrayLoadOp arrayLoad) { 6508 return getShape(ArrayOperand{arrayLoad.getMemref(), arrayLoad.getShape(), 6509 arrayLoad.getSlice()}); 6510 } 6511 6512 /// Returns the first array operand that may not be absent. If all 6513 /// array operands may be absent, return the first one. 6514 const ArrayOperand &getInducingShapeArrayOperand() const { 6515 assert(!arrayOperands.empty()); 6516 for (const ArrayOperand &op : arrayOperands) 6517 if (!op.mayBeAbsent) 6518 return op; 6519 // If all arrays operand appears in optional position, then none of them 6520 // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the 6521 // first operands. 6522 // TODO: There is an opportunity to add a runtime check here that 6523 // this array is present as required. 6524 return arrayOperands[0]; 6525 } 6526 6527 /// Generate the shape of the iteration space over the array expression. The 6528 /// iteration space may be implicit, explicit, or both. If it is implied it is 6529 /// based on the destination and operand array loads, or an optional 6530 /// Fortran::evaluate::Shape from the front end. If the shape is explicit, 6531 /// this returns any implicit shape component, if it exists. 6532 llvm::SmallVector<mlir::Value> genIterationShape() { 6533 // Use the precomputed destination shape. 6534 if (!destShape.empty()) 6535 return destShape; 6536 // Otherwise, use the destination's shape. 6537 if (destination) 6538 return getShape(destination); 6539 // Otherwise, use the first ArrayLoad operand shape. 6540 if (!arrayOperands.empty()) 6541 return getShape(getInducingShapeArrayOperand()); 6542 fir::emitFatalError(getLoc(), 6543 "failed to compute the array expression shape"); 6544 } 6545 6546 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter, 6547 Fortran::lower::StatementContext &stmtCtx, 6548 Fortran::lower::SymMap &symMap) 6549 : converter{converter}, builder{converter.getFirOpBuilder()}, 6550 stmtCtx{stmtCtx}, symMap{symMap} {} 6551 6552 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter, 6553 Fortran::lower::StatementContext &stmtCtx, 6554 Fortran::lower::SymMap &symMap, 6555 ConstituentSemantics sem) 6556 : converter{converter}, builder{converter.getFirOpBuilder()}, 6557 stmtCtx{stmtCtx}, symMap{symMap}, semant{sem} {} 6558 6559 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter, 6560 Fortran::lower::StatementContext &stmtCtx, 6561 Fortran::lower::SymMap &symMap, 6562 ConstituentSemantics sem, 6563 Fortran::lower::ExplicitIterSpace *expSpace, 6564 Fortran::lower::ImplicitIterSpace *impSpace) 6565 : converter{converter}, builder{converter.getFirOpBuilder()}, 6566 stmtCtx{stmtCtx}, symMap{symMap}, 6567 explicitSpace(expSpace->isActive() ? expSpace : nullptr), 6568 implicitSpace(impSpace->empty() ? nullptr : impSpace), semant{sem} { 6569 // Generate any mask expressions, as necessary. This is the compute step 6570 // that creates the effective masks. See 10.2.3.2 in particular. 6571 genMasks(); 6572 } 6573 6574 mlir::Location getLoc() { return converter.getCurrentLocation(); } 6575 6576 /// Array appears in a lhs context such that it is assigned after the rhs is 6577 /// fully evaluated. 6578 inline bool isCopyInCopyOut() { 6579 return semant == ConstituentSemantics::CopyInCopyOut; 6580 } 6581 6582 /// Array appears in a lhs (or temp) context such that a projected, 6583 /// discontiguous subspace of the array is assigned after the rhs is fully 6584 /// evaluated. That is, the rhs array value is merged into a section of the 6585 /// lhs array. 6586 inline bool isProjectedCopyInCopyOut() { 6587 return semant == ConstituentSemantics::ProjectedCopyInCopyOut; 6588 } 6589 6590 inline bool isCustomCopyInCopyOut() { 6591 return semant == ConstituentSemantics::CustomCopyInCopyOut; 6592 } 6593 6594 /// Array appears in a context where it must be boxed. 6595 inline bool isBoxValue() { return semant == ConstituentSemantics::BoxValue; } 6596 6597 /// Array appears in a context where differences in the memory reference can 6598 /// be observable in the computational results. For example, an array 6599 /// element is passed to an impure procedure. 6600 inline bool isReferentiallyOpaque() { 6601 return semant == ConstituentSemantics::RefOpaque; 6602 } 6603 6604 /// Array appears in a context where it is passed as a VALUE argument. 6605 inline bool isValueAttribute() { 6606 return semant == ConstituentSemantics::ByValueArg; 6607 } 6608 6609 /// Can the loops over the expression be unordered? 6610 inline bool isUnordered() const { return unordered; } 6611 6612 void setUnordered(bool b) { unordered = b; } 6613 6614 Fortran::lower::AbstractConverter &converter; 6615 fir::FirOpBuilder &builder; 6616 Fortran::lower::StatementContext &stmtCtx; 6617 bool elementCtx = false; 6618 Fortran::lower::SymMap &symMap; 6619 /// The continuation to generate code to update the destination. 6620 llvm::Optional<CC> ccStoreToDest; 6621 llvm::Optional<std::function<void(llvm::ArrayRef<mlir::Value>)>> ccPrelude; 6622 llvm::Optional<std::function<fir::ArrayLoadOp(llvm::ArrayRef<mlir::Value>)>> 6623 ccLoadDest; 6624 /// The destination is the loaded array into which the results will be 6625 /// merged. 6626 fir::ArrayLoadOp destination; 6627 /// The shape of the destination. 6628 llvm::SmallVector<mlir::Value> destShape; 6629 /// List of arrays in the expression that have been loaded. 6630 llvm::SmallVector<ArrayOperand> arrayOperands; 6631 /// If there is a user-defined iteration space, explicitShape will hold the 6632 /// information from the front end. 6633 Fortran::lower::ExplicitIterSpace *explicitSpace = nullptr; 6634 Fortran::lower::ImplicitIterSpace *implicitSpace = nullptr; 6635 ConstituentSemantics semant = ConstituentSemantics::RefTransparent; 6636 // Can the array expression be evaluated in any order? 6637 // Will be set to false if any of the expression parts prevent this. 6638 bool unordered = true; 6639 }; 6640 } // namespace 6641 6642 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression( 6643 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 6644 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 6645 Fortran::lower::StatementContext &stmtCtx) { 6646 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n'); 6647 return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr); 6648 } 6649 6650 fir::GlobalOp Fortran::lower::createDenseGlobal( 6651 mlir::Location loc, mlir::Type symTy, llvm::StringRef globalName, 6652 mlir::StringAttr linkage, bool isConst, 6653 const Fortran::lower::SomeExpr &expr, 6654 Fortran::lower::AbstractConverter &converter) { 6655 6656 Fortran::lower::StatementContext stmtCtx(/*prohibited=*/true); 6657 Fortran::lower::SymMap emptyMap; 6658 InitializerData initData(/*genRawVals=*/true); 6659 ScalarExprLowering sel(loc, converter, emptyMap, stmtCtx, 6660 /*initializer=*/&initData); 6661 sel.genval(expr); 6662 6663 size_t sz = initData.rawVals.size(); 6664 llvm::ArrayRef<mlir::Attribute> ar = {initData.rawVals.data(), sz}; 6665 6666 mlir::RankedTensorType tensorTy; 6667 auto &builder = converter.getFirOpBuilder(); 6668 mlir::Type iTy = initData.rawType; 6669 if (!iTy) 6670 return 0; // array extent is probably 0 in this case, so just return 0. 6671 tensorTy = mlir::RankedTensorType::get(sz, iTy); 6672 auto init = mlir::DenseElementsAttr::get(tensorTy, ar); 6673 return builder.createGlobal(loc, symTy, globalName, linkage, init, isConst); 6674 } 6675 6676 fir::ExtendedValue Fortran::lower::createSomeInitializerExpression( 6677 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 6678 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 6679 Fortran::lower::StatementContext &stmtCtx) { 6680 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n'); 6681 InitializerData initData; // needed for initializations 6682 return ScalarExprLowering{loc, converter, symMap, stmtCtx, 6683 /*initializer=*/&initData} 6684 .genval(expr); 6685 } 6686 6687 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress( 6688 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 6689 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 6690 Fortran::lower::StatementContext &stmtCtx) { 6691 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n'); 6692 return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr); 6693 } 6694 6695 fir::ExtendedValue Fortran::lower::createInitializerAddress( 6696 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 6697 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 6698 Fortran::lower::StatementContext &stmtCtx) { 6699 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n'); 6700 InitializerData init; 6701 return ScalarExprLowering(loc, converter, symMap, stmtCtx, &init).gen(expr); 6702 } 6703 6704 fir::ExtendedValue 6705 Fortran::lower::createSomeArrayBox(Fortran::lower::AbstractConverter &converter, 6706 const Fortran::lower::SomeExpr &expr, 6707 Fortran::lower::SymMap &symMap, 6708 Fortran::lower::StatementContext &stmtCtx) { 6709 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "box designator: ") << '\n'); 6710 return ArrayExprLowering::lowerBoxedArrayExpression(converter, symMap, 6711 stmtCtx, expr); 6712 } 6713 6714 fir::MutableBoxValue Fortran::lower::createMutableBox( 6715 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 6716 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) { 6717 // MutableBox lowering StatementContext does not need to be propagated 6718 // to the caller because the result value is a variable, not a temporary 6719 // expression. The StatementContext clean-up can occur before using the 6720 // resulting MutableBoxValue. Variables of all other types are handled in the 6721 // bridge. 6722 Fortran::lower::StatementContext dummyStmtCtx; 6723 return ScalarExprLowering{loc, converter, symMap, dummyStmtCtx} 6724 .genMutableBoxValue(expr); 6725 } 6726 6727 fir::ExtendedValue Fortran::lower::createBoxValue( 6728 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 6729 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 6730 Fortran::lower::StatementContext &stmtCtx) { 6731 if (expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) && 6732 !Fortran::evaluate::HasVectorSubscript(expr)) 6733 return Fortran::lower::createSomeArrayBox(converter, expr, symMap, stmtCtx); 6734 fir::ExtendedValue addr = Fortran::lower::createSomeExtendedAddress( 6735 loc, converter, expr, symMap, stmtCtx); 6736 return fir::BoxValue(converter.getFirOpBuilder().createBox(loc, addr)); 6737 } 6738 6739 mlir::Value Fortran::lower::createSubroutineCall( 6740 AbstractConverter &converter, const evaluate::ProcedureRef &call, 6741 ExplicitIterSpace &explicitIterSpace, ImplicitIterSpace &implicitIterSpace, 6742 SymMap &symMap, StatementContext &stmtCtx, bool isUserDefAssignment) { 6743 mlir::Location loc = converter.getCurrentLocation(); 6744 6745 if (isUserDefAssignment) { 6746 assert(call.arguments().size() == 2); 6747 const auto *lhs = call.arguments()[0].value().UnwrapExpr(); 6748 const auto *rhs = call.arguments()[1].value().UnwrapExpr(); 6749 assert(lhs && rhs && 6750 "user defined assignment arguments must be expressions"); 6751 if (call.IsElemental() && lhs->Rank() > 0) { 6752 // Elemental user defined assignment has special requirements to deal with 6753 // LHS/RHS overlaps. See 10.2.1.5 p2. 6754 ArrayExprLowering::lowerElementalUserAssignment( 6755 converter, symMap, stmtCtx, explicitIterSpace, implicitIterSpace, 6756 call); 6757 } else if (explicitIterSpace.isActive() && lhs->Rank() == 0) { 6758 // Scalar defined assignment (elemental or not) in a FORALL context. 6759 mlir::FuncOp func = 6760 Fortran::lower::CallerInterface(call, converter).getFuncOp(); 6761 ArrayExprLowering::lowerScalarUserAssignment( 6762 converter, symMap, stmtCtx, explicitIterSpace, func, *lhs, *rhs); 6763 } else if (explicitIterSpace.isActive()) { 6764 // TODO: need to array fetch/modify sub-arrays? 6765 TODO(loc, "non elemental user defined array assignment inside FORALL"); 6766 } else { 6767 if (!implicitIterSpace.empty()) 6768 fir::emitFatalError( 6769 loc, 6770 "C1032: user defined assignment inside WHERE must be elemental"); 6771 // Non elemental user defined assignment outside of FORALL and WHERE. 6772 // FIXME: The non elemental user defined assignment case with array 6773 // arguments must be take into account potential overlap. So far the front 6774 // end does not add parentheses around the RHS argument in the call as it 6775 // should according to 15.4.3.4.3 p2. 6776 Fortran::lower::createSomeExtendedExpression( 6777 loc, converter, toEvExpr(call), symMap, stmtCtx); 6778 } 6779 return {}; 6780 } 6781 6782 assert(implicitIterSpace.empty() && !explicitIterSpace.isActive() && 6783 "subroutine calls are not allowed inside WHERE and FORALL"); 6784 6785 if (isElementalProcWithArrayArgs(call)) { 6786 ArrayExprLowering::lowerElementalSubroutine(converter, symMap, stmtCtx, 6787 toEvExpr(call)); 6788 return {}; 6789 } 6790 // Simple subroutine call, with potential alternate return. 6791 auto res = Fortran::lower::createSomeExtendedExpression( 6792 loc, converter, toEvExpr(call), symMap, stmtCtx); 6793 return fir::getBase(res); 6794 } 6795 6796 template <typename A> 6797 fir::ArrayLoadOp genArrayLoad(mlir::Location loc, 6798 Fortran::lower::AbstractConverter &converter, 6799 fir::FirOpBuilder &builder, const A *x, 6800 Fortran::lower::SymMap &symMap, 6801 Fortran::lower::StatementContext &stmtCtx) { 6802 auto exv = ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(*x); 6803 mlir::Value addr = fir::getBase(exv); 6804 mlir::Value shapeOp = builder.createShape(loc, exv); 6805 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(addr.getType()); 6806 return builder.create<fir::ArrayLoadOp>(loc, arrTy, addr, shapeOp, 6807 /*slice=*/mlir::Value{}, 6808 fir::getTypeParams(exv)); 6809 } 6810 template <> 6811 fir::ArrayLoadOp 6812 genArrayLoad(mlir::Location loc, Fortran::lower::AbstractConverter &converter, 6813 fir::FirOpBuilder &builder, const Fortran::evaluate::ArrayRef *x, 6814 Fortran::lower::SymMap &symMap, 6815 Fortran::lower::StatementContext &stmtCtx) { 6816 if (x->base().IsSymbol()) 6817 return genArrayLoad(loc, converter, builder, &x->base().GetLastSymbol(), 6818 symMap, stmtCtx); 6819 return genArrayLoad(loc, converter, builder, &x->base().GetComponent(), 6820 symMap, stmtCtx); 6821 } 6822 6823 void Fortran::lower::createArrayLoads( 6824 Fortran::lower::AbstractConverter &converter, 6825 Fortran::lower::ExplicitIterSpace &esp, Fortran::lower::SymMap &symMap) { 6826 std::size_t counter = esp.getCounter(); 6827 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 6828 mlir::Location loc = converter.getCurrentLocation(); 6829 Fortran::lower::StatementContext &stmtCtx = esp.stmtContext(); 6830 // Gen the fir.array_load ops. 6831 auto genLoad = [&](const auto *x) -> fir::ArrayLoadOp { 6832 return genArrayLoad(loc, converter, builder, x, symMap, stmtCtx); 6833 }; 6834 if (esp.lhsBases[counter].hasValue()) { 6835 auto &base = esp.lhsBases[counter].getValue(); 6836 auto load = std::visit(genLoad, base); 6837 esp.initialArgs.push_back(load); 6838 esp.resetInnerArgs(); 6839 esp.bindLoad(base, load); 6840 } 6841 for (const auto &base : esp.rhsBases[counter]) 6842 esp.bindLoad(base, std::visit(genLoad, base)); 6843 } 6844 6845 void Fortran::lower::createArrayMergeStores( 6846 Fortran::lower::AbstractConverter &converter, 6847 Fortran::lower::ExplicitIterSpace &esp) { 6848 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 6849 mlir::Location loc = converter.getCurrentLocation(); 6850 builder.setInsertionPointAfter(esp.getOuterLoop()); 6851 // Gen the fir.array_merge_store ops for all LHS arrays. 6852 for (auto i : llvm::enumerate(esp.getOuterLoop().getResults())) 6853 if (llvm::Optional<fir::ArrayLoadOp> ldOpt = esp.getLhsLoad(i.index())) { 6854 fir::ArrayLoadOp load = ldOpt.getValue(); 6855 builder.create<fir::ArrayMergeStoreOp>(loc, load, i.value(), 6856 load.getMemref(), load.getSlice(), 6857 load.getTypeparams()); 6858 } 6859 if (esp.loopCleanup.hasValue()) { 6860 esp.loopCleanup.getValue()(builder); 6861 esp.loopCleanup = llvm::None; 6862 } 6863 esp.initialArgs.clear(); 6864 esp.innerArgs.clear(); 6865 esp.outerLoop = llvm::None; 6866 esp.resetBindings(); 6867 esp.incrementCounter(); 6868 } 6869 6870 void Fortran::lower::createSomeArrayAssignment( 6871 Fortran::lower::AbstractConverter &converter, 6872 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 6873 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 6874 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n'; 6875 rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';); 6876 ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs); 6877 } 6878 6879 void Fortran::lower::createSomeArrayAssignment( 6880 Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs, 6881 const Fortran::lower::SomeExpr &rhs, Fortran::lower::SymMap &symMap, 6882 Fortran::lower::StatementContext &stmtCtx) { 6883 LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n'; 6884 rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';); 6885 ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs); 6886 } 6887 6888 void Fortran::lower::createSomeArrayAssignment( 6889 Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs, 6890 const fir::ExtendedValue &rhs, Fortran::lower::SymMap &symMap, 6891 Fortran::lower::StatementContext &stmtCtx) { 6892 LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n'; 6893 llvm::dbgs() << "assign expression: " << rhs << '\n';); 6894 ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs); 6895 } 6896 6897 void Fortran::lower::createAnyMaskedArrayAssignment( 6898 Fortran::lower::AbstractConverter &converter, 6899 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 6900 Fortran::lower::ExplicitIterSpace &explicitSpace, 6901 Fortran::lower::ImplicitIterSpace &implicitSpace, 6902 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 6903 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n'; 6904 rhs.AsFortran(llvm::dbgs() << "assign expression: ") 6905 << " given the explicit iteration space:\n" 6906 << explicitSpace << "\n and implied mask conditions:\n" 6907 << implicitSpace << '\n';); 6908 ArrayExprLowering::lowerAnyMaskedArrayAssignment( 6909 converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace); 6910 } 6911 6912 void Fortran::lower::createAllocatableArrayAssignment( 6913 Fortran::lower::AbstractConverter &converter, 6914 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 6915 Fortran::lower::ExplicitIterSpace &explicitSpace, 6916 Fortran::lower::ImplicitIterSpace &implicitSpace, 6917 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 6918 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining array: ") << '\n'; 6919 rhs.AsFortran(llvm::dbgs() << "assign expression: ") 6920 << " given the explicit iteration space:\n" 6921 << explicitSpace << "\n and implied mask conditions:\n" 6922 << implicitSpace << '\n';); 6923 ArrayExprLowering::lowerAllocatableArrayAssignment( 6924 converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace); 6925 } 6926 6927 fir::ExtendedValue Fortran::lower::createSomeArrayTempValue( 6928 Fortran::lower::AbstractConverter &converter, 6929 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 6930 Fortran::lower::StatementContext &stmtCtx) { 6931 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n'); 6932 return ArrayExprLowering::lowerNewArrayExpression(converter, symMap, stmtCtx, 6933 expr); 6934 } 6935 6936 void Fortran::lower::createLazyArrayTempValue( 6937 Fortran::lower::AbstractConverter &converter, 6938 const Fortran::lower::SomeExpr &expr, mlir::Value raggedHeader, 6939 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 6940 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n'); 6941 ArrayExprLowering::lowerLazyArrayExpression(converter, symMap, stmtCtx, expr, 6942 raggedHeader); 6943 } 6944 6945 mlir::Value Fortran::lower::genMaxWithZero(fir::FirOpBuilder &builder, 6946 mlir::Location loc, 6947 mlir::Value value) { 6948 mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0); 6949 if (mlir::Operation *definingOp = value.getDefiningOp()) 6950 if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp)) 6951 if (auto intAttr = cst.getValue().dyn_cast<mlir::IntegerAttr>()) 6952 return intAttr.getInt() < 0 ? zero : value; 6953 return Fortran::lower::genMax(builder, loc, 6954 llvm::SmallVector<mlir::Value>{value, zero}); 6955 } 6956