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