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