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