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