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