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