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