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