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