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