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