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