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