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