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/StatementContext.h" 25 #include "flang/Lower/SymbolMap.h" 26 #include "flang/Lower/Todo.h" 27 #include "flang/Optimizer/Builder/Character.h" 28 #include "flang/Optimizer/Builder/Complex.h" 29 #include "flang/Optimizer/Builder/Factory.h" 30 #include "flang/Optimizer/Builder/LowLevelIntrinsics.h" 31 #include "flang/Optimizer/Builder/MutableBox.h" 32 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h" 33 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 34 #include "flang/Semantics/expression.h" 35 #include "flang/Semantics/symbol.h" 36 #include "flang/Semantics/tools.h" 37 #include "flang/Semantics/type.h" 38 #include "mlir/Dialect/Func/IR/FuncOps.h" 39 #include "llvm/Support/CommandLine.h" 40 #include "llvm/Support/Debug.h" 41 42 #define DEBUG_TYPE "flang-lower-expr" 43 44 //===----------------------------------------------------------------------===// 45 // The composition and structure of Fortran::evaluate::Expr is defined in 46 // the various header files in include/flang/Evaluate. You are referred 47 // there for more information on these data structures. Generally speaking, 48 // these data structures are a strongly typed family of abstract data types 49 // that, composed as trees, describe the syntax of Fortran expressions. 50 // 51 // This part of the bridge can traverse these tree structures and lower them 52 // to the correct FIR representation in SSA form. 53 //===----------------------------------------------------------------------===// 54 55 // The default attempts to balance a modest allocation size with expected user 56 // input to minimize bounds checks and reallocations during dynamic array 57 // construction. Some user codes may have very large array constructors for 58 // which the default can be increased. 59 static llvm::cl::opt<unsigned> clInitialBufferSize( 60 "array-constructor-initial-buffer-size", 61 llvm::cl::desc( 62 "set the incremental array construction buffer size (default=32)"), 63 llvm::cl::init(32u)); 64 65 /// The various semantics of a program constituent (or a part thereof) as it may 66 /// appear in an expression. 67 /// 68 /// Given the following Fortran declarations. 69 /// ```fortran 70 /// REAL :: v1, v2, v3 71 /// REAL, POINTER :: vp1 72 /// REAL :: a1(c), a2(c) 73 /// REAL ELEMENTAL FUNCTION f1(arg) ! array -> array 74 /// FUNCTION f2(arg) ! array -> array 75 /// vp1 => v3 ! 1 76 /// v1 = v2 * vp1 ! 2 77 /// a1 = a1 + a2 ! 3 78 /// a1 = f1(a2) ! 4 79 /// a1 = f2(a2) ! 5 80 /// ``` 81 /// 82 /// In line 1, `vp1` is a BoxAddr to copy a box value into. The box value is 83 /// constructed from the DataAddr of `v3`. 84 /// In line 2, `v1` is a DataAddr to copy a value into. The value is constructed 85 /// from the DataValue of `v2` and `vp1`. DataValue is implicitly a double 86 /// dereference in the `vp1` case. 87 /// In line 3, `a1` and `a2` on the rhs are RefTransparent. The `a1` on the lhs 88 /// is CopyInCopyOut as `a1` is replaced elementally by the additions. 89 /// In line 4, `a2` can be RefTransparent, ByValueArg, RefOpaque, or BoxAddr if 90 /// `arg` is declared as C-like pass-by-value, VALUE, INTENT(?), or ALLOCATABLE/ 91 /// POINTER, respectively. `a1` on the lhs is CopyInCopyOut. 92 /// In line 5, `a2` may be DataAddr or BoxAddr assuming f2 is transformational. 93 /// `a1` on the lhs is again CopyInCopyOut. 94 enum class ConstituentSemantics { 95 // Scalar data reference semantics. 96 // 97 // For these let `v` be the location in memory of a variable with value `x` 98 DataValue, // refers to the value `x` 99 DataAddr, // refers to the address `v` 100 BoxValue, // refers to a box value containing `v` 101 BoxAddr, // refers to the address of a box value containing `v` 102 103 // Array data reference semantics. 104 // 105 // For these let `a` be the location in memory of a sequence of value `[xs]`. 106 // Let `x_i` be the `i`-th value in the sequence `[xs]`. 107 108 // Referentially transparent. Refers to the array's value, `[xs]`. 109 RefTransparent, 110 // Refers to an ephemeral address `tmp` containing value `x_i` (15.5.2.3.p7 111 // note 2). (Passing a copy by reference to simulate pass-by-value.) 112 ByValueArg, 113 // Refers to the merge of array value `[xs]` with another array value `[ys]`. 114 // This merged array value will be written into memory location `a`. 115 CopyInCopyOut, 116 // Similar to CopyInCopyOut but `a` may be a transient projection (rather than 117 // a whole array). 118 ProjectedCopyInCopyOut, 119 // Similar to ProjectedCopyInCopyOut, except the merge value is not assigned 120 // automatically by the framework. Instead, and address for `[xs]` is made 121 // accessible so that custom assignments to `[xs]` can be implemented. 122 CustomCopyInCopyOut, 123 // Referentially opaque. Refers to the address of `x_i`. 124 RefOpaque 125 }; 126 127 /// Convert parser's INTEGER relational operators to MLIR. TODO: using 128 /// unordered, but we may want to cons ordered in certain situation. 129 static mlir::arith::CmpIPredicate 130 translateRelational(Fortran::common::RelationalOperator rop) { 131 switch (rop) { 132 case Fortran::common::RelationalOperator::LT: 133 return mlir::arith::CmpIPredicate::slt; 134 case Fortran::common::RelationalOperator::LE: 135 return mlir::arith::CmpIPredicate::sle; 136 case Fortran::common::RelationalOperator::EQ: 137 return mlir::arith::CmpIPredicate::eq; 138 case Fortran::common::RelationalOperator::NE: 139 return mlir::arith::CmpIPredicate::ne; 140 case Fortran::common::RelationalOperator::GT: 141 return mlir::arith::CmpIPredicate::sgt; 142 case Fortran::common::RelationalOperator::GE: 143 return mlir::arith::CmpIPredicate::sge; 144 } 145 llvm_unreachable("unhandled INTEGER relational operator"); 146 } 147 148 /// Convert parser's REAL relational operators to MLIR. 149 /// The choice of order (O prefix) vs unorder (U prefix) follows Fortran 2018 150 /// requirements in the IEEE context (table 17.1 of F2018). This choice is 151 /// also applied in other contexts because it is easier and in line with 152 /// other Fortran compilers. 153 /// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not 154 /// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee 155 /// whether the comparison will signal or not in case of quiet NaN argument. 156 static mlir::arith::CmpFPredicate 157 translateFloatRelational(Fortran::common::RelationalOperator rop) { 158 switch (rop) { 159 case Fortran::common::RelationalOperator::LT: 160 return mlir::arith::CmpFPredicate::OLT; 161 case Fortran::common::RelationalOperator::LE: 162 return mlir::arith::CmpFPredicate::OLE; 163 case Fortran::common::RelationalOperator::EQ: 164 return mlir::arith::CmpFPredicate::OEQ; 165 case Fortran::common::RelationalOperator::NE: 166 return mlir::arith::CmpFPredicate::UNE; 167 case Fortran::common::RelationalOperator::GT: 168 return mlir::arith::CmpFPredicate::OGT; 169 case Fortran::common::RelationalOperator::GE: 170 return mlir::arith::CmpFPredicate::OGE; 171 } 172 llvm_unreachable("unhandled REAL relational operator"); 173 } 174 175 static mlir::Value genActualIsPresentTest(fir::FirOpBuilder &builder, 176 mlir::Location loc, 177 fir::ExtendedValue actual) { 178 if (const auto *ptrOrAlloc = actual.getBoxOf<fir::MutableBoxValue>()) 179 return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, 180 *ptrOrAlloc); 181 // Optional case (not that optional allocatable/pointer cannot be absent 182 // when passed to CMPLX as per 15.5.2.12 point 3 (7) and (8)). It is 183 // therefore possible to catch them in the `then` case above. 184 return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), 185 fir::getBase(actual)); 186 } 187 188 /// Place \p exv in memory if it is not already a memory reference. If 189 /// \p forceValueType is provided, the value is first casted to the provided 190 /// type before being stored (this is mainly intended for logicals whose value 191 /// may be `i1` but needed to be stored as Fortran logicals). 192 static fir::ExtendedValue 193 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc, 194 const fir::ExtendedValue &exv, 195 mlir::Type storageType) { 196 mlir::Value valBase = fir::getBase(exv); 197 if (fir::conformsWithPassByRef(valBase.getType())) 198 return exv; 199 200 assert(!fir::hasDynamicSize(storageType) && 201 "only expect statically sized scalars to be by value"); 202 203 // Since `a` is not itself a valid referent, determine its value and 204 // create a temporary location at the beginning of the function for 205 // referencing. 206 mlir::Value val = builder.createConvert(loc, storageType, valBase); 207 mlir::Value temp = builder.createTemporary( 208 loc, storageType, 209 llvm::ArrayRef<mlir::NamedAttribute>{ 210 Fortran::lower::getAdaptToByRefAttr(builder)}); 211 builder.create<fir::StoreOp>(loc, val, temp); 212 return fir::substBase(exv, temp); 213 } 214 215 // Copy a copy of scalar \p exv in a new temporary. 216 static fir::ExtendedValue 217 createInMemoryScalarCopy(fir::FirOpBuilder &builder, mlir::Location loc, 218 const fir::ExtendedValue &exv) { 219 assert(exv.rank() == 0 && "input to scalar memory copy must be a scalar"); 220 if (exv.getCharBox() != nullptr) 221 return fir::factory::CharacterExprHelper{builder, loc}.createTempFrom(exv); 222 if (fir::isDerivedWithLengthParameters(exv)) 223 TODO(loc, "copy derived type with length parameters"); 224 mlir::Type type = fir::unwrapPassByRefType(fir::getBase(exv).getType()); 225 fir::ExtendedValue temp = builder.createTemporary(loc, type); 226 fir::factory::genScalarAssignment(builder, loc, temp, exv); 227 return temp; 228 } 229 230 /// Is this a variable wrapped in parentheses? 231 template <typename A> 232 static bool isParenthesizedVariable(const A &) { 233 return false; 234 } 235 template <typename T> 236 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) { 237 using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u); 238 using Parentheses = Fortran::evaluate::Parentheses<T>; 239 if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) { 240 if (const auto *parentheses = std::get_if<Parentheses>(&expr.u)) 241 return Fortran::evaluate::IsVariable(parentheses->left()); 242 return false; 243 } else { 244 return std::visit([&](const auto &x) { return isParenthesizedVariable(x); }, 245 expr.u); 246 } 247 } 248 249 /// Generate a load of a value from an address. Beware that this will lose 250 /// any dynamic type information for polymorphic entities (note that unlimited 251 /// polymorphic cannot be loaded and must not be provided here). 252 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder, 253 mlir::Location loc, 254 const fir::ExtendedValue &addr) { 255 return addr.match( 256 [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; }, 257 [&](const fir::UnboxedValue &v) -> fir::ExtendedValue { 258 if (fir::unwrapRefType(fir::getBase(v).getType()) 259 .isa<fir::RecordType>()) 260 return v; 261 return builder.create<fir::LoadOp>(loc, fir::getBase(v)); 262 }, 263 [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue { 264 TODO(loc, "genLoad for MutableBoxValue"); 265 }, 266 [&](const fir::BoxValue &box) -> fir::ExtendedValue { 267 TODO(loc, "genLoad for BoxValue"); 268 }, 269 [&](const auto &) -> fir::ExtendedValue { 270 fir::emitFatalError( 271 loc, "attempting to load whole array or procedure address"); 272 }); 273 } 274 275 /// Create an optional dummy argument value from entity \p exv that may be 276 /// absent. This can only be called with numerical or logical scalar \p exv. 277 /// If \p exv is considered absent according to 15.5.2.12 point 1., the returned 278 /// value is zero (or false), otherwise it is the value of \p exv. 279 static fir::ExtendedValue genOptionalValue(fir::FirOpBuilder &builder, 280 mlir::Location loc, 281 const fir::ExtendedValue &exv, 282 mlir::Value isPresent) { 283 mlir::Type eleType = fir::getBaseTypeOf(exv); 284 assert(exv.rank() == 0 && fir::isa_trivial(eleType) && 285 "must be a numerical or logical scalar"); 286 return builder 287 .genIfOp(loc, {eleType}, isPresent, 288 /*withElseRegion=*/true) 289 .genThen([&]() { 290 mlir::Value val = fir::getBase(genLoad(builder, loc, exv)); 291 builder.create<fir::ResultOp>(loc, val); 292 }) 293 .genElse([&]() { 294 mlir::Value zero = fir::factory::createZeroValue(builder, loc, eleType); 295 builder.create<fir::ResultOp>(loc, zero); 296 }) 297 .getResults()[0]; 298 } 299 300 /// Create an optional dummy argument address from entity \p exv that may be 301 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the 302 /// returned value is a null pointer, otherwise it is the address of \p exv. 303 static fir::ExtendedValue genOptionalAddr(fir::FirOpBuilder &builder, 304 mlir::Location loc, 305 const fir::ExtendedValue &exv, 306 mlir::Value isPresent) { 307 // If it is an exv pointer/allocatable, then it cannot be absent 308 // because it is passed to a non-pointer/non-allocatable. 309 if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>()) 310 return fir::factory::genMutableBoxRead(builder, loc, *box); 311 // If this is not a POINTER or ALLOCATABLE, then it is already an OPTIONAL 312 // address and can be passed directly. 313 return exv; 314 } 315 316 /// Create an optional dummy argument address from entity \p exv that may be 317 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the 318 /// returned value is an absent fir.box, otherwise it is a fir.box describing \p 319 /// exv. 320 static fir::ExtendedValue genOptionalBox(fir::FirOpBuilder &builder, 321 mlir::Location loc, 322 const fir::ExtendedValue &exv, 323 mlir::Value isPresent) { 324 // Non allocatable/pointer optional box -> simply forward 325 if (exv.getBoxOf<fir::BoxValue>()) 326 return exv; 327 328 fir::ExtendedValue newExv = exv; 329 // Optional allocatable/pointer -> Cannot be absent, but need to translate 330 // unallocated/diassociated into absent fir.box. 331 if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>()) 332 newExv = fir::factory::genMutableBoxRead(builder, loc, *box); 333 334 // createBox will not do create any invalid memory dereferences if exv is 335 // absent. The created fir.box will not be usable, but the SelectOp below 336 // ensures it won't be. 337 mlir::Value box = builder.createBox(loc, newExv); 338 mlir::Type boxType = box.getType(); 339 auto absent = builder.create<fir::AbsentOp>(loc, boxType); 340 auto boxOrAbsent = builder.create<mlir::arith::SelectOp>( 341 loc, boxType, isPresent, box, absent); 342 return fir::BoxValue(boxOrAbsent); 343 } 344 345 /// Is this a call to an elemental procedure with at least one array argument? 346 static bool 347 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) { 348 if (procRef.IsElemental()) 349 for (const std::optional<Fortran::evaluate::ActualArgument> &arg : 350 procRef.arguments()) 351 if (arg && arg->Rank() != 0) 352 return true; 353 return false; 354 } 355 template <typename T> 356 static bool isElementalProcWithArrayArgs(const Fortran::evaluate::Expr<T> &) { 357 return false; 358 } 359 template <> 360 bool isElementalProcWithArrayArgs(const Fortran::lower::SomeExpr &x) { 361 if (const auto *procRef = std::get_if<Fortran::evaluate::ProcedureRef>(&x.u)) 362 return isElementalProcWithArrayArgs(*procRef); 363 return false; 364 } 365 366 /// Some auxiliary data for processing initialization in ScalarExprLowering 367 /// below. This is currently used for generating dense attributed global 368 /// arrays. 369 struct InitializerData { 370 explicit InitializerData(bool getRawVals = false) : genRawVals{getRawVals} {} 371 llvm::SmallVector<mlir::Attribute> rawVals; // initialization raw values 372 mlir::Type rawType; // Type of elements processed for rawVals vector. 373 bool genRawVals; // generate the rawVals vector if set. 374 }; 375 376 /// If \p arg is the address of a function with a denoted host-association tuple 377 /// argument, then return the host-associations tuple value of the current 378 /// procedure. Otherwise, return nullptr. 379 static mlir::Value 380 argumentHostAssocs(Fortran::lower::AbstractConverter &converter, 381 mlir::Value arg) { 382 if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) { 383 auto &builder = converter.getFirOpBuilder(); 384 if (auto funcOp = builder.getNamedFunction(addr.getSymbol())) 385 if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName())) 386 return converter.hostAssocTupleValue(); 387 } 388 return {}; 389 } 390 391 namespace { 392 393 /// Lowering of Fortran::evaluate::Expr<T> expressions 394 class ScalarExprLowering { 395 public: 396 using ExtValue = fir::ExtendedValue; 397 398 explicit ScalarExprLowering(mlir::Location loc, 399 Fortran::lower::AbstractConverter &converter, 400 Fortran::lower::SymMap &symMap, 401 Fortran::lower::StatementContext &stmtCtx, 402 InitializerData *initializer = nullptr) 403 : location{loc}, converter{converter}, 404 builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap}, 405 inInitializer{initializer} {} 406 407 ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) { 408 return gen(expr); 409 } 410 411 /// Lower `expr` to be passed as a fir.box argument. Do not create a temp 412 /// for the expr if it is a variable that can be described as a fir.box. 413 ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) { 414 bool saveUseBoxArg = useBoxArg; 415 useBoxArg = true; 416 ExtValue result = gen(expr); 417 useBoxArg = saveUseBoxArg; 418 return result; 419 } 420 421 ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) { 422 return genval(expr); 423 } 424 425 /// Lower an expression that is a pointer or an allocatable to a 426 /// MutableBoxValue. 427 fir::MutableBoxValue 428 genMutableBoxValue(const Fortran::lower::SomeExpr &expr) { 429 // Pointers and allocatables can only be: 430 // - a simple designator "x" 431 // - a component designator "a%b(i,j)%x" 432 // - a function reference "foo()" 433 // - result of NULL() or NULL(MOLD) intrinsic. 434 // NULL() requires some context to be lowered, so it is not handled 435 // here and must be lowered according to the context where it appears. 436 ExtValue exv = std::visit( 437 [&](const auto &x) { return genMutableBoxValueImpl(x); }, expr.u); 438 const fir::MutableBoxValue *mutableBox = 439 exv.getBoxOf<fir::MutableBoxValue>(); 440 if (!mutableBox) 441 fir::emitFatalError(getLoc(), "expr was not lowered to MutableBoxValue"); 442 return *mutableBox; 443 } 444 445 template <typename T> 446 ExtValue genMutableBoxValueImpl(const T &) { 447 // NULL() case should not be handled here. 448 fir::emitFatalError(getLoc(), "NULL() must be lowered in its context"); 449 } 450 451 template <typename T> 452 ExtValue 453 genMutableBoxValueImpl(const Fortran::evaluate::FunctionRef<T> &funRef) { 454 return genRawProcedureRef(funRef, converter.genType(toEvExpr(funRef))); 455 } 456 457 template <typename T> 458 ExtValue 459 genMutableBoxValueImpl(const Fortran::evaluate::Designator<T> &designator) { 460 return std::visit( 461 Fortran::common::visitors{ 462 [&](const Fortran::evaluate::SymbolRef &sym) -> ExtValue { 463 return symMap.lookupSymbol(*sym).toExtendedValue(); 464 }, 465 [&](const Fortran::evaluate::Component &comp) -> ExtValue { 466 return genComponent(comp); 467 }, 468 [&](const auto &) -> ExtValue { 469 fir::emitFatalError(getLoc(), 470 "not an allocatable or pointer designator"); 471 }}, 472 designator.u); 473 } 474 475 template <typename T> 476 ExtValue genMutableBoxValueImpl(const Fortran::evaluate::Expr<T> &expr) { 477 return std::visit([&](const auto &x) { return genMutableBoxValueImpl(x); }, 478 expr.u); 479 } 480 481 mlir::Location getLoc() { return location; } 482 483 template <typename A> 484 mlir::Value genunbox(const A &expr) { 485 ExtValue e = genval(expr); 486 if (const fir::UnboxedValue *r = e.getUnboxed()) 487 return *r; 488 fir::emitFatalError(getLoc(), "unboxed expression expected"); 489 } 490 491 /// Generate an integral constant of `value` 492 template <int KIND> 493 mlir::Value genIntegerConstant(mlir::MLIRContext *context, 494 std::int64_t value) { 495 mlir::Type type = 496 converter.genType(Fortran::common::TypeCategory::Integer, KIND); 497 return builder.createIntegerConstant(getLoc(), type, value); 498 } 499 500 /// Generate a logical/boolean constant of `value` 501 mlir::Value genBoolConstant(bool value) { 502 return builder.createBool(getLoc(), value); 503 } 504 505 /// Generate a real constant with a value `value`. 506 template <int KIND> 507 mlir::Value genRealConstant(mlir::MLIRContext *context, 508 const llvm::APFloat &value) { 509 mlir::Type fltTy = Fortran::lower::convertReal(context, KIND); 510 return builder.createRealConstant(getLoc(), fltTy, value); 511 } 512 513 template <typename OpTy> 514 mlir::Value createCompareOp(mlir::arith::CmpIPredicate pred, 515 const ExtValue &left, const ExtValue &right) { 516 if (const fir::UnboxedValue *lhs = left.getUnboxed()) 517 if (const fir::UnboxedValue *rhs = right.getUnboxed()) 518 return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs); 519 fir::emitFatalError(getLoc(), "array compare should be handled in genarr"); 520 } 521 template <typename OpTy, typename A> 522 mlir::Value createCompareOp(const A &ex, mlir::arith::CmpIPredicate pred) { 523 ExtValue left = genval(ex.left()); 524 return createCompareOp<OpTy>(pred, left, genval(ex.right())); 525 } 526 527 template <typename OpTy> 528 mlir::Value createFltCmpOp(mlir::arith::CmpFPredicate pred, 529 const ExtValue &left, const ExtValue &right) { 530 if (const fir::UnboxedValue *lhs = left.getUnboxed()) 531 if (const fir::UnboxedValue *rhs = right.getUnboxed()) 532 return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs); 533 fir::emitFatalError(getLoc(), "array compare should be handled in genarr"); 534 } 535 template <typename OpTy, typename A> 536 mlir::Value createFltCmpOp(const A &ex, mlir::arith::CmpFPredicate pred) { 537 ExtValue left = genval(ex.left()); 538 return createFltCmpOp<OpTy>(pred, left, genval(ex.right())); 539 } 540 541 /// Returns a reference to a symbol or its box/boxChar descriptor if it has 542 /// one. 543 ExtValue gen(Fortran::semantics::SymbolRef sym) { 544 if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym)) 545 return val.match( 546 [&](const Fortran::lower::SymbolBox::PointerOrAllocatable &boxAddr) { 547 return fir::factory::genMutableBoxRead(builder, getLoc(), boxAddr); 548 }, 549 [&val](auto &) { return val.toExtendedValue(); }); 550 LLVM_DEBUG(llvm::dbgs() 551 << "unknown symbol: " << sym << "\nmap: " << symMap << '\n'); 552 fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value"); 553 } 554 555 ExtValue genLoad(const ExtValue &exv) { 556 return ::genLoad(builder, getLoc(), exv); 557 } 558 559 ExtValue genval(Fortran::semantics::SymbolRef sym) { 560 ExtValue var = gen(sym); 561 if (const fir::UnboxedValue *s = var.getUnboxed()) 562 if (fir::isReferenceLike(s->getType())) 563 return genLoad(*s); 564 return var; 565 } 566 567 ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) { 568 TODO(getLoc(), "genval BOZ"); 569 } 570 571 /// Return indirection to function designated in ProcedureDesignator. 572 /// The type of the function indirection is not guaranteed to match the one 573 /// of the ProcedureDesignator due to Fortran implicit typing rules. 574 ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) { 575 TODO(getLoc(), "genval ProcedureDesignator"); 576 } 577 578 ExtValue genval(const Fortran::evaluate::NullPointer &) { 579 TODO(getLoc(), "genval NullPointer"); 580 } 581 582 ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) { 583 TODO(getLoc(), "genval StructureConstructor"); 584 } 585 586 /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol. 587 ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) { 588 return converter.impliedDoBinding(toStringRef(var.name)); 589 } 590 591 ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) { 592 ExtValue exv = desc.base().IsSymbol() ? gen(desc.base().GetLastSymbol()) 593 : gen(desc.base().GetComponent()); 594 mlir::IndexType idxTy = builder.getIndexType(); 595 mlir::Location loc = getLoc(); 596 auto castResult = [&](mlir::Value v) { 597 using ResTy = Fortran::evaluate::DescriptorInquiry::Result; 598 return builder.createConvert( 599 loc, converter.genType(ResTy::category, ResTy::kind), v); 600 }; 601 switch (desc.field()) { 602 case Fortran::evaluate::DescriptorInquiry::Field::Len: 603 return castResult(fir::factory::readCharLen(builder, loc, exv)); 604 case Fortran::evaluate::DescriptorInquiry::Field::LowerBound: 605 return castResult(fir::factory::readLowerBound( 606 builder, loc, exv, desc.dimension(), 607 builder.createIntegerConstant(loc, idxTy, 1))); 608 case Fortran::evaluate::DescriptorInquiry::Field::Extent: 609 return castResult( 610 fir::factory::readExtent(builder, loc, exv, desc.dimension())); 611 case Fortran::evaluate::DescriptorInquiry::Field::Rank: 612 TODO(loc, "rank inquiry on assumed rank"); 613 case Fortran::evaluate::DescriptorInquiry::Field::Stride: 614 // So far the front end does not generate this inquiry. 615 TODO(loc, "Stride inquiry"); 616 } 617 llvm_unreachable("unknown descriptor inquiry"); 618 } 619 620 ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) { 621 TODO(getLoc(), "genval TypeParamInquiry"); 622 } 623 624 template <int KIND> 625 ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) { 626 TODO(getLoc(), "genval ComplexComponent"); 627 } 628 629 template <int KIND> 630 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 631 Fortran::common::TypeCategory::Integer, KIND>> &op) { 632 mlir::Value input = genunbox(op.left()); 633 // Like LLVM, integer negation is the binary op "0 - value" 634 mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0); 635 return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input); 636 } 637 638 template <int KIND> 639 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 640 Fortran::common::TypeCategory::Real, KIND>> &op) { 641 return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left())); 642 } 643 template <int KIND> 644 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 645 Fortran::common::TypeCategory::Complex, KIND>> &op) { 646 return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left())); 647 } 648 649 template <typename OpTy> 650 mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) { 651 assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right)); 652 mlir::Value lhs = fir::getBase(left); 653 mlir::Value rhs = fir::getBase(right); 654 assert(lhs.getType() == rhs.getType() && "types must be the same"); 655 return builder.create<OpTy>(getLoc(), lhs, rhs); 656 } 657 658 template <typename OpTy, typename A> 659 mlir::Value createBinaryOp(const A &ex) { 660 ExtValue left = genval(ex.left()); 661 return createBinaryOp<OpTy>(left, genval(ex.right())); 662 } 663 664 #undef GENBIN 665 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \ 666 template <int KIND> \ 667 ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \ 668 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \ 669 return createBinaryOp<GenBinFirOp>(x); \ 670 } 671 672 GENBIN(Add, Integer, mlir::arith::AddIOp) 673 GENBIN(Add, Real, mlir::arith::AddFOp) 674 GENBIN(Add, Complex, fir::AddcOp) 675 GENBIN(Subtract, Integer, mlir::arith::SubIOp) 676 GENBIN(Subtract, Real, mlir::arith::SubFOp) 677 GENBIN(Subtract, Complex, fir::SubcOp) 678 GENBIN(Multiply, Integer, mlir::arith::MulIOp) 679 GENBIN(Multiply, Real, mlir::arith::MulFOp) 680 GENBIN(Multiply, Complex, fir::MulcOp) 681 GENBIN(Divide, Integer, mlir::arith::DivSIOp) 682 GENBIN(Divide, Real, mlir::arith::DivFOp) 683 GENBIN(Divide, Complex, fir::DivcOp) 684 685 template <Fortran::common::TypeCategory TC, int KIND> 686 ExtValue genval( 687 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) { 688 mlir::Type ty = converter.genType(TC, KIND); 689 mlir::Value lhs = genunbox(op.left()); 690 mlir::Value rhs = genunbox(op.right()); 691 return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs); 692 } 693 694 template <Fortran::common::TypeCategory TC, int KIND> 695 ExtValue genval( 696 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>> 697 &op) { 698 mlir::Type ty = converter.genType(TC, KIND); 699 mlir::Value lhs = genunbox(op.left()); 700 mlir::Value rhs = genunbox(op.right()); 701 return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs); 702 } 703 704 template <int KIND> 705 ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) { 706 mlir::Value realPartValue = genunbox(op.left()); 707 return fir::factory::Complex{builder, getLoc()}.createComplex( 708 KIND, realPartValue, genunbox(op.right())); 709 } 710 711 template <int KIND> 712 ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) { 713 TODO(getLoc(), "genval Concat<KIND>"); 714 } 715 716 /// MIN and MAX operations 717 template <Fortran::common::TypeCategory TC, int KIND> 718 ExtValue 719 genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> 720 &op) { 721 TODO(getLoc(), "genval Extremum<TC, KIND>"); 722 } 723 724 template <int KIND> 725 ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) { 726 TODO(getLoc(), "genval SetLength<KIND>"); 727 } 728 729 template <int KIND> 730 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 731 Fortran::common::TypeCategory::Integer, KIND>> &op) { 732 return createCompareOp<mlir::arith::CmpIOp>(op, 733 translateRelational(op.opr)); 734 } 735 template <int KIND> 736 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 737 Fortran::common::TypeCategory::Real, KIND>> &op) { 738 return createFltCmpOp<mlir::arith::CmpFOp>( 739 op, translateFloatRelational(op.opr)); 740 } 741 template <int KIND> 742 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 743 Fortran::common::TypeCategory::Complex, KIND>> &op) { 744 TODO(getLoc(), "genval complex comparison"); 745 } 746 template <int KIND> 747 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 748 Fortran::common::TypeCategory::Character, KIND>> &op) { 749 TODO(getLoc(), "genval char comparison"); 750 } 751 752 ExtValue 753 genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) { 754 return std::visit([&](const auto &x) { return genval(x); }, op.u); 755 } 756 757 template <Fortran::common::TypeCategory TC1, int KIND, 758 Fortran::common::TypeCategory TC2> 759 ExtValue 760 genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, 761 TC2> &convert) { 762 mlir::Type ty = converter.genType(TC1, KIND); 763 mlir::Value operand = genunbox(convert.left()); 764 return builder.convertWithSemantics(getLoc(), ty, operand); 765 } 766 767 template <typename A> 768 ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) { 769 TODO(getLoc(), "genval parentheses<A>"); 770 } 771 772 template <int KIND> 773 ExtValue genval(const Fortran::evaluate::Not<KIND> &op) { 774 mlir::Value logical = genunbox(op.left()); 775 mlir::Value one = genBoolConstant(true); 776 mlir::Value val = 777 builder.createConvert(getLoc(), builder.getI1Type(), logical); 778 return builder.create<mlir::arith::XOrIOp>(getLoc(), val, one); 779 } 780 781 template <int KIND> 782 ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) { 783 mlir::IntegerType i1Type = builder.getI1Type(); 784 mlir::Value slhs = genunbox(op.left()); 785 mlir::Value srhs = genunbox(op.right()); 786 mlir::Value lhs = builder.createConvert(getLoc(), i1Type, slhs); 787 mlir::Value rhs = builder.createConvert(getLoc(), i1Type, srhs); 788 switch (op.logicalOperator) { 789 case Fortran::evaluate::LogicalOperator::And: 790 return createBinaryOp<mlir::arith::AndIOp>(lhs, rhs); 791 case Fortran::evaluate::LogicalOperator::Or: 792 return createBinaryOp<mlir::arith::OrIOp>(lhs, rhs); 793 case Fortran::evaluate::LogicalOperator::Eqv: 794 return createCompareOp<mlir::arith::CmpIOp>( 795 mlir::arith::CmpIPredicate::eq, lhs, rhs); 796 case Fortran::evaluate::LogicalOperator::Neqv: 797 return createCompareOp<mlir::arith::CmpIOp>( 798 mlir::arith::CmpIPredicate::ne, lhs, rhs); 799 case Fortran::evaluate::LogicalOperator::Not: 800 // lib/evaluate expression for .NOT. is Fortran::evaluate::Not<KIND>. 801 llvm_unreachable(".NOT. is not a binary operator"); 802 } 803 llvm_unreachable("unhandled logical operation"); 804 } 805 806 /// Convert a scalar literal constant to IR. 807 template <Fortran::common::TypeCategory TC, int KIND> 808 ExtValue genScalarLit( 809 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> 810 &value) { 811 if constexpr (TC == Fortran::common::TypeCategory::Integer) { 812 return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64()); 813 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) { 814 return genBoolConstant(value.IsTrue()); 815 } else if constexpr (TC == Fortran::common::TypeCategory::Real) { 816 std::string str = value.DumpHexadecimal(); 817 if constexpr (KIND == 2) { 818 llvm::APFloat floatVal{llvm::APFloatBase::IEEEhalf(), str}; 819 return genRealConstant<KIND>(builder.getContext(), floatVal); 820 } else if constexpr (KIND == 3) { 821 llvm::APFloat floatVal{llvm::APFloatBase::BFloat(), str}; 822 return genRealConstant<KIND>(builder.getContext(), floatVal); 823 } else if constexpr (KIND == 4) { 824 llvm::APFloat floatVal{llvm::APFloatBase::IEEEsingle(), str}; 825 return genRealConstant<KIND>(builder.getContext(), floatVal); 826 } else if constexpr (KIND == 10) { 827 llvm::APFloat floatVal{llvm::APFloatBase::x87DoubleExtended(), str}; 828 return genRealConstant<KIND>(builder.getContext(), floatVal); 829 } else if constexpr (KIND == 16) { 830 llvm::APFloat floatVal{llvm::APFloatBase::IEEEquad(), str}; 831 return genRealConstant<KIND>(builder.getContext(), floatVal); 832 } else { 833 // convert everything else to double 834 llvm::APFloat floatVal{llvm::APFloatBase::IEEEdouble(), str}; 835 return genRealConstant<KIND>(builder.getContext(), floatVal); 836 } 837 } else if constexpr (TC == Fortran::common::TypeCategory::Complex) { 838 using TR = 839 Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>; 840 Fortran::evaluate::ComplexConstructor<KIND> ctor( 841 Fortran::evaluate::Expr<TR>{ 842 Fortran::evaluate::Constant<TR>{value.REAL()}}, 843 Fortran::evaluate::Expr<TR>{ 844 Fortran::evaluate::Constant<TR>{value.AIMAG()}}); 845 return genunbox(ctor); 846 } else /*constexpr*/ { 847 llvm_unreachable("unhandled constant"); 848 } 849 } 850 851 /// Convert a ascii scalar literal CHARACTER to IR. (specialization) 852 ExtValue 853 genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type< 854 Fortran::common::TypeCategory::Character, 1>> &value, 855 int64_t len) { 856 assert(value.size() == static_cast<std::uint64_t>(len) && 857 "value.size() doesn't match with len"); 858 return fir::factory::createStringLiteral(builder, getLoc(), value); 859 } 860 861 template <Fortran::common::TypeCategory TC, int KIND> 862 ExtValue 863 genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>> 864 &con) { 865 if (con.Rank() > 0) 866 TODO(getLoc(), "genval array constant"); 867 std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>> 868 opt = con.GetScalarValue(); 869 assert(opt.has_value() && "constant has no value"); 870 if constexpr (TC == Fortran::common::TypeCategory::Character) { 871 if constexpr (KIND == 1) 872 return genAsciiScalarLit(opt.value(), con.LEN()); 873 TODO(getLoc(), "genval for Character with KIND != 1"); 874 } else { 875 return genScalarLit<TC, KIND>(opt.value()); 876 } 877 } 878 879 fir::ExtendedValue genval( 880 const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) { 881 TODO(getLoc(), "genval constant derived"); 882 } 883 884 template <typename A> 885 ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) { 886 TODO(getLoc(), "genval ArrayConstructor<A>"); 887 } 888 889 ExtValue gen(const Fortran::evaluate::ComplexPart &x) { 890 TODO(getLoc(), "gen ComplexPart"); 891 } 892 ExtValue genval(const Fortran::evaluate::ComplexPart &x) { 893 TODO(getLoc(), "genval ComplexPart"); 894 } 895 896 ExtValue gen(const Fortran::evaluate::Substring &s) { 897 TODO(getLoc(), "gen Substring"); 898 } 899 ExtValue genval(const Fortran::evaluate::Substring &ss) { 900 TODO(getLoc(), "genval Substring"); 901 } 902 903 ExtValue genval(const Fortran::evaluate::Subscript &subs) { 904 if (auto *s = std::get_if<Fortran::evaluate::IndirectSubscriptIntegerExpr>( 905 &subs.u)) { 906 if (s->value().Rank() > 0) 907 fir::emitFatalError(getLoc(), "vector subscript is not scalar"); 908 return {genval(s->value())}; 909 } 910 fir::emitFatalError(getLoc(), "subscript triple notation is not scalar"); 911 } 912 913 ExtValue genSubscript(const Fortran::evaluate::Subscript &subs) { 914 return genval(subs); 915 } 916 917 ExtValue gen(const Fortran::evaluate::DataRef &dref) { 918 TODO(getLoc(), "gen DataRef"); 919 } 920 ExtValue genval(const Fortran::evaluate::DataRef &dref) { 921 TODO(getLoc(), "genval DataRef"); 922 } 923 924 // Helper function to turn the Component structure into a list of nested 925 // components, ordered from largest/leftmost to smallest/rightmost: 926 // - where only the smallest/rightmost item may be allocatable or a pointer 927 // (nested allocatable/pointer components require nested coordinate_of ops) 928 // - that does not contain any parent components 929 // (the front end places parent components directly in the object) 930 // Return the object used as the base coordinate for the component chain. 931 static Fortran::evaluate::DataRef const * 932 reverseComponents(const Fortran::evaluate::Component &cmpt, 933 std::list<const Fortran::evaluate::Component *> &list) { 934 if (!cmpt.GetLastSymbol().test( 935 Fortran::semantics::Symbol::Flag::ParentComp)) 936 list.push_front(&cmpt); 937 return std::visit( 938 Fortran::common::visitors{ 939 [&](const Fortran::evaluate::Component &x) { 940 if (Fortran::semantics::IsAllocatableOrPointer(x.GetLastSymbol())) 941 return &cmpt.base(); 942 return reverseComponents(x, list); 943 }, 944 [&](auto &) { return &cmpt.base(); }, 945 }, 946 cmpt.base().u); 947 } 948 949 // Return the coordinate of the component reference 950 ExtValue genComponent(const Fortran::evaluate::Component &cmpt) { 951 std::list<const Fortran::evaluate::Component *> list; 952 const Fortran::evaluate::DataRef *base = reverseComponents(cmpt, list); 953 llvm::SmallVector<mlir::Value> coorArgs; 954 ExtValue obj = gen(*base); 955 mlir::Type ty = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(obj).getType()); 956 mlir::Location loc = getLoc(); 957 auto fldTy = fir::FieldType::get(&converter.getMLIRContext()); 958 // FIXME: need to thread the LEN type parameters here. 959 for (const Fortran::evaluate::Component *field : list) { 960 auto recTy = ty.cast<fir::RecordType>(); 961 const Fortran::semantics::Symbol &sym = field->GetLastSymbol(); 962 llvm::StringRef name = toStringRef(sym.name()); 963 coorArgs.push_back(builder.create<fir::FieldIndexOp>( 964 loc, fldTy, name, recTy, fir::getTypeParams(obj))); 965 ty = recTy.getType(name); 966 } 967 ty = builder.getRefType(ty); 968 return fir::factory::componentToExtendedValue( 969 builder, loc, 970 builder.create<fir::CoordinateOp>(loc, ty, fir::getBase(obj), 971 coorArgs)); 972 } 973 974 ExtValue gen(const Fortran::evaluate::Component &cmpt) { 975 TODO(getLoc(), "gen Component"); 976 } 977 ExtValue genval(const Fortran::evaluate::Component &cmpt) { 978 TODO(getLoc(), "genval Component"); 979 } 980 981 ExtValue genval(const Fortran::semantics::Bound &bound) { 982 TODO(getLoc(), "genval Bound"); 983 } 984 985 /// Return lower bounds of \p box in dimension \p dim. The returned value 986 /// has type \ty. 987 mlir::Value getLBound(const ExtValue &box, unsigned dim, mlir::Type ty) { 988 assert(box.rank() > 0 && "must be an array"); 989 mlir::Location loc = getLoc(); 990 mlir::Value one = builder.createIntegerConstant(loc, ty, 1); 991 mlir::Value lb = fir::factory::readLowerBound(builder, loc, box, dim, one); 992 return builder.createConvert(loc, ty, lb); 993 } 994 995 static bool isSlice(const Fortran::evaluate::ArrayRef &aref) { 996 for (const Fortran::evaluate::Subscript &sub : aref.subscript()) 997 if (std::holds_alternative<Fortran::evaluate::Triplet>(sub.u)) 998 return true; 999 return false; 1000 } 1001 1002 /// Lower an ArrayRef to a fir.coordinate_of given its lowered base. 1003 ExtValue genCoordinateOp(const ExtValue &array, 1004 const Fortran::evaluate::ArrayRef &aref) { 1005 mlir::Location loc = getLoc(); 1006 // References to array of rank > 1 with non constant shape that are not 1007 // fir.box must be collapsed into an offset computation in lowering already. 1008 // The same is needed with dynamic length character arrays of all ranks. 1009 mlir::Type baseType = 1010 fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(array).getType()); 1011 if ((array.rank() > 1 && fir::hasDynamicSize(baseType)) || 1012 fir::characterWithDynamicLen(fir::unwrapSequenceType(baseType))) 1013 if (!array.getBoxOf<fir::BoxValue>()) 1014 return genOffsetAndCoordinateOp(array, aref); 1015 // Generate a fir.coordinate_of with zero based array indexes. 1016 llvm::SmallVector<mlir::Value> args; 1017 for (const auto &subsc : llvm::enumerate(aref.subscript())) { 1018 ExtValue subVal = genSubscript(subsc.value()); 1019 assert(fir::isUnboxedValue(subVal) && "subscript must be simple scalar"); 1020 mlir::Value val = fir::getBase(subVal); 1021 mlir::Type ty = val.getType(); 1022 mlir::Value lb = getLBound(array, subsc.index(), ty); 1023 args.push_back(builder.create<mlir::arith::SubIOp>(loc, ty, val, lb)); 1024 } 1025 1026 mlir::Value base = fir::getBase(array); 1027 auto seqTy = 1028 fir::dyn_cast_ptrOrBoxEleTy(base.getType()).cast<fir::SequenceType>(); 1029 assert(args.size() == seqTy.getDimension()); 1030 mlir::Type ty = builder.getRefType(seqTy.getEleTy()); 1031 auto addr = builder.create<fir::CoordinateOp>(loc, ty, base, args); 1032 return fir::factory::arrayElementToExtendedValue(builder, loc, array, addr); 1033 } 1034 1035 /// Lower an ArrayRef to a fir.coordinate_of using an element offset instead 1036 /// of array indexes. 1037 /// This generates offset computation from the indexes and length parameters, 1038 /// and use the offset to access the element with a fir.coordinate_of. This 1039 /// must only be used if it is not possible to generate a normal 1040 /// fir.coordinate_of using array indexes (i.e. when the shape information is 1041 /// unavailable in the IR). 1042 ExtValue genOffsetAndCoordinateOp(const ExtValue &array, 1043 const Fortran::evaluate::ArrayRef &aref) { 1044 mlir::Location loc = getLoc(); 1045 mlir::Value addr = fir::getBase(array); 1046 mlir::Type arrTy = fir::dyn_cast_ptrEleTy(addr.getType()); 1047 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy(); 1048 mlir::Type seqTy = builder.getRefType(builder.getVarLenSeqTy(eleTy)); 1049 mlir::Type refTy = builder.getRefType(eleTy); 1050 mlir::Value base = builder.createConvert(loc, seqTy, addr); 1051 mlir::IndexType idxTy = builder.getIndexType(); 1052 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); 1053 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); 1054 auto getLB = [&](const auto &arr, unsigned dim) -> mlir::Value { 1055 return arr.getLBounds().empty() ? one : arr.getLBounds()[dim]; 1056 }; 1057 auto genFullDim = [&](const auto &arr, mlir::Value delta) -> mlir::Value { 1058 mlir::Value total = zero; 1059 assert(arr.getExtents().size() == aref.subscript().size()); 1060 delta = builder.createConvert(loc, idxTy, delta); 1061 unsigned dim = 0; 1062 for (auto [ext, sub] : llvm::zip(arr.getExtents(), aref.subscript())) { 1063 ExtValue subVal = genSubscript(sub); 1064 assert(fir::isUnboxedValue(subVal)); 1065 mlir::Value val = 1066 builder.createConvert(loc, idxTy, fir::getBase(subVal)); 1067 mlir::Value lb = builder.createConvert(loc, idxTy, getLB(arr, dim)); 1068 mlir::Value diff = builder.create<mlir::arith::SubIOp>(loc, val, lb); 1069 mlir::Value prod = 1070 builder.create<mlir::arith::MulIOp>(loc, delta, diff); 1071 total = builder.create<mlir::arith::AddIOp>(loc, prod, total); 1072 if (ext) 1073 delta = builder.create<mlir::arith::MulIOp>(loc, delta, ext); 1074 ++dim; 1075 } 1076 mlir::Type origRefTy = refTy; 1077 if (fir::factory::CharacterExprHelper::isCharacterScalar(refTy)) { 1078 fir::CharacterType chTy = 1079 fir::factory::CharacterExprHelper::getCharacterType(refTy); 1080 if (fir::characterWithDynamicLen(chTy)) { 1081 mlir::MLIRContext *ctx = builder.getContext(); 1082 fir::KindTy kind = 1083 fir::factory::CharacterExprHelper::getCharacterKind(chTy); 1084 fir::CharacterType singleTy = 1085 fir::CharacterType::getSingleton(ctx, kind); 1086 refTy = builder.getRefType(singleTy); 1087 mlir::Type seqRefTy = 1088 builder.getRefType(builder.getVarLenSeqTy(singleTy)); 1089 base = builder.createConvert(loc, seqRefTy, base); 1090 } 1091 } 1092 auto coor = builder.create<fir::CoordinateOp>( 1093 loc, refTy, base, llvm::ArrayRef<mlir::Value>{total}); 1094 // Convert to expected, original type after address arithmetic. 1095 return builder.createConvert(loc, origRefTy, coor); 1096 }; 1097 return array.match( 1098 [&](const fir::ArrayBoxValue &arr) -> ExtValue { 1099 // FIXME: this check can be removed when slicing is implemented 1100 if (isSlice(aref)) 1101 fir::emitFatalError( 1102 getLoc(), 1103 "slice should be handled in array expression context"); 1104 return genFullDim(arr, one); 1105 }, 1106 [&](const fir::CharArrayBoxValue &arr) -> ExtValue { 1107 mlir::Value delta = arr.getLen(); 1108 // If the length is known in the type, fir.coordinate_of will 1109 // already take the length into account. 1110 if (fir::factory::CharacterExprHelper::hasConstantLengthInType(arr)) 1111 delta = one; 1112 return fir::CharBoxValue(genFullDim(arr, delta), arr.getLen()); 1113 }, 1114 [&](const fir::BoxValue &arr) -> ExtValue { 1115 // CoordinateOp for BoxValue is not generated here. The dimensions 1116 // must be kept in the fir.coordinate_op so that potential fir.box 1117 // strides can be applied by codegen. 1118 fir::emitFatalError( 1119 loc, "internal: BoxValue in dim-collapsed fir.coordinate_of"); 1120 }, 1121 [&](const auto &) -> ExtValue { 1122 fir::emitFatalError(loc, "internal: array lowering failed"); 1123 }); 1124 } 1125 1126 ExtValue gen(const Fortran::evaluate::ArrayRef &aref) { 1127 ExtValue base = aref.base().IsSymbol() ? gen(aref.base().GetFirstSymbol()) 1128 : gen(aref.base().GetComponent()); 1129 return genCoordinateOp(base, aref); 1130 } 1131 ExtValue genval(const Fortran::evaluate::ArrayRef &aref) { 1132 return genLoad(gen(aref)); 1133 } 1134 1135 ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) { 1136 TODO(getLoc(), "gen CoarrayRef"); 1137 } 1138 ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) { 1139 TODO(getLoc(), "genval CoarrayRef"); 1140 } 1141 1142 template <typename A> 1143 ExtValue gen(const Fortran::evaluate::Designator<A> &des) { 1144 return std::visit([&](const auto &x) { return gen(x); }, des.u); 1145 } 1146 template <typename A> 1147 ExtValue genval(const Fortran::evaluate::Designator<A> &des) { 1148 return std::visit([&](const auto &x) { return genval(x); }, des.u); 1149 } 1150 1151 mlir::Type genType(const Fortran::evaluate::DynamicType &dt) { 1152 if (dt.category() != Fortran::common::TypeCategory::Derived) 1153 return converter.genType(dt.category(), dt.kind()); 1154 TODO(getLoc(), "genType Derived Type"); 1155 } 1156 1157 /// Lower a function reference 1158 template <typename A> 1159 ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) { 1160 if (!funcRef.GetType().has_value()) 1161 fir::emitFatalError(getLoc(), "internal: a function must have a type"); 1162 mlir::Type resTy = genType(*funcRef.GetType()); 1163 return genProcedureRef(funcRef, {resTy}); 1164 } 1165 1166 /// Lower function call `funcRef` and return a reference to the resultant 1167 /// value. This is required for lowering expressions such as `f1(f2(v))`. 1168 template <typename A> 1169 ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) { 1170 ExtValue retVal = genFunctionRef(funcRef); 1171 mlir::Value retValBase = fir::getBase(retVal); 1172 if (fir::conformsWithPassByRef(retValBase.getType())) 1173 return retVal; 1174 auto mem = builder.create<fir::AllocaOp>(getLoc(), retValBase.getType()); 1175 builder.create<fir::StoreOp>(getLoc(), retValBase, mem); 1176 return fir::substBase(retVal, mem.getResult()); 1177 } 1178 1179 /// helper to detect statement functions 1180 static bool 1181 isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) { 1182 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol()) 1183 if (const auto *details = 1184 symbol->detailsIf<Fortran::semantics::SubprogramDetails>()) 1185 return details->stmtFunction().has_value(); 1186 return false; 1187 } 1188 1189 /// Helper to package a Value and its properties into an ExtendedValue. 1190 static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base, 1191 llvm::ArrayRef<mlir::Value> extents, 1192 llvm::ArrayRef<mlir::Value> lengths) { 1193 mlir::Type type = base.getType(); 1194 if (type.isa<fir::BoxType>()) 1195 return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents); 1196 type = fir::unwrapRefType(type); 1197 if (type.isa<fir::BoxType>()) 1198 return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {}); 1199 if (auto seqTy = type.dyn_cast<fir::SequenceType>()) { 1200 if (seqTy.getDimension() != extents.size()) 1201 fir::emitFatalError(loc, "incorrect number of extents for array"); 1202 if (seqTy.getEleTy().isa<fir::CharacterType>()) { 1203 if (lengths.empty()) 1204 fir::emitFatalError(loc, "missing length for character"); 1205 assert(lengths.size() == 1); 1206 return fir::CharArrayBoxValue(base, lengths[0], extents); 1207 } 1208 return fir::ArrayBoxValue(base, extents); 1209 } 1210 if (type.isa<fir::CharacterType>()) { 1211 if (lengths.empty()) 1212 fir::emitFatalError(loc, "missing length for character"); 1213 assert(lengths.size() == 1); 1214 return fir::CharBoxValue(base, lengths[0]); 1215 } 1216 return base; 1217 } 1218 1219 // Find the argument that corresponds to the host associations. 1220 // Verify some assumptions about how the signature was built here. 1221 [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::FuncOp fn) { 1222 // Scan the argument list from last to first as the host associations are 1223 // appended for now. 1224 for (unsigned i = fn.getNumArguments(); i > 0; --i) 1225 if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) { 1226 // Host assoc tuple must be last argument (for now). 1227 assert(i == fn.getNumArguments() && "tuple must be last"); 1228 return i - 1; 1229 } 1230 llvm_unreachable("anyFuncArgsHaveAttr failed"); 1231 } 1232 1233 /// Create a contiguous temporary array with the same shape, 1234 /// length parameters and type as mold. It is up to the caller to deallocate 1235 /// the temporary. 1236 ExtValue genArrayTempFromMold(const ExtValue &mold, 1237 llvm::StringRef tempName) { 1238 mlir::Type type = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(mold).getType()); 1239 assert(type && "expected descriptor or memory type"); 1240 mlir::Location loc = getLoc(); 1241 llvm::SmallVector<mlir::Value> extents = 1242 fir::factory::getExtents(builder, loc, mold); 1243 llvm::SmallVector<mlir::Value> allocMemTypeParams = 1244 fir::getTypeParams(mold); 1245 mlir::Value charLen; 1246 mlir::Type elementType = fir::unwrapSequenceType(type); 1247 if (auto charType = elementType.dyn_cast<fir::CharacterType>()) { 1248 charLen = allocMemTypeParams.empty() 1249 ? fir::factory::readCharLen(builder, loc, mold) 1250 : allocMemTypeParams[0]; 1251 if (charType.hasDynamicLen() && allocMemTypeParams.empty()) 1252 allocMemTypeParams.push_back(charLen); 1253 } else if (fir::hasDynamicSize(elementType)) { 1254 TODO(loc, "Creating temporary for derived type with length parameters"); 1255 } 1256 1257 mlir::Value temp = builder.create<fir::AllocMemOp>( 1258 loc, type, tempName, allocMemTypeParams, extents); 1259 if (fir::unwrapSequenceType(type).isa<fir::CharacterType>()) 1260 return fir::CharArrayBoxValue{temp, charLen, extents}; 1261 return fir::ArrayBoxValue{temp, extents}; 1262 } 1263 1264 /// Copy \p source array into \p dest array. Both arrays must be 1265 /// conforming, but neither array must be contiguous. 1266 void genArrayCopy(ExtValue dest, ExtValue source) { 1267 return createSomeArrayAssignment(converter, dest, source, symMap, stmtCtx); 1268 } 1269 1270 /// Lower a non-elemental procedure reference and read allocatable and pointer 1271 /// results into normal values. 1272 ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef, 1273 llvm::Optional<mlir::Type> resultType) { 1274 ExtValue res = genRawProcedureRef(procRef, resultType); 1275 return res; 1276 } 1277 1278 /// Given a call site for which the arguments were already lowered, generate 1279 /// the call and return the result. This function deals with explicit result 1280 /// allocation and lowering if needed. It also deals with passing the host 1281 /// link to internal procedures. 1282 ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller, 1283 mlir::FunctionType callSiteType, 1284 llvm::Optional<mlir::Type> resultType) { 1285 mlir::Location loc = getLoc(); 1286 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 1287 // Handle cases where caller must allocate the result or a fir.box for it. 1288 bool mustPopSymMap = false; 1289 if (caller.mustMapInterfaceSymbols()) { 1290 symMap.pushScope(); 1291 mustPopSymMap = true; 1292 Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap); 1293 } 1294 // If this is an indirect call, retrieve the function address. Also retrieve 1295 // the result length if this is a character function (note that this length 1296 // will be used only if there is no explicit length in the local interface). 1297 mlir::Value funcPointer; 1298 mlir::Value charFuncPointerLength; 1299 if (const Fortran::semantics::Symbol *sym = 1300 caller.getIfIndirectCallSymbol()) { 1301 funcPointer = symMap.lookupSymbol(*sym).getAddr(); 1302 if (!funcPointer) 1303 fir::emitFatalError(loc, "failed to find indirect call symbol address"); 1304 if (fir::isCharacterProcedureTuple(funcPointer.getType(), 1305 /*acceptRawFunc=*/false)) 1306 std::tie(funcPointer, charFuncPointerLength) = 1307 fir::factory::extractCharacterProcedureTuple(builder, loc, 1308 funcPointer); 1309 } 1310 1311 mlir::IndexType idxTy = builder.getIndexType(); 1312 auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value { 1313 return builder.createConvert( 1314 loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx))); 1315 }; 1316 llvm::SmallVector<mlir::Value> resultLengths; 1317 auto allocatedResult = [&]() -> llvm::Optional<ExtValue> { 1318 llvm::SmallVector<mlir::Value> extents; 1319 llvm::SmallVector<mlir::Value> lengths; 1320 if (!caller.callerAllocateResult()) 1321 return {}; 1322 mlir::Type type = caller.getResultStorageType(); 1323 if (type.isa<fir::SequenceType>()) 1324 caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) { 1325 extents.emplace_back(lowerSpecExpr(e)); 1326 }); 1327 caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) { 1328 lengths.emplace_back(lowerSpecExpr(e)); 1329 }); 1330 1331 // Result length parameters should not be provided to box storage 1332 // allocation and save_results, but they are still useful information to 1333 // keep in the ExtendedValue if non-deferred. 1334 if (!type.isa<fir::BoxType>()) { 1335 if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) { 1336 // Calling an assumed length function. This is only possible if this 1337 // is a call to a character dummy procedure. 1338 if (!charFuncPointerLength) 1339 fir::emitFatalError(loc, "failed to retrieve character function " 1340 "length while calling it"); 1341 lengths.push_back(charFuncPointerLength); 1342 } 1343 resultLengths = lengths; 1344 } 1345 1346 if (!extents.empty() || !lengths.empty()) { 1347 auto *bldr = &converter.getFirOpBuilder(); 1348 auto stackSaveFn = fir::factory::getLlvmStackSave(builder); 1349 auto stackSaveSymbol = bldr->getSymbolRefAttr(stackSaveFn.getName()); 1350 mlir::Value sp = 1351 bldr->create<fir::CallOp>(loc, stackSaveFn.getType().getResults(), 1352 stackSaveSymbol, mlir::ValueRange{}) 1353 .getResult(0); 1354 stmtCtx.attachCleanup([bldr, loc, sp]() { 1355 auto stackRestoreFn = fir::factory::getLlvmStackRestore(*bldr); 1356 auto stackRestoreSymbol = 1357 bldr->getSymbolRefAttr(stackRestoreFn.getName()); 1358 bldr->create<fir::CallOp>(loc, stackRestoreFn.getType().getResults(), 1359 stackRestoreSymbol, mlir::ValueRange{sp}); 1360 }); 1361 } 1362 mlir::Value temp = 1363 builder.createTemporary(loc, type, ".result", extents, resultLengths); 1364 return toExtendedValue(loc, temp, extents, lengths); 1365 }(); 1366 1367 if (mustPopSymMap) 1368 symMap.popScope(); 1369 1370 // Place allocated result or prepare the fir.save_result arguments. 1371 mlir::Value arrayResultShape; 1372 if (allocatedResult) { 1373 if (std::optional<Fortran::lower::CallInterface< 1374 Fortran::lower::CallerInterface>::PassedEntity> 1375 resultArg = caller.getPassedResult()) { 1376 if (resultArg->passBy == PassBy::AddressAndLength) 1377 caller.placeAddressAndLengthInput(*resultArg, 1378 fir::getBase(*allocatedResult), 1379 fir::getLen(*allocatedResult)); 1380 else if (resultArg->passBy == PassBy::BaseAddress) 1381 caller.placeInput(*resultArg, fir::getBase(*allocatedResult)); 1382 else 1383 fir::emitFatalError( 1384 loc, "only expect character scalar result to be passed by ref"); 1385 } else { 1386 assert(caller.mustSaveResult()); 1387 arrayResultShape = allocatedResult->match( 1388 [&](const fir::CharArrayBoxValue &) { 1389 return builder.createShape(loc, *allocatedResult); 1390 }, 1391 [&](const fir::ArrayBoxValue &) { 1392 return builder.createShape(loc, *allocatedResult); 1393 }, 1394 [&](const auto &) { return mlir::Value{}; }); 1395 } 1396 } 1397 1398 // In older Fortran, procedure argument types are inferred. This may lead 1399 // different view of what the function signature is in different locations. 1400 // Casts are inserted as needed below to accommodate this. 1401 1402 // The mlir::FuncOp type prevails, unless it has a different number of 1403 // arguments which can happen in legal program if it was passed as a dummy 1404 // procedure argument earlier with no further type information. 1405 mlir::SymbolRefAttr funcSymbolAttr; 1406 bool addHostAssociations = false; 1407 if (!funcPointer) { 1408 mlir::FunctionType funcOpType = caller.getFuncOp().getType(); 1409 mlir::SymbolRefAttr symbolAttr = 1410 builder.getSymbolRefAttr(caller.getMangledName()); 1411 if (callSiteType.getNumResults() == funcOpType.getNumResults() && 1412 callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() && 1413 fir::anyFuncArgsHaveAttr(caller.getFuncOp(), 1414 fir::getHostAssocAttrName())) { 1415 // The number of arguments is off by one, and we're lowering a function 1416 // with host associations. Modify call to include host associations 1417 // argument by appending the value at the end of the operands. 1418 assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) == 1419 converter.hostAssocTupleValue().getType()); 1420 addHostAssociations = true; 1421 } 1422 if (!addHostAssociations && 1423 (callSiteType.getNumResults() != funcOpType.getNumResults() || 1424 callSiteType.getNumInputs() != funcOpType.getNumInputs())) { 1425 // Deal with argument number mismatch by making a function pointer so 1426 // that function type cast can be inserted. Do not emit a warning here 1427 // because this can happen in legal program if the function is not 1428 // defined here and it was first passed as an argument without any more 1429 // information. 1430 funcPointer = 1431 builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr); 1432 } else if (callSiteType.getResults() != funcOpType.getResults()) { 1433 // Implicit interface result type mismatch are not standard Fortran, but 1434 // some compilers are not complaining about it. The front end is not 1435 // protecting lowering from this currently. Support this with a 1436 // discouraging warning. 1437 LLVM_DEBUG(mlir::emitWarning( 1438 loc, "a return type mismatch is not standard compliant and may " 1439 "lead to undefined behavior.")); 1440 // Cast the actual function to the current caller implicit type because 1441 // that is the behavior we would get if we could not see the definition. 1442 funcPointer = 1443 builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr); 1444 } else { 1445 funcSymbolAttr = symbolAttr; 1446 } 1447 } 1448 1449 mlir::FunctionType funcType = 1450 funcPointer ? callSiteType : caller.getFuncOp().getType(); 1451 llvm::SmallVector<mlir::Value> operands; 1452 // First operand of indirect call is the function pointer. Cast it to 1453 // required function type for the call to handle procedures that have a 1454 // compatible interface in Fortran, but that have different signatures in 1455 // FIR. 1456 if (funcPointer) { 1457 operands.push_back( 1458 funcPointer.getType().isa<fir::BoxProcType>() 1459 ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer) 1460 : builder.createConvert(loc, funcType, funcPointer)); 1461 } 1462 1463 // Deal with potential mismatches in arguments types. Passing an array to a 1464 // scalar argument should for instance be tolerated here. 1465 bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface(); 1466 for (auto [fst, snd] : 1467 llvm::zip(caller.getInputs(), funcType.getInputs())) { 1468 // When passing arguments to a procedure that can be called an implicit 1469 // interface, allow character actual arguments to be passed to dummy 1470 // arguments of any type and vice versa 1471 mlir::Value cast; 1472 auto *context = builder.getContext(); 1473 if (snd.isa<fir::BoxProcType>() && 1474 fst.getType().isa<mlir::FunctionType>()) { 1475 auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None); 1476 auto boxProcTy = builder.getBoxProcType(funcTy); 1477 if (mlir::Value host = argumentHostAssocs(converter, fst)) { 1478 cast = builder.create<fir::EmboxProcOp>( 1479 loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host}); 1480 } else { 1481 cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst); 1482 } 1483 } else { 1484 cast = builder.convertWithSemantics(loc, snd, fst, 1485 callingImplicitInterface); 1486 } 1487 operands.push_back(cast); 1488 } 1489 1490 // Add host associations as necessary. 1491 if (addHostAssociations) 1492 operands.push_back(converter.hostAssocTupleValue()); 1493 1494 auto call = builder.create<fir::CallOp>(loc, funcType.getResults(), 1495 funcSymbolAttr, operands); 1496 1497 if (caller.mustSaveResult()) 1498 builder.create<fir::SaveResultOp>( 1499 loc, call.getResult(0), fir::getBase(allocatedResult.getValue()), 1500 arrayResultShape, resultLengths); 1501 1502 if (allocatedResult) { 1503 allocatedResult->match( 1504 [&](const fir::MutableBoxValue &box) { 1505 if (box.isAllocatable()) { 1506 // 9.7.3.2 point 4. Finalize allocatables. 1507 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 1508 stmtCtx.attachCleanup([bldr, loc, box]() { 1509 fir::factory::genFinalization(*bldr, loc, box); 1510 }); 1511 } 1512 }, 1513 [](const auto &) {}); 1514 return *allocatedResult; 1515 } 1516 1517 if (!resultType.hasValue()) 1518 return mlir::Value{}; // subroutine call 1519 // For now, Fortran return values are implemented with a single MLIR 1520 // function return value. 1521 assert(call.getNumResults() == 1 && 1522 "Expected exactly one result in FUNCTION call"); 1523 return call.getResult(0); 1524 } 1525 1526 /// Like genExtAddr, but ensure the address returned is a temporary even if \p 1527 /// expr is variable inside parentheses. 1528 ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) { 1529 // In general, genExtAddr might not create a temp for variable inside 1530 // parentheses to avoid creating array temporary in sub-expressions. It only 1531 // ensures the sub-expression is not re-associated with other parts of the 1532 // expression. In the call semantics, there is a difference between expr and 1533 // variable (see R1524). For expressions, a variable storage must not be 1534 // argument associated since it could be modified inside the call, or the 1535 // variable could also be modified by other means during the call. 1536 if (!isParenthesizedVariable(expr)) 1537 return genExtAddr(expr); 1538 mlir::Location loc = getLoc(); 1539 if (expr.Rank() > 0) 1540 TODO(loc, "genTempExtAddr array"); 1541 return genExtValue(expr).match( 1542 [&](const fir::CharBoxValue &boxChar) -> ExtValue { 1543 TODO(loc, "genTempExtAddr CharBoxValue"); 1544 }, 1545 [&](const fir::UnboxedValue &v) -> ExtValue { 1546 mlir::Type type = v.getType(); 1547 mlir::Value value = v; 1548 if (fir::isa_ref_type(type)) 1549 value = builder.create<fir::LoadOp>(loc, value); 1550 mlir::Value temp = builder.createTemporary(loc, value.getType()); 1551 builder.create<fir::StoreOp>(loc, value, temp); 1552 return temp; 1553 }, 1554 [&](const fir::BoxValue &x) -> ExtValue { 1555 // Derived type scalar that may be polymorphic. 1556 assert(!x.hasRank() && x.isDerived()); 1557 if (x.isDerivedWithLengthParameters()) 1558 fir::emitFatalError( 1559 loc, "making temps for derived type with length parameters"); 1560 // TODO: polymorphic aspects should be kept but for now the temp 1561 // created always has the declared type. 1562 mlir::Value var = 1563 fir::getBase(fir::factory::readBoxValue(builder, loc, x)); 1564 auto value = builder.create<fir::LoadOp>(loc, var); 1565 mlir::Value temp = builder.createTemporary(loc, value.getType()); 1566 builder.create<fir::StoreOp>(loc, value, temp); 1567 return temp; 1568 }, 1569 [&](const auto &) -> ExtValue { 1570 fir::emitFatalError(loc, "expr is not a scalar value"); 1571 }); 1572 } 1573 1574 /// Helper structure to track potential copy-in of non contiguous variable 1575 /// argument into a contiguous temp. It is used to deallocate the temp that 1576 /// may have been created as well as to the copy-out from the temp to the 1577 /// variable after the call. 1578 struct CopyOutPair { 1579 ExtValue var; 1580 ExtValue temp; 1581 // Flag to indicate if the argument may have been modified by the 1582 // callee, in which case it must be copied-out to the variable. 1583 bool argMayBeModifiedByCall; 1584 // Optional boolean value that, if present and false, prevents 1585 // the copy-out and temp deallocation. 1586 llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime; 1587 }; 1588 using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>; 1589 1590 /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories 1591 /// not based on fir.box. 1592 /// This will lose any non contiguous stride information and dynamic type and 1593 /// should only be called if \p exv is known to be contiguous or if its base 1594 /// address will be replaced by a contiguous one. If \p exv is not a 1595 /// fir::BoxValue, this is a no-op. 1596 ExtValue readIfBoxValue(const ExtValue &exv) { 1597 if (const auto *box = exv.getBoxOf<fir::BoxValue>()) 1598 return fir::factory::readBoxValue(builder, getLoc(), *box); 1599 return exv; 1600 } 1601 1602 /// Generate a contiguous temp to pass \p actualArg as argument \p arg. The 1603 /// creation of the temp and copy-in can be made conditional at runtime by 1604 /// providing a runtime boolean flag \p restrictCopyAtRuntime (in which case 1605 /// the temp and copy will only be made if the value is true at runtime). 1606 ExtValue genCopyIn(const ExtValue &actualArg, 1607 const Fortran::lower::CallerInterface::PassedEntity &arg, 1608 CopyOutPairs ©OutPairs, 1609 llvm::Optional<mlir::Value> restrictCopyAtRuntime) { 1610 if (!restrictCopyAtRuntime) { 1611 ExtValue temp = genArrayTempFromMold(actualArg, ".copyinout"); 1612 if (arg.mayBeReadByCall()) 1613 genArrayCopy(temp, actualArg); 1614 copyOutPairs.emplace_back(CopyOutPair{ 1615 actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime}); 1616 return temp; 1617 } 1618 // Otherwise, need to be careful to only copy-in if allowed at runtime. 1619 mlir::Location loc = getLoc(); 1620 auto addrType = fir::HeapType::get( 1621 fir::unwrapPassByRefType(fir::getBase(actualArg).getType())); 1622 mlir::Value addr = 1623 builder 1624 .genIfOp(loc, {addrType}, *restrictCopyAtRuntime, 1625 /*withElseRegion=*/true) 1626 .genThen([&]() { 1627 auto temp = genArrayTempFromMold(actualArg, ".copyinout"); 1628 if (arg.mayBeReadByCall()) 1629 genArrayCopy(temp, actualArg); 1630 builder.create<fir::ResultOp>(loc, fir::getBase(temp)); 1631 }) 1632 .genElse([&]() { 1633 auto nullPtr = builder.createNullConstant(loc, addrType); 1634 builder.create<fir::ResultOp>(loc, nullPtr); 1635 }) 1636 .getResults()[0]; 1637 // Associate the temp address with actualArg lengths and extents. 1638 fir::ExtendedValue temp = fir::substBase(readIfBoxValue(actualArg), addr); 1639 copyOutPairs.emplace_back(CopyOutPair{ 1640 actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime}); 1641 return temp; 1642 } 1643 1644 /// Lower a non-elemental procedure reference. 1645 ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef, 1646 llvm::Optional<mlir::Type> resultType) { 1647 mlir::Location loc = getLoc(); 1648 if (isElementalProcWithArrayArgs(procRef)) 1649 fir::emitFatalError(loc, "trying to lower elemental procedure with array " 1650 "arguments as normal procedure"); 1651 if (const Fortran::evaluate::SpecificIntrinsic *intrinsic = 1652 procRef.proc().GetSpecificIntrinsic()) 1653 return genIntrinsicRef(procRef, *intrinsic, resultType); 1654 1655 if (isStatementFunctionCall(procRef)) 1656 TODO(loc, "Lower statement function call"); 1657 1658 Fortran::lower::CallerInterface caller(procRef, converter); 1659 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 1660 1661 llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall; 1662 // List of <var, temp> where temp must be copied into var after the call. 1663 CopyOutPairs copyOutPairs; 1664 1665 mlir::FunctionType callSiteType = caller.genFunctionType(); 1666 1667 // Lower the actual arguments and map the lowered values to the dummy 1668 // arguments. 1669 for (const Fortran::lower::CallInterface< 1670 Fortran::lower::CallerInterface>::PassedEntity &arg : 1671 caller.getPassedArguments()) { 1672 const auto *actual = arg.entity; 1673 mlir::Type argTy = callSiteType.getInput(arg.firArgument); 1674 if (!actual) { 1675 // Optional dummy argument for which there is no actual argument. 1676 caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy)); 1677 continue; 1678 } 1679 const auto *expr = actual->UnwrapExpr(); 1680 if (!expr) 1681 TODO(loc, "assumed type actual argument lowering"); 1682 1683 if (arg.passBy == PassBy::Value) { 1684 ExtValue argVal = genval(*expr); 1685 if (!fir::isUnboxedValue(argVal)) 1686 fir::emitFatalError( 1687 loc, "internal error: passing non trivial value by value"); 1688 caller.placeInput(arg, fir::getBase(argVal)); 1689 continue; 1690 } 1691 1692 if (arg.passBy == PassBy::MutableBox) { 1693 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 1694 *expr)) { 1695 // If expr is NULL(), the mutableBox created must be a deallocated 1696 // pointer with the dummy argument characteristics (see table 16.5 1697 // in Fortran 2018 standard). 1698 // No length parameters are set for the created box because any non 1699 // deferred type parameters of the dummy will be evaluated on the 1700 // callee side, and it is illegal to use NULL without a MOLD if any 1701 // dummy length parameters are assumed. 1702 mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy); 1703 assert(boxTy && boxTy.isa<fir::BoxType>() && 1704 "must be a fir.box type"); 1705 mlir::Value boxStorage = builder.createTemporary(loc, boxTy); 1706 mlir::Value nullBox = fir::factory::createUnallocatedBox( 1707 builder, loc, boxTy, /*nonDeferredParams=*/{}); 1708 builder.create<fir::StoreOp>(loc, nullBox, boxStorage); 1709 caller.placeInput(arg, boxStorage); 1710 continue; 1711 } 1712 fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr); 1713 mlir::Value irBox = 1714 fir::factory::getMutableIRBox(builder, loc, mutableBox); 1715 caller.placeInput(arg, irBox); 1716 if (arg.mayBeModifiedByCall()) 1717 mutableModifiedByCall.emplace_back(std::move(mutableBox)); 1718 continue; 1719 } 1720 const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr); 1721 if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) { 1722 const bool actualIsSimplyContiguous = 1723 !actualArgIsVariable || Fortran::evaluate::IsSimplyContiguous( 1724 *expr, converter.getFoldingContext()); 1725 auto argAddr = [&]() -> ExtValue { 1726 ExtValue baseAddr; 1727 if (actualArgIsVariable && arg.isOptional()) { 1728 if (Fortran::evaluate::IsAllocatableOrPointerObject( 1729 *expr, converter.getFoldingContext())) { 1730 TODO(loc, "Allocatable or pointer argument"); 1731 } 1732 if (const Fortran::semantics::Symbol *wholeSymbol = 1733 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef( 1734 *expr)) 1735 if (Fortran::semantics::IsOptional(*wholeSymbol)) { 1736 TODO(loc, "procedureref optional arg"); 1737 } 1738 // Fall through: The actual argument can safely be 1739 // copied-in/copied-out without any care if needed. 1740 } 1741 if (actualArgIsVariable && expr->Rank() > 0) { 1742 ExtValue box = genBoxArg(*expr); 1743 if (!actualIsSimplyContiguous) 1744 return genCopyIn(box, arg, copyOutPairs, 1745 /*restrictCopyAtRuntime=*/llvm::None); 1746 // Contiguous: just use the box we created above! 1747 // This gets "unboxed" below, if needed. 1748 return box; 1749 } 1750 // Actual argument is a non optional/non pointer/non allocatable 1751 // scalar. 1752 if (actualArgIsVariable) 1753 return genExtAddr(*expr); 1754 // Actual argument is not a variable. Make sure a variable address is 1755 // not passed. 1756 return genTempExtAddr(*expr); 1757 }(); 1758 // Scalar and contiguous expressions may be lowered to a fir.box, 1759 // either to account for potential polymorphism, or because lowering 1760 // did not account for some contiguity hints. 1761 // Here, polymorphism does not matter (an entity of the declared type 1762 // is passed, not one of the dynamic type), and the expr is known to 1763 // be simply contiguous, so it is safe to unbox it and pass the 1764 // address without making a copy. 1765 argAddr = readIfBoxValue(argAddr); 1766 1767 if (arg.passBy == PassBy::BaseAddress) { 1768 caller.placeInput(arg, fir::getBase(argAddr)); 1769 } else { 1770 assert(arg.passBy == PassBy::BoxChar); 1771 auto helper = fir::factory::CharacterExprHelper{builder, loc}; 1772 auto boxChar = argAddr.match( 1773 [&](const fir::CharBoxValue &x) { return helper.createEmbox(x); }, 1774 [&](const fir::CharArrayBoxValue &x) { 1775 return helper.createEmbox(x); 1776 }, 1777 [&](const auto &x) -> mlir::Value { 1778 // Fortran allows an actual argument of a completely different 1779 // type to be passed to a procedure expecting a CHARACTER in the 1780 // dummy argument position. When this happens, the data pointer 1781 // argument is simply assumed to point to CHARACTER data and the 1782 // LEN argument used is garbage. Simulate this behavior by 1783 // free-casting the base address to be a !fir.char reference and 1784 // setting the LEN argument to undefined. What could go wrong? 1785 auto dataPtr = fir::getBase(x); 1786 assert(!dataPtr.getType().template isa<fir::BoxType>()); 1787 return builder.convertWithSemantics( 1788 loc, argTy, dataPtr, 1789 /*allowCharacterConversion=*/true); 1790 }); 1791 caller.placeInput(arg, boxChar); 1792 } 1793 } else if (arg.passBy == PassBy::Box) { 1794 // Before lowering to an address, handle the allocatable/pointer actual 1795 // argument to optional fir.box dummy. It is legal to pass 1796 // unallocated/disassociated entity to an optional. In this case, an 1797 // absent fir.box must be created instead of a fir.box with a null value 1798 // (Fortran 2018 15.5.2.12 point 1). 1799 if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject( 1800 *expr, converter.getFoldingContext())) { 1801 TODO(loc, "optional allocatable or pointer argument"); 1802 } else { 1803 // Make sure a variable address is only passed if the expression is 1804 // actually a variable. 1805 mlir::Value box = 1806 actualArgIsVariable 1807 ? builder.createBox(loc, genBoxArg(*expr)) 1808 : builder.createBox(getLoc(), genTempExtAddr(*expr)); 1809 caller.placeInput(arg, box); 1810 } 1811 } else if (arg.passBy == PassBy::AddressAndLength) { 1812 ExtValue argRef = genExtAddr(*expr); 1813 caller.placeAddressAndLengthInput(arg, fir::getBase(argRef), 1814 fir::getLen(argRef)); 1815 } else if (arg.passBy == PassBy::CharProcTuple) { 1816 TODO(loc, "procedureref CharProcTuple"); 1817 } else { 1818 TODO(loc, "pass by value in non elemental function call"); 1819 } 1820 } 1821 1822 ExtValue result = genCallOpAndResult(caller, callSiteType, resultType); 1823 1824 // // Copy-out temps that were created for non contiguous variable arguments 1825 // if 1826 // // needed. 1827 // for (const auto ©OutPair : copyOutPairs) 1828 // genCopyOut(copyOutPair); 1829 1830 return result; 1831 } 1832 1833 template <typename A> 1834 ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) { 1835 ExtValue result = genFunctionRef(funcRef); 1836 if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType())) 1837 return genLoad(result); 1838 return result; 1839 } 1840 1841 ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) { 1842 llvm::Optional<mlir::Type> resTy; 1843 if (procRef.hasAlternateReturns()) 1844 resTy = builder.getIndexType(); 1845 return genProcedureRef(procRef, resTy); 1846 } 1847 1848 /// Helper to lower intrinsic arguments for inquiry intrinsic. 1849 ExtValue 1850 lowerIntrinsicArgumentAsInquired(const Fortran::lower::SomeExpr &expr) { 1851 if (Fortran::evaluate::IsAllocatableOrPointerObject( 1852 expr, converter.getFoldingContext())) 1853 return genMutableBoxValue(expr); 1854 return gen(expr); 1855 } 1856 1857 /// Helper to lower intrinsic arguments to a fir::BoxValue. 1858 /// It preserves all the non default lower bounds/non deferred length 1859 /// parameter information. 1860 ExtValue lowerIntrinsicArgumentAsBox(const Fortran::lower::SomeExpr &expr) { 1861 mlir::Location loc = getLoc(); 1862 ExtValue exv = genBoxArg(expr); 1863 mlir::Value box = builder.createBox(loc, exv); 1864 return fir::BoxValue( 1865 box, fir::factory::getNonDefaultLowerBounds(builder, loc, exv), 1866 fir::factory::getNonDeferredLengthParams(exv)); 1867 } 1868 1869 /// Generate a call to an intrinsic function. 1870 ExtValue 1871 genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef, 1872 const Fortran::evaluate::SpecificIntrinsic &intrinsic, 1873 llvm::Optional<mlir::Type> resultType) { 1874 llvm::SmallVector<ExtValue> operands; 1875 1876 llvm::StringRef name = intrinsic.name; 1877 mlir::Location loc = getLoc(); 1878 1879 const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering = 1880 Fortran::lower::getIntrinsicArgumentLowering(name); 1881 for (const auto &[arg, dummy] : 1882 llvm::zip(procRef.arguments(), 1883 intrinsic.characteristics.value().dummyArguments)) { 1884 auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg); 1885 if (!expr) { 1886 // Absent optional. 1887 operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument()); 1888 continue; 1889 } 1890 if (!argLowering) { 1891 // No argument lowering instruction, lower by value. 1892 operands.emplace_back(genval(*expr)); 1893 continue; 1894 } 1895 // Ad-hoc argument lowering handling. 1896 Fortran::lower::ArgLoweringRule argRules = 1897 Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering, 1898 dummy.name); 1899 if (argRules.handleDynamicOptional && 1900 Fortran::evaluate::MayBePassedAsAbsentOptional( 1901 *expr, converter.getFoldingContext())) { 1902 ExtValue optional = lowerIntrinsicArgumentAsInquired(*expr); 1903 mlir::Value isPresent = genActualIsPresentTest(builder, loc, optional); 1904 switch (argRules.lowerAs) { 1905 case Fortran::lower::LowerIntrinsicArgAs::Value: 1906 operands.emplace_back( 1907 genOptionalValue(builder, loc, optional, isPresent)); 1908 continue; 1909 case Fortran::lower::LowerIntrinsicArgAs::Addr: 1910 operands.emplace_back( 1911 genOptionalAddr(builder, loc, optional, isPresent)); 1912 continue; 1913 case Fortran::lower::LowerIntrinsicArgAs::Box: 1914 operands.emplace_back( 1915 genOptionalBox(builder, loc, optional, isPresent)); 1916 continue; 1917 case Fortran::lower::LowerIntrinsicArgAs::Inquired: 1918 operands.emplace_back(optional); 1919 continue; 1920 } 1921 llvm_unreachable("bad switch"); 1922 } 1923 switch (argRules.lowerAs) { 1924 case Fortran::lower::LowerIntrinsicArgAs::Value: 1925 operands.emplace_back(genval(*expr)); 1926 continue; 1927 case Fortran::lower::LowerIntrinsicArgAs::Addr: 1928 operands.emplace_back(gen(*expr)); 1929 continue; 1930 case Fortran::lower::LowerIntrinsicArgAs::Box: 1931 operands.emplace_back(lowerIntrinsicArgumentAsBox(*expr)); 1932 continue; 1933 case Fortran::lower::LowerIntrinsicArgAs::Inquired: 1934 operands.emplace_back(lowerIntrinsicArgumentAsInquired(*expr)); 1935 continue; 1936 } 1937 llvm_unreachable("bad switch"); 1938 } 1939 // Let the intrinsic library lower the intrinsic procedure call 1940 return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType, 1941 operands, stmtCtx); 1942 } 1943 1944 template <typename A> 1945 ExtValue genval(const Fortran::evaluate::Expr<A> &x) { 1946 if (isScalar(x) || Fortran::evaluate::UnwrapWholeSymbolDataRef(x) || 1947 inInitializer) 1948 return std::visit([&](const auto &e) { return genval(e); }, x.u); 1949 return asArray(x); 1950 } 1951 1952 /// Helper to detect Transformational function reference. 1953 template <typename T> 1954 bool isTransformationalRef(const T &) { 1955 return false; 1956 } 1957 template <typename T> 1958 bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) { 1959 return !funcRef.IsElemental() && funcRef.Rank(); 1960 } 1961 template <typename T> 1962 bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) { 1963 return std::visit([&](const auto &e) { return isTransformationalRef(e); }, 1964 expr.u); 1965 } 1966 1967 template <typename A> 1968 ExtValue gen(const Fortran::evaluate::Expr<A> &x) { 1969 // Whole array symbols or components, and results of transformational 1970 // functions already have a storage and the scalar expression lowering path 1971 // is used to not create a new temporary storage. 1972 if (isScalar(x) || 1973 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) || 1974 isTransformationalRef(x)) 1975 return std::visit([&](const auto &e) { return genref(e); }, x.u); 1976 TODO(getLoc(), "gen Expr non-scalar"); 1977 } 1978 1979 template <typename A> 1980 bool isScalar(const A &x) { 1981 return x.Rank() == 0; 1982 } 1983 1984 template <typename A> 1985 ExtValue asArray(const A &x) { 1986 return Fortran::lower::createSomeArrayTempValue(converter, toEvExpr(x), 1987 symMap, stmtCtx); 1988 } 1989 1990 template <int KIND> 1991 ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type< 1992 Fortran::common::TypeCategory::Logical, KIND>> &exp) { 1993 return std::visit([&](const auto &e) { return genval(e); }, exp.u); 1994 } 1995 1996 using RefSet = 1997 std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring, 1998 Fortran::evaluate::DataRef, Fortran::evaluate::Component, 1999 Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef, 2000 Fortran::semantics::SymbolRef>; 2001 template <typename A> 2002 static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>; 2003 2004 template <typename A, typename = std::enable_if_t<inRefSet<A>>> 2005 ExtValue genref(const A &a) { 2006 return gen(a); 2007 } 2008 template <typename A> 2009 ExtValue genref(const A &a) { 2010 mlir::Type storageType = converter.genType(toEvExpr(a)); 2011 return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType); 2012 } 2013 2014 template <typename A, template <typename> typename T, 2015 typename B = std::decay_t<T<A>>, 2016 std::enable_if_t< 2017 std::is_same_v<B, Fortran::evaluate::Expr<A>> || 2018 std::is_same_v<B, Fortran::evaluate::Designator<A>> || 2019 std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>, 2020 bool> = true> 2021 ExtValue genref(const T<A> &x) { 2022 return gen(x); 2023 } 2024 2025 private: 2026 mlir::Location location; 2027 Fortran::lower::AbstractConverter &converter; 2028 fir::FirOpBuilder &builder; 2029 Fortran::lower::StatementContext &stmtCtx; 2030 Fortran::lower::SymMap &symMap; 2031 InitializerData *inInitializer = nullptr; 2032 bool useBoxArg = false; // expression lowered as argument 2033 }; 2034 } // namespace 2035 2036 // Helper for changing the semantics in a given context. Preserves the current 2037 // semantics which is resumed when the "push" goes out of scope. 2038 #define PushSemantics(PushVal) \ 2039 [[maybe_unused]] auto pushSemanticsLocalVariable##__LINE__ = \ 2040 Fortran::common::ScopedSet(semant, PushVal); 2041 2042 static bool isAdjustedArrayElementType(mlir::Type t) { 2043 return fir::isa_char(t) || fir::isa_derived(t) || t.isa<fir::SequenceType>(); 2044 } 2045 2046 /// Build an ExtendedValue from a fir.array<?x...?xT> without actually setting 2047 /// the actual extents and lengths. This is only to allow their propagation as 2048 /// ExtendedValue without triggering verifier failures when propagating 2049 /// character/arrays as unboxed values. Only the base of the resulting 2050 /// ExtendedValue should be used, it is undefined to use the length or extents 2051 /// of the extended value returned, 2052 inline static fir::ExtendedValue 2053 convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder, 2054 mlir::Value val, mlir::Value len) { 2055 mlir::Type ty = fir::unwrapRefType(val.getType()); 2056 mlir::IndexType idxTy = builder.getIndexType(); 2057 auto seqTy = ty.cast<fir::SequenceType>(); 2058 auto undef = builder.create<fir::UndefOp>(loc, idxTy); 2059 llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), undef); 2060 if (fir::isa_char(seqTy.getEleTy())) 2061 return fir::CharArrayBoxValue(val, len ? len : undef, extents); 2062 return fir::ArrayBoxValue(val, extents); 2063 } 2064 2065 //===----------------------------------------------------------------------===// 2066 // 2067 // Lowering of scalar expressions in an explicit iteration space context. 2068 // 2069 //===----------------------------------------------------------------------===// 2070 2071 // Shared code for creating a copy of a derived type element. This function is 2072 // called from a continuation. 2073 inline static fir::ArrayAmendOp 2074 createDerivedArrayAmend(mlir::Location loc, fir::ArrayLoadOp destLoad, 2075 fir::FirOpBuilder &builder, fir::ArrayAccessOp destAcc, 2076 const fir::ExtendedValue &elementExv, mlir::Type eleTy, 2077 mlir::Value innerArg) { 2078 if (destLoad.getTypeparams().empty()) { 2079 fir::factory::genRecordAssignment(builder, loc, destAcc, elementExv); 2080 } else { 2081 auto boxTy = fir::BoxType::get(eleTy); 2082 auto toBox = builder.create<fir::EmboxOp>(loc, boxTy, destAcc.getResult(), 2083 mlir::Value{}, mlir::Value{}, 2084 destLoad.getTypeparams()); 2085 auto fromBox = builder.create<fir::EmboxOp>( 2086 loc, boxTy, fir::getBase(elementExv), mlir::Value{}, mlir::Value{}, 2087 destLoad.getTypeparams()); 2088 fir::factory::genRecordAssignment(builder, loc, fir::BoxValue(toBox), 2089 fir::BoxValue(fromBox)); 2090 } 2091 return builder.create<fir::ArrayAmendOp>(loc, innerArg.getType(), innerArg, 2092 destAcc); 2093 } 2094 2095 inline static fir::ArrayAmendOp 2096 createCharArrayAmend(mlir::Location loc, fir::FirOpBuilder &builder, 2097 fir::ArrayAccessOp dstOp, mlir::Value &dstLen, 2098 const fir::ExtendedValue &srcExv, mlir::Value innerArg, 2099 llvm::ArrayRef<mlir::Value> bounds) { 2100 fir::CharBoxValue dstChar(dstOp, dstLen); 2101 fir::factory::CharacterExprHelper helper{builder, loc}; 2102 if (!bounds.empty()) { 2103 dstChar = helper.createSubstring(dstChar, bounds); 2104 fir::factory::genCharacterCopy(fir::getBase(srcExv), fir::getLen(srcExv), 2105 dstChar.getAddr(), dstChar.getLen(), builder, 2106 loc); 2107 // Update the LEN to the substring's LEN. 2108 dstLen = dstChar.getLen(); 2109 } 2110 // For a CHARACTER, we generate the element assignment loops inline. 2111 helper.createAssign(fir::ExtendedValue{dstChar}, srcExv); 2112 // Mark this array element as amended. 2113 mlir::Type ty = innerArg.getType(); 2114 auto amend = builder.create<fir::ArrayAmendOp>(loc, ty, innerArg, dstOp); 2115 return amend; 2116 } 2117 2118 //===----------------------------------------------------------------------===// 2119 // 2120 // Lowering of array expressions. 2121 // 2122 //===----------------------------------------------------------------------===// 2123 2124 namespace { 2125 class ArrayExprLowering { 2126 using ExtValue = fir::ExtendedValue; 2127 2128 /// Structure to keep track of lowered array operands in the 2129 /// array expression. Useful to later deduce the shape of the 2130 /// array expression. 2131 struct ArrayOperand { 2132 /// Array base (can be a fir.box). 2133 mlir::Value memref; 2134 /// ShapeOp, ShapeShiftOp or ShiftOp 2135 mlir::Value shape; 2136 /// SliceOp 2137 mlir::Value slice; 2138 /// Can this operand be absent ? 2139 bool mayBeAbsent = false; 2140 }; 2141 2142 using ImplicitSubscripts = Fortran::lower::details::ImplicitSubscripts; 2143 using PathComponent = Fortran::lower::PathComponent; 2144 2145 /// Active iteration space. 2146 using IterationSpace = Fortran::lower::IterationSpace; 2147 using IterSpace = const Fortran::lower::IterationSpace &; 2148 2149 /// Current continuation. Function that will generate IR for a single 2150 /// iteration of the pending iterative loop structure. 2151 using CC = Fortran::lower::GenerateElementalArrayFunc; 2152 2153 /// Projection continuation. Function that will project one iteration space 2154 /// into another. 2155 using PC = std::function<IterationSpace(IterSpace)>; 2156 using ArrayBaseTy = 2157 std::variant<std::monostate, const Fortran::evaluate::ArrayRef *, 2158 const Fortran::evaluate::DataRef *>; 2159 using ComponentPath = Fortran::lower::ComponentPath; 2160 2161 public: 2162 //===--------------------------------------------------------------------===// 2163 // Regular array assignment 2164 //===--------------------------------------------------------------------===// 2165 2166 /// Entry point for array assignments. Both the left-hand and right-hand sides 2167 /// can either be ExtendedValue or evaluate::Expr. 2168 template <typename TL, typename TR> 2169 static void lowerArrayAssignment(Fortran::lower::AbstractConverter &converter, 2170 Fortran::lower::SymMap &symMap, 2171 Fortran::lower::StatementContext &stmtCtx, 2172 const TL &lhs, const TR &rhs) { 2173 ArrayExprLowering ael{converter, stmtCtx, symMap, 2174 ConstituentSemantics::CopyInCopyOut}; 2175 ael.lowerArrayAssignment(lhs, rhs); 2176 } 2177 2178 template <typename TL, typename TR> 2179 void lowerArrayAssignment(const TL &lhs, const TR &rhs) { 2180 mlir::Location loc = getLoc(); 2181 /// Here the target subspace is not necessarily contiguous. The ArrayUpdate 2182 /// continuation is implicitly returned in `ccStoreToDest` and the ArrayLoad 2183 /// in `destination`. 2184 PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut); 2185 ccStoreToDest = genarr(lhs); 2186 determineShapeOfDest(lhs); 2187 semant = ConstituentSemantics::RefTransparent; 2188 ExtValue exv = lowerArrayExpression(rhs); 2189 if (explicitSpaceIsActive()) { 2190 explicitSpace->finalizeContext(); 2191 builder.create<fir::ResultOp>(loc, fir::getBase(exv)); 2192 } else { 2193 builder.create<fir::ArrayMergeStoreOp>( 2194 loc, destination, fir::getBase(exv), destination.getMemref(), 2195 destination.getSlice(), destination.getTypeparams()); 2196 } 2197 } 2198 2199 //===--------------------------------------------------------------------===// 2200 // Array assignment to allocatable array 2201 //===--------------------------------------------------------------------===// 2202 2203 /// Entry point for assignment to allocatable array. 2204 static void lowerAllocatableArrayAssignment( 2205 Fortran::lower::AbstractConverter &converter, 2206 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 2207 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 2208 Fortran::lower::ExplicitIterSpace &explicitSpace, 2209 Fortran::lower::ImplicitIterSpace &implicitSpace) { 2210 ArrayExprLowering ael(converter, stmtCtx, symMap, 2211 ConstituentSemantics::CopyInCopyOut, &explicitSpace, 2212 &implicitSpace); 2213 ael.lowerAllocatableArrayAssignment(lhs, rhs); 2214 } 2215 2216 /// Assignment to allocatable array. 2217 /// 2218 /// The semantics are reverse that of a "regular" array assignment. The rhs 2219 /// defines the iteration space of the computation and the lhs is 2220 /// resized/reallocated to fit if necessary. 2221 void lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr &lhs, 2222 const Fortran::lower::SomeExpr &rhs) { 2223 // With assignment to allocatable, we want to lower the rhs first and use 2224 // its shape to determine if we need to reallocate, etc. 2225 mlir::Location loc = getLoc(); 2226 // FIXME: If the lhs is in an explicit iteration space, the assignment may 2227 // be to an array of allocatable arrays rather than a single allocatable 2228 // array. 2229 fir::MutableBoxValue mutableBox = 2230 createMutableBox(loc, converter, lhs, symMap); 2231 mlir::Type resultTy = converter.genType(rhs); 2232 if (rhs.Rank() > 0) 2233 determineShapeOfDest(rhs); 2234 auto rhsCC = [&]() { 2235 PushSemantics(ConstituentSemantics::RefTransparent); 2236 return genarr(rhs); 2237 }(); 2238 2239 llvm::SmallVector<mlir::Value> lengthParams; 2240 // Currently no safe way to gather length from rhs (at least for 2241 // character, it cannot be taken from array_loads since it may be 2242 // changed by concatenations). 2243 if ((mutableBox.isCharacter() && !mutableBox.hasNonDeferredLenParams()) || 2244 mutableBox.isDerivedWithLengthParameters()) 2245 TODO(loc, "gather rhs length parameters in assignment to allocatable"); 2246 2247 // The allocatable must take lower bounds from the expr if it is 2248 // reallocated and the right hand side is not a scalar. 2249 const bool takeLboundsIfRealloc = rhs.Rank() > 0; 2250 llvm::SmallVector<mlir::Value> lbounds; 2251 // When the reallocated LHS takes its lower bounds from the RHS, 2252 // they will be non default only if the RHS is a whole array 2253 // variable. Otherwise, lbounds is left empty and default lower bounds 2254 // will be used. 2255 if (takeLboundsIfRealloc && 2256 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs)) { 2257 assert(arrayOperands.size() == 1 && 2258 "lbounds can only come from one array"); 2259 std::vector<mlir::Value> lbs = 2260 fir::factory::getOrigins(arrayOperands[0].shape); 2261 lbounds.append(lbs.begin(), lbs.end()); 2262 } 2263 fir::factory::MutableBoxReallocation realloc = 2264 fir::factory::genReallocIfNeeded(builder, loc, mutableBox, destShape, 2265 lengthParams); 2266 // Create ArrayLoad for the mutable box and save it into `destination`. 2267 PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut); 2268 ccStoreToDest = genarr(realloc.newValue); 2269 // If the rhs is scalar, get shape from the allocatable ArrayLoad. 2270 if (destShape.empty()) 2271 destShape = getShape(destination); 2272 // Finish lowering the loop nest. 2273 assert(destination && "destination must have been set"); 2274 ExtValue exv = lowerArrayExpression(rhsCC, resultTy); 2275 if (explicitSpaceIsActive()) { 2276 explicitSpace->finalizeContext(); 2277 builder.create<fir::ResultOp>(loc, fir::getBase(exv)); 2278 } else { 2279 builder.create<fir::ArrayMergeStoreOp>( 2280 loc, destination, fir::getBase(exv), destination.getMemref(), 2281 destination.getSlice(), destination.getTypeparams()); 2282 } 2283 fir::factory::finalizeRealloc(builder, loc, mutableBox, lbounds, 2284 takeLboundsIfRealloc, realloc); 2285 } 2286 2287 /// Entry point for when an array expression appears in a context where the 2288 /// result must be boxed. (BoxValue semantics.) 2289 static ExtValue 2290 lowerBoxedArrayExpression(Fortran::lower::AbstractConverter &converter, 2291 Fortran::lower::SymMap &symMap, 2292 Fortran::lower::StatementContext &stmtCtx, 2293 const Fortran::lower::SomeExpr &expr) { 2294 ArrayExprLowering ael{converter, stmtCtx, symMap, 2295 ConstituentSemantics::BoxValue}; 2296 return ael.lowerBoxedArrayExpr(expr); 2297 } 2298 2299 ExtValue lowerBoxedArrayExpr(const Fortran::lower::SomeExpr &exp) { 2300 return std::visit( 2301 [&](const auto &e) { 2302 auto f = genarr(e); 2303 ExtValue exv = f(IterationSpace{}); 2304 if (fir::getBase(exv).getType().template isa<fir::BoxType>()) 2305 return exv; 2306 fir::emitFatalError(getLoc(), "array must be emboxed"); 2307 }, 2308 exp.u); 2309 } 2310 2311 /// Entry point into lowering an expression with rank. This entry point is for 2312 /// lowering a rhs expression, for example. (RefTransparent semantics.) 2313 static ExtValue 2314 lowerNewArrayExpression(Fortran::lower::AbstractConverter &converter, 2315 Fortran::lower::SymMap &symMap, 2316 Fortran::lower::StatementContext &stmtCtx, 2317 const Fortran::lower::SomeExpr &expr) { 2318 ArrayExprLowering ael{converter, stmtCtx, symMap}; 2319 ael.determineShapeOfDest(expr); 2320 ExtValue loopRes = ael.lowerArrayExpression(expr); 2321 fir::ArrayLoadOp dest = ael.destination; 2322 mlir::Value tempRes = dest.getMemref(); 2323 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 2324 mlir::Location loc = converter.getCurrentLocation(); 2325 builder.create<fir::ArrayMergeStoreOp>(loc, dest, fir::getBase(loopRes), 2326 tempRes, dest.getSlice(), 2327 dest.getTypeparams()); 2328 2329 auto arrTy = 2330 fir::dyn_cast_ptrEleTy(tempRes.getType()).cast<fir::SequenceType>(); 2331 if (auto charTy = 2332 arrTy.getEleTy().template dyn_cast<fir::CharacterType>()) { 2333 if (fir::characterWithDynamicLen(charTy)) 2334 TODO(loc, "CHARACTER does not have constant LEN"); 2335 mlir::Value len = builder.createIntegerConstant( 2336 loc, builder.getCharacterLengthType(), charTy.getLen()); 2337 return fir::CharArrayBoxValue(tempRes, len, dest.getExtents()); 2338 } 2339 return fir::ArrayBoxValue(tempRes, dest.getExtents()); 2340 } 2341 2342 // FIXME: should take multiple inner arguments. 2343 std::pair<IterationSpace, mlir::OpBuilder::InsertPoint> 2344 genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) { 2345 mlir::Location loc = getLoc(); 2346 mlir::IndexType idxTy = builder.getIndexType(); 2347 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); 2348 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); 2349 llvm::SmallVector<mlir::Value> loopUppers; 2350 2351 // Convert any implied shape to closed interval form. The fir.do_loop will 2352 // run from 0 to `extent - 1` inclusive. 2353 for (auto extent : shape) 2354 loopUppers.push_back( 2355 builder.create<mlir::arith::SubIOp>(loc, extent, one)); 2356 2357 // Iteration space is created with outermost columns, innermost rows 2358 llvm::SmallVector<fir::DoLoopOp> loops; 2359 2360 const std::size_t loopDepth = loopUppers.size(); 2361 llvm::SmallVector<mlir::Value> ivars; 2362 2363 for (auto i : llvm::enumerate(llvm::reverse(loopUppers))) { 2364 if (i.index() > 0) { 2365 assert(!loops.empty()); 2366 builder.setInsertionPointToStart(loops.back().getBody()); 2367 } 2368 fir::DoLoopOp loop; 2369 if (innerArg) { 2370 loop = builder.create<fir::DoLoopOp>( 2371 loc, zero, i.value(), one, isUnordered(), 2372 /*finalCount=*/false, mlir::ValueRange{innerArg}); 2373 innerArg = loop.getRegionIterArgs().front(); 2374 if (explicitSpaceIsActive()) 2375 explicitSpace->setInnerArg(0, innerArg); 2376 } else { 2377 loop = builder.create<fir::DoLoopOp>(loc, zero, i.value(), one, 2378 isUnordered(), 2379 /*finalCount=*/false); 2380 } 2381 ivars.push_back(loop.getInductionVar()); 2382 loops.push_back(loop); 2383 } 2384 2385 if (innerArg) 2386 for (std::remove_const_t<decltype(loopDepth)> i = 0; i + 1 < loopDepth; 2387 ++i) { 2388 builder.setInsertionPointToEnd(loops[i].getBody()); 2389 builder.create<fir::ResultOp>(loc, loops[i + 1].getResult(0)); 2390 } 2391 2392 // Move insertion point to the start of the innermost loop in the nest. 2393 builder.setInsertionPointToStart(loops.back().getBody()); 2394 // Set `afterLoopNest` to just after the entire loop nest. 2395 auto currPt = builder.saveInsertionPoint(); 2396 builder.setInsertionPointAfter(loops[0]); 2397 auto afterLoopNest = builder.saveInsertionPoint(); 2398 builder.restoreInsertionPoint(currPt); 2399 2400 // Put the implicit loop variables in row to column order to match FIR's 2401 // Ops. (The loops were constructed from outermost column to innermost 2402 // row.) 2403 mlir::Value outerRes = loops[0].getResult(0); 2404 return {IterationSpace(innerArg, outerRes, llvm::reverse(ivars)), 2405 afterLoopNest}; 2406 } 2407 2408 /// Build the iteration space into which the array expression will be 2409 /// lowered. The resultType is used to create a temporary, if needed. 2410 std::pair<IterationSpace, mlir::OpBuilder::InsertPoint> 2411 genIterSpace(mlir::Type resultType) { 2412 mlir::Location loc = getLoc(); 2413 llvm::SmallVector<mlir::Value> shape = genIterationShape(); 2414 if (!destination) { 2415 // Allocate storage for the result if it is not already provided. 2416 destination = createAndLoadSomeArrayTemp(resultType, shape); 2417 } 2418 2419 // Generate the lazy mask allocation, if one was given. 2420 if (ccPrelude.hasValue()) 2421 ccPrelude.getValue()(shape); 2422 2423 // Now handle the implicit loops. 2424 mlir::Value inner = explicitSpaceIsActive() 2425 ? explicitSpace->getInnerArgs().front() 2426 : destination.getResult(); 2427 auto [iters, afterLoopNest] = genImplicitLoops(shape, inner); 2428 mlir::Value innerArg = iters.innerArgument(); 2429 2430 // Generate the mask conditional structure, if there are masks. Unlike the 2431 // explicit masks, which are interleaved, these mask expression appear in 2432 // the innermost loop. 2433 if (implicitSpaceHasMasks()) { 2434 // Recover the cached condition from the mask buffer. 2435 auto genCond = [&](Fortran::lower::FrontEndExpr e, IterSpace iters) { 2436 return implicitSpace->getBoundClosure(e)(iters); 2437 }; 2438 2439 // Handle the negated conditions in topological order of the WHERE 2440 // clauses. See 10.2.3.2p4 as to why this control structure is produced. 2441 for (llvm::SmallVector<Fortran::lower::FrontEndExpr> maskExprs : 2442 implicitSpace->getMasks()) { 2443 const std::size_t size = maskExprs.size() - 1; 2444 auto genFalseBlock = [&](const auto *e, auto &&cond) { 2445 auto ifOp = builder.create<fir::IfOp>( 2446 loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond), 2447 /*withElseRegion=*/true); 2448 builder.create<fir::ResultOp>(loc, ifOp.getResult(0)); 2449 builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); 2450 builder.create<fir::ResultOp>(loc, innerArg); 2451 builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 2452 }; 2453 auto genTrueBlock = [&](const auto *e, auto &&cond) { 2454 auto ifOp = builder.create<fir::IfOp>( 2455 loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond), 2456 /*withElseRegion=*/true); 2457 builder.create<fir::ResultOp>(loc, ifOp.getResult(0)); 2458 builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 2459 builder.create<fir::ResultOp>(loc, innerArg); 2460 builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); 2461 }; 2462 for (std::remove_const_t<decltype(size)> i = 0; i < size; ++i) 2463 if (const auto *e = maskExprs[i]) 2464 genFalseBlock(e, genCond(e, iters)); 2465 2466 // The last condition is either non-negated or unconditionally negated. 2467 if (const auto *e = maskExprs[size]) 2468 genTrueBlock(e, genCond(e, iters)); 2469 } 2470 } 2471 2472 // We're ready to lower the body (an assignment statement) for this context 2473 // of loop nests at this point. 2474 return {iters, afterLoopNest}; 2475 } 2476 2477 fir::ArrayLoadOp 2478 createAndLoadSomeArrayTemp(mlir::Type type, 2479 llvm::ArrayRef<mlir::Value> shape) { 2480 if (ccLoadDest.hasValue()) 2481 return ccLoadDest.getValue()(shape); 2482 auto seqTy = type.dyn_cast<fir::SequenceType>(); 2483 assert(seqTy && "must be an array"); 2484 mlir::Location loc = getLoc(); 2485 // TODO: Need to thread the length parameters here. For character, they may 2486 // differ from the operands length (e.g concatenation). So the array loads 2487 // type parameters are not enough. 2488 if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>()) 2489 if (charTy.hasDynamicLen()) 2490 TODO(loc, "character array expression temp with dynamic length"); 2491 if (auto recTy = seqTy.getEleTy().dyn_cast<fir::RecordType>()) 2492 if (recTy.getNumLenParams() > 0) 2493 TODO(loc, "derived type array expression temp with length parameters"); 2494 mlir::Value temp = seqTy.hasConstantShape() 2495 ? builder.create<fir::AllocMemOp>(loc, type) 2496 : builder.create<fir::AllocMemOp>( 2497 loc, type, ".array.expr", llvm::None, shape); 2498 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 2499 stmtCtx.attachCleanup( 2500 [bldr, loc, temp]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 2501 mlir::Value shapeOp = genShapeOp(shape); 2502 return builder.create<fir::ArrayLoadOp>(loc, seqTy, temp, shapeOp, 2503 /*slice=*/mlir::Value{}, 2504 llvm::None); 2505 } 2506 2507 static fir::ShapeOp genShapeOp(mlir::Location loc, fir::FirOpBuilder &builder, 2508 llvm::ArrayRef<mlir::Value> shape) { 2509 mlir::IndexType idxTy = builder.getIndexType(); 2510 llvm::SmallVector<mlir::Value> idxShape; 2511 for (auto s : shape) 2512 idxShape.push_back(builder.createConvert(loc, idxTy, s)); 2513 auto shapeTy = fir::ShapeType::get(builder.getContext(), idxShape.size()); 2514 return builder.create<fir::ShapeOp>(loc, shapeTy, idxShape); 2515 } 2516 2517 fir::ShapeOp genShapeOp(llvm::ArrayRef<mlir::Value> shape) { 2518 return genShapeOp(getLoc(), builder, shape); 2519 } 2520 2521 //===--------------------------------------------------------------------===// 2522 // Expression traversal and lowering. 2523 //===--------------------------------------------------------------------===// 2524 2525 /// Lower the expression, \p x, in a scalar context. 2526 template <typename A> 2527 ExtValue asScalar(const A &x) { 2528 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.genval(x); 2529 } 2530 2531 /// Lower the expression in a scalar context to a memory reference. 2532 template <typename A> 2533 ExtValue asScalarRef(const A &x) { 2534 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.gen(x); 2535 } 2536 2537 /// Lower an expression without dereferencing any indirection that may be 2538 /// a nullptr (because this is an absent optional or unallocated/disassociated 2539 /// descriptor). The returned expression cannot be addressed directly, it is 2540 /// meant to inquire about its status before addressing the related entity. 2541 template <typename A> 2542 ExtValue asInquired(const A &x) { 2543 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx} 2544 .lowerIntrinsicArgumentAsInquired(x); 2545 } 2546 2547 // An expression with non-zero rank is an array expression. 2548 template <typename A> 2549 bool isArray(const A &x) const { 2550 return x.Rank() != 0; 2551 } 2552 2553 /// Some temporaries are allocated on an element-by-element basis during the 2554 /// array expression evaluation. Collect the cleanups here so the resources 2555 /// can be freed before the next loop iteration, avoiding memory leaks. etc. 2556 Fortran::lower::StatementContext &getElementCtx() { 2557 if (!elementCtx) { 2558 stmtCtx.pushScope(); 2559 elementCtx = true; 2560 } 2561 return stmtCtx; 2562 } 2563 2564 /// If there were temporaries created for this element evaluation, finalize 2565 /// and deallocate the resources now. This should be done just prior the the 2566 /// fir::ResultOp at the end of the innermost loop. 2567 void finalizeElementCtx() { 2568 if (elementCtx) { 2569 stmtCtx.finalize(/*popScope=*/true); 2570 elementCtx = false; 2571 } 2572 } 2573 2574 /// Lower an elemental function array argument. This ensures array 2575 /// sub-expressions that are not variables and must be passed by address 2576 /// are lowered by value and placed in memory. 2577 template <typename A> 2578 CC genElementalArgument(const A &x) { 2579 // Ensure the returned element is in memory if this is what was requested. 2580 if ((semant == ConstituentSemantics::RefOpaque || 2581 semant == ConstituentSemantics::DataAddr || 2582 semant == ConstituentSemantics::ByValueArg)) { 2583 if (!Fortran::evaluate::IsVariable(x)) { 2584 PushSemantics(ConstituentSemantics::DataValue); 2585 CC cc = genarr(x); 2586 mlir::Location loc = getLoc(); 2587 if (isParenthesizedVariable(x)) { 2588 // Parenthesised variables are lowered to a reference to the variable 2589 // storage. When passing it as an argument, a copy must be passed. 2590 return [=](IterSpace iters) -> ExtValue { 2591 return createInMemoryScalarCopy(builder, loc, cc(iters)); 2592 }; 2593 } 2594 mlir::Type storageType = 2595 fir::unwrapSequenceType(converter.genType(toEvExpr(x))); 2596 return [=](IterSpace iters) -> ExtValue { 2597 return placeScalarValueInMemory(builder, loc, cc(iters), storageType); 2598 }; 2599 } 2600 } 2601 return genarr(x); 2602 } 2603 2604 // A procedure reference to a Fortran elemental intrinsic procedure. 2605 CC genElementalIntrinsicProcRef( 2606 const Fortran::evaluate::ProcedureRef &procRef, 2607 llvm::Optional<mlir::Type> retTy, 2608 const Fortran::evaluate::SpecificIntrinsic &intrinsic) { 2609 llvm::SmallVector<CC> operands; 2610 llvm::StringRef name = intrinsic.name; 2611 const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering = 2612 Fortran::lower::getIntrinsicArgumentLowering(name); 2613 mlir::Location loc = getLoc(); 2614 if (Fortran::lower::intrinsicRequiresCustomOptionalHandling( 2615 procRef, intrinsic, converter)) { 2616 using CcPairT = std::pair<CC, llvm::Optional<mlir::Value>>; 2617 llvm::SmallVector<CcPairT> operands; 2618 auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) { 2619 if (expr.Rank() == 0) { 2620 ExtValue optionalArg = this->asInquired(expr); 2621 mlir::Value isPresent = 2622 genActualIsPresentTest(builder, loc, optionalArg); 2623 operands.emplace_back( 2624 [=](IterSpace iters) -> ExtValue { 2625 return genLoad(builder, loc, optionalArg); 2626 }, 2627 isPresent); 2628 } else { 2629 auto [cc, isPresent, _] = this->genOptionalArrayFetch(expr); 2630 operands.emplace_back(cc, isPresent); 2631 } 2632 }; 2633 auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) { 2634 PushSemantics(ConstituentSemantics::RefTransparent); 2635 operands.emplace_back(genElementalArgument(expr), llvm::None); 2636 }; 2637 Fortran::lower::prepareCustomIntrinsicArgument( 2638 procRef, intrinsic, retTy, prepareOptionalArg, prepareOtherArg, 2639 converter); 2640 2641 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 2642 llvm::StringRef name = intrinsic.name; 2643 return [=](IterSpace iters) -> ExtValue { 2644 auto getArgument = [&](std::size_t i) -> ExtValue { 2645 return operands[i].first(iters); 2646 }; 2647 auto isPresent = [&](std::size_t i) -> llvm::Optional<mlir::Value> { 2648 return operands[i].second; 2649 }; 2650 return Fortran::lower::lowerCustomIntrinsic( 2651 *bldr, loc, name, retTy, isPresent, getArgument, operands.size(), 2652 getElementCtx()); 2653 }; 2654 } 2655 /// Otherwise, pre-lower arguments and use intrinsic lowering utility. 2656 for (const auto &[arg, dummy] : 2657 llvm::zip(procRef.arguments(), 2658 intrinsic.characteristics.value().dummyArguments)) { 2659 const auto *expr = 2660 Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg); 2661 if (!expr) { 2662 // Absent optional. 2663 operands.emplace_back([=](IterSpace) { return mlir::Value{}; }); 2664 } else if (!argLowering) { 2665 // No argument lowering instruction, lower by value. 2666 PushSemantics(ConstituentSemantics::RefTransparent); 2667 operands.emplace_back(genElementalArgument(*expr)); 2668 } else { 2669 // Ad-hoc argument lowering handling. 2670 Fortran::lower::ArgLoweringRule argRules = 2671 Fortran::lower::lowerIntrinsicArgumentAs(getLoc(), *argLowering, 2672 dummy.name); 2673 if (argRules.handleDynamicOptional && 2674 Fortran::evaluate::MayBePassedAsAbsentOptional( 2675 *expr, converter.getFoldingContext())) { 2676 // Currently, there is not elemental intrinsic that requires lowering 2677 // a potentially absent argument to something else than a value (apart 2678 // from character MAX/MIN that are handled elsewhere.) 2679 if (argRules.lowerAs != Fortran::lower::LowerIntrinsicArgAs::Value) 2680 TODO(loc, "lowering non trivial optional elemental intrinsic array " 2681 "argument"); 2682 PushSemantics(ConstituentSemantics::RefTransparent); 2683 operands.emplace_back(genarrForwardOptionalArgumentToCall(*expr)); 2684 continue; 2685 } 2686 switch (argRules.lowerAs) { 2687 case Fortran::lower::LowerIntrinsicArgAs::Value: { 2688 PushSemantics(ConstituentSemantics::RefTransparent); 2689 operands.emplace_back(genElementalArgument(*expr)); 2690 } break; 2691 case Fortran::lower::LowerIntrinsicArgAs::Addr: { 2692 // Note: assume does not have Fortran VALUE attribute semantics. 2693 PushSemantics(ConstituentSemantics::RefOpaque); 2694 operands.emplace_back(genElementalArgument(*expr)); 2695 } break; 2696 case Fortran::lower::LowerIntrinsicArgAs::Box: { 2697 PushSemantics(ConstituentSemantics::RefOpaque); 2698 auto lambda = genElementalArgument(*expr); 2699 operands.emplace_back([=](IterSpace iters) { 2700 return builder.createBox(loc, lambda(iters)); 2701 }); 2702 } break; 2703 case Fortran::lower::LowerIntrinsicArgAs::Inquired: 2704 TODO(loc, "intrinsic function with inquired argument"); 2705 break; 2706 } 2707 } 2708 } 2709 2710 // Let the intrinsic library lower the intrinsic procedure call 2711 return [=](IterSpace iters) { 2712 llvm::SmallVector<ExtValue> args; 2713 for (const auto &cc : operands) 2714 args.push_back(cc(iters)); 2715 return Fortran::lower::genIntrinsicCall(builder, loc, name, retTy, args, 2716 getElementCtx()); 2717 }; 2718 } 2719 2720 /// Generate a procedure reference. This code is shared for both functions and 2721 /// subroutines, the difference being reflected by `retTy`. 2722 CC genProcRef(const Fortran::evaluate::ProcedureRef &procRef, 2723 llvm::Optional<mlir::Type> retTy) { 2724 mlir::Location loc = getLoc(); 2725 if (procRef.IsElemental()) { 2726 if (const Fortran::evaluate::SpecificIntrinsic *intrin = 2727 procRef.proc().GetSpecificIntrinsic()) { 2728 // All elemental intrinsic functions are pure and cannot modify their 2729 // arguments. The only elemental subroutine, MVBITS has an Intent(inout) 2730 // argument. So for this last one, loops must be in element order 2731 // according to 15.8.3 p1. 2732 if (!retTy) 2733 setUnordered(false); 2734 2735 // Elemental intrinsic call. 2736 // The intrinsic procedure is called once per element of the array. 2737 return genElementalIntrinsicProcRef(procRef, retTy, *intrin); 2738 } 2739 if (ScalarExprLowering::isStatementFunctionCall(procRef)) 2740 fir::emitFatalError(loc, "statement function cannot be elemental"); 2741 2742 TODO(loc, "elemental user defined proc ref"); 2743 } 2744 2745 // Transformational call. 2746 // The procedure is called once and produces a value of rank > 0. 2747 if (const Fortran::evaluate::SpecificIntrinsic *intrinsic = 2748 procRef.proc().GetSpecificIntrinsic()) { 2749 if (explicitSpaceIsActive() && procRef.Rank() == 0) { 2750 // Elide any implicit loop iters. 2751 return [=, &procRef](IterSpace) { 2752 return ScalarExprLowering{loc, converter, symMap, stmtCtx} 2753 .genIntrinsicRef(procRef, *intrinsic, retTy); 2754 }; 2755 } 2756 return genarr( 2757 ScalarExprLowering{loc, converter, symMap, stmtCtx}.genIntrinsicRef( 2758 procRef, *intrinsic, retTy)); 2759 } 2760 2761 if (explicitSpaceIsActive() && procRef.Rank() == 0) { 2762 // Elide any implicit loop iters. 2763 return [=, &procRef](IterSpace) { 2764 return ScalarExprLowering{loc, converter, symMap, stmtCtx} 2765 .genProcedureRef(procRef, retTy); 2766 }; 2767 } 2768 // In the default case, the call can be hoisted out of the loop nest. Apply 2769 // the iterations to the result, which may be an array value. 2770 return genarr( 2771 ScalarExprLowering{loc, converter, symMap, stmtCtx}.genProcedureRef( 2772 procRef, retTy)); 2773 } 2774 2775 template <typename A> 2776 CC genScalarAndForwardValue(const A &x) { 2777 ExtValue result = asScalar(x); 2778 return [=](IterSpace) { return result; }; 2779 } 2780 2781 template <typename A, typename = std::enable_if_t<Fortran::common::HasMember< 2782 A, Fortran::evaluate::TypelessExpression>>> 2783 CC genarr(const A &x) { 2784 return genScalarAndForwardValue(x); 2785 } 2786 2787 template <typename A> 2788 CC genarr(const Fortran::evaluate::Expr<A> &x) { 2789 LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(llvm::dbgs(), x)); 2790 if (isArray(x) || explicitSpaceIsActive() || 2791 isElementalProcWithArrayArgs(x)) 2792 return std::visit([&](const auto &e) { return genarr(e); }, x.u); 2793 return genScalarAndForwardValue(x); 2794 } 2795 2796 template <Fortran::common::TypeCategory TC1, int KIND, 2797 Fortran::common::TypeCategory TC2> 2798 CC genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, 2799 TC2> &x) { 2800 TODO(getLoc(), ""); 2801 } 2802 2803 template <int KIND> 2804 CC genarr(const Fortran::evaluate::ComplexComponent<KIND> &x) { 2805 TODO(getLoc(), ""); 2806 } 2807 2808 template <typename T> 2809 CC genarr(const Fortran::evaluate::Parentheses<T> &x) { 2810 TODO(getLoc(), ""); 2811 } 2812 2813 template <int KIND> 2814 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 2815 Fortran::common::TypeCategory::Integer, KIND>> &x) { 2816 TODO(getLoc(), ""); 2817 } 2818 2819 template <int KIND> 2820 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 2821 Fortran::common::TypeCategory::Real, KIND>> &x) { 2822 TODO(getLoc(), ""); 2823 } 2824 template <int KIND> 2825 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 2826 Fortran::common::TypeCategory::Complex, KIND>> &x) { 2827 TODO(getLoc(), ""); 2828 } 2829 2830 //===--------------------------------------------------------------------===// 2831 // Binary elemental ops 2832 //===--------------------------------------------------------------------===// 2833 2834 template <typename OP, typename A> 2835 CC createBinaryOp(const A &evEx) { 2836 mlir::Location loc = getLoc(); 2837 auto lambda = genarr(evEx.left()); 2838 auto rf = genarr(evEx.right()); 2839 return [=](IterSpace iters) -> ExtValue { 2840 mlir::Value left = fir::getBase(lambda(iters)); 2841 mlir::Value right = fir::getBase(rf(iters)); 2842 return builder.create<OP>(loc, left, right); 2843 }; 2844 } 2845 2846 #undef GENBIN 2847 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \ 2848 template <int KIND> \ 2849 CC genarr(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \ 2850 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \ 2851 return createBinaryOp<GenBinFirOp>(x); \ 2852 } 2853 2854 GENBIN(Add, Integer, mlir::arith::AddIOp) 2855 GENBIN(Add, Real, mlir::arith::AddFOp) 2856 GENBIN(Add, Complex, fir::AddcOp) 2857 GENBIN(Subtract, Integer, mlir::arith::SubIOp) 2858 GENBIN(Subtract, Real, mlir::arith::SubFOp) 2859 GENBIN(Subtract, Complex, fir::SubcOp) 2860 GENBIN(Multiply, Integer, mlir::arith::MulIOp) 2861 GENBIN(Multiply, Real, mlir::arith::MulFOp) 2862 GENBIN(Multiply, Complex, fir::MulcOp) 2863 GENBIN(Divide, Integer, mlir::arith::DivSIOp) 2864 GENBIN(Divide, Real, mlir::arith::DivFOp) 2865 GENBIN(Divide, Complex, fir::DivcOp) 2866 2867 template <Fortran::common::TypeCategory TC, int KIND> 2868 CC genarr( 2869 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) { 2870 TODO(getLoc(), "genarr "); 2871 } 2872 template <Fortran::common::TypeCategory TC, int KIND> 2873 CC genarr( 2874 const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) { 2875 TODO(getLoc(), "genarr "); 2876 } 2877 template <Fortran::common::TypeCategory TC, int KIND> 2878 CC genarr( 2879 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>> 2880 &x) { 2881 TODO(getLoc(), "genarr "); 2882 } 2883 template <int KIND> 2884 CC genarr(const Fortran::evaluate::ComplexConstructor<KIND> &x) { 2885 TODO(getLoc(), "genarr "); 2886 } 2887 2888 template <int KIND> 2889 CC genarr(const Fortran::evaluate::Concat<KIND> &x) { 2890 TODO(getLoc(), "genarr "); 2891 } 2892 2893 template <int KIND> 2894 CC genarr(const Fortran::evaluate::SetLength<KIND> &x) { 2895 TODO(getLoc(), "genarr "); 2896 } 2897 2898 template <typename A> 2899 CC genarr(const Fortran::evaluate::Constant<A> &x) { 2900 TODO(getLoc(), "genarr "); 2901 } 2902 2903 CC genarr(const Fortran::semantics::SymbolRef &sym, 2904 ComponentPath &components) { 2905 return genarr(sym.get(), components); 2906 } 2907 2908 ExtValue abstractArrayExtValue(mlir::Value val, mlir::Value len = {}) { 2909 return convertToArrayBoxValue(getLoc(), builder, val, len); 2910 } 2911 2912 CC genarr(const ExtValue &extMemref) { 2913 ComponentPath dummy(/*isImplicit=*/true); 2914 return genarr(extMemref, dummy); 2915 } 2916 2917 //===--------------------------------------------------------------------===// 2918 // Array construction 2919 //===--------------------------------------------------------------------===// 2920 2921 /// Target agnostic computation of the size of an element in the array. 2922 /// Returns the size in bytes with type `index` or a null Value if the element 2923 /// size is not constant. 2924 mlir::Value computeElementSize(const ExtValue &exv, mlir::Type eleTy, 2925 mlir::Type resTy) { 2926 mlir::Location loc = getLoc(); 2927 mlir::IndexType idxTy = builder.getIndexType(); 2928 mlir::Value multiplier = builder.createIntegerConstant(loc, idxTy, 1); 2929 if (fir::hasDynamicSize(eleTy)) { 2930 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 2931 // Array of char with dynamic length parameter. Downcast to an array 2932 // of singleton char, and scale by the len type parameter from 2933 // `exv`. 2934 exv.match( 2935 [&](const fir::CharBoxValue &cb) { multiplier = cb.getLen(); }, 2936 [&](const fir::CharArrayBoxValue &cb) { multiplier = cb.getLen(); }, 2937 [&](const fir::BoxValue &box) { 2938 multiplier = fir::factory::CharacterExprHelper(builder, loc) 2939 .readLengthFromBox(box.getAddr()); 2940 }, 2941 [&](const fir::MutableBoxValue &box) { 2942 multiplier = fir::factory::CharacterExprHelper(builder, loc) 2943 .readLengthFromBox(box.getAddr()); 2944 }, 2945 [&](const auto &) { 2946 fir::emitFatalError(loc, 2947 "array constructor element has unknown size"); 2948 }); 2949 fir::CharacterType newEleTy = fir::CharacterType::getSingleton( 2950 eleTy.getContext(), charTy.getFKind()); 2951 if (auto seqTy = resTy.dyn_cast<fir::SequenceType>()) { 2952 assert(eleTy == seqTy.getEleTy()); 2953 resTy = fir::SequenceType::get(seqTy.getShape(), newEleTy); 2954 } 2955 eleTy = newEleTy; 2956 } else { 2957 TODO(loc, "dynamic sized type"); 2958 } 2959 } 2960 mlir::Type eleRefTy = builder.getRefType(eleTy); 2961 mlir::Type resRefTy = builder.getRefType(resTy); 2962 mlir::Value nullPtr = builder.createNullConstant(loc, resRefTy); 2963 auto offset = builder.create<fir::CoordinateOp>( 2964 loc, eleRefTy, nullPtr, mlir::ValueRange{multiplier}); 2965 return builder.createConvert(loc, idxTy, offset); 2966 } 2967 2968 /// Get the function signature of the LLVM memcpy intrinsic. 2969 mlir::FunctionType memcpyType() { 2970 return fir::factory::getLlvmMemcpy(builder).getType(); 2971 } 2972 2973 /// Create a call to the LLVM memcpy intrinsic. 2974 void createCallMemcpy(llvm::ArrayRef<mlir::Value> args) { 2975 mlir::Location loc = getLoc(); 2976 mlir::FuncOp memcpyFunc = fir::factory::getLlvmMemcpy(builder); 2977 mlir::SymbolRefAttr funcSymAttr = 2978 builder.getSymbolRefAttr(memcpyFunc.getName()); 2979 mlir::FunctionType funcTy = memcpyFunc.getType(); 2980 builder.create<fir::CallOp>(loc, funcTy.getResults(), funcSymAttr, args); 2981 } 2982 2983 // Construct code to check for a buffer overrun and realloc the buffer when 2984 // space is depleted. This is done between each item in the ac-value-list. 2985 mlir::Value growBuffer(mlir::Value mem, mlir::Value needed, 2986 mlir::Value bufferSize, mlir::Value buffSize, 2987 mlir::Value eleSz) { 2988 mlir::Location loc = getLoc(); 2989 mlir::FuncOp reallocFunc = fir::factory::getRealloc(builder); 2990 auto cond = builder.create<mlir::arith::CmpIOp>( 2991 loc, mlir::arith::CmpIPredicate::sle, bufferSize, needed); 2992 auto ifOp = builder.create<fir::IfOp>(loc, mem.getType(), cond, 2993 /*withElseRegion=*/true); 2994 auto insPt = builder.saveInsertionPoint(); 2995 builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); 2996 // Not enough space, resize the buffer. 2997 mlir::IndexType idxTy = builder.getIndexType(); 2998 mlir::Value two = builder.createIntegerConstant(loc, idxTy, 2); 2999 auto newSz = builder.create<mlir::arith::MulIOp>(loc, needed, two); 3000 builder.create<fir::StoreOp>(loc, newSz, buffSize); 3001 mlir::Value byteSz = builder.create<mlir::arith::MulIOp>(loc, newSz, eleSz); 3002 mlir::SymbolRefAttr funcSymAttr = 3003 builder.getSymbolRefAttr(reallocFunc.getName()); 3004 mlir::FunctionType funcTy = reallocFunc.getType(); 3005 auto newMem = builder.create<fir::CallOp>( 3006 loc, funcTy.getResults(), funcSymAttr, 3007 llvm::ArrayRef<mlir::Value>{ 3008 builder.createConvert(loc, funcTy.getInputs()[0], mem), 3009 builder.createConvert(loc, funcTy.getInputs()[1], byteSz)}); 3010 mlir::Value castNewMem = 3011 builder.createConvert(loc, mem.getType(), newMem.getResult(0)); 3012 builder.create<fir::ResultOp>(loc, castNewMem); 3013 builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 3014 // Otherwise, just forward the buffer. 3015 builder.create<fir::ResultOp>(loc, mem); 3016 builder.restoreInsertionPoint(insPt); 3017 return ifOp.getResult(0); 3018 } 3019 3020 /// Copy the next value (or vector of values) into the array being 3021 /// constructed. 3022 mlir::Value copyNextArrayCtorSection(const ExtValue &exv, mlir::Value buffPos, 3023 mlir::Value buffSize, mlir::Value mem, 3024 mlir::Value eleSz, mlir::Type eleTy, 3025 mlir::Type eleRefTy, mlir::Type resTy) { 3026 mlir::Location loc = getLoc(); 3027 auto off = builder.create<fir::LoadOp>(loc, buffPos); 3028 auto limit = builder.create<fir::LoadOp>(loc, buffSize); 3029 mlir::IndexType idxTy = builder.getIndexType(); 3030 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); 3031 3032 if (fir::isRecordWithAllocatableMember(eleTy)) 3033 TODO(loc, "deep copy on allocatable members"); 3034 3035 if (!eleSz) { 3036 // Compute the element size at runtime. 3037 assert(fir::hasDynamicSize(eleTy)); 3038 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 3039 auto charBytes = 3040 builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8; 3041 mlir::Value bytes = 3042 builder.createIntegerConstant(loc, idxTy, charBytes); 3043 mlir::Value length = fir::getLen(exv); 3044 if (!length) 3045 fir::emitFatalError(loc, "result is not boxed character"); 3046 eleSz = builder.create<mlir::arith::MulIOp>(loc, bytes, length); 3047 } else { 3048 TODO(loc, "PDT size"); 3049 // Will call the PDT's size function with the type parameters. 3050 } 3051 } 3052 3053 // Compute the coordinate using `fir.coordinate_of`, or, if the type has 3054 // dynamic size, generating the pointer arithmetic. 3055 auto computeCoordinate = [&](mlir::Value buff, mlir::Value off) { 3056 mlir::Type refTy = eleRefTy; 3057 if (fir::hasDynamicSize(eleTy)) { 3058 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 3059 // Scale a simple pointer using dynamic length and offset values. 3060 auto chTy = fir::CharacterType::getSingleton(charTy.getContext(), 3061 charTy.getFKind()); 3062 refTy = builder.getRefType(chTy); 3063 mlir::Type toTy = builder.getRefType(builder.getVarLenSeqTy(chTy)); 3064 buff = builder.createConvert(loc, toTy, buff); 3065 off = builder.create<mlir::arith::MulIOp>(loc, off, eleSz); 3066 } else { 3067 TODO(loc, "PDT offset"); 3068 } 3069 } 3070 auto coor = builder.create<fir::CoordinateOp>(loc, refTy, buff, 3071 mlir::ValueRange{off}); 3072 return builder.createConvert(loc, eleRefTy, coor); 3073 }; 3074 3075 // Lambda to lower an abstract array box value. 3076 auto doAbstractArray = [&](const auto &v) { 3077 // Compute the array size. 3078 mlir::Value arrSz = one; 3079 for (auto ext : v.getExtents()) 3080 arrSz = builder.create<mlir::arith::MulIOp>(loc, arrSz, ext); 3081 3082 // Grow the buffer as needed. 3083 auto endOff = builder.create<mlir::arith::AddIOp>(loc, off, arrSz); 3084 mem = growBuffer(mem, endOff, limit, buffSize, eleSz); 3085 3086 // Copy the elements to the buffer. 3087 mlir::Value byteSz = 3088 builder.create<mlir::arith::MulIOp>(loc, arrSz, eleSz); 3089 auto buff = builder.createConvert(loc, fir::HeapType::get(resTy), mem); 3090 mlir::Value buffi = computeCoordinate(buff, off); 3091 llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( 3092 builder, loc, memcpyType(), buffi, v.getAddr(), byteSz, 3093 /*volatile=*/builder.createBool(loc, false)); 3094 createCallMemcpy(args); 3095 3096 // Save the incremented buffer position. 3097 builder.create<fir::StoreOp>(loc, endOff, buffPos); 3098 }; 3099 3100 // Copy a trivial scalar value into the buffer. 3101 auto doTrivialScalar = [&](const ExtValue &v, mlir::Value len = {}) { 3102 // Increment the buffer position. 3103 auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one); 3104 3105 // Grow the buffer as needed. 3106 mem = growBuffer(mem, plusOne, limit, buffSize, eleSz); 3107 3108 // Store the element in the buffer. 3109 mlir::Value buff = 3110 builder.createConvert(loc, fir::HeapType::get(resTy), mem); 3111 auto buffi = builder.create<fir::CoordinateOp>(loc, eleRefTy, buff, 3112 mlir::ValueRange{off}); 3113 fir::factory::genScalarAssignment( 3114 builder, loc, 3115 [&]() -> ExtValue { 3116 if (len) 3117 return fir::CharBoxValue(buffi, len); 3118 return buffi; 3119 }(), 3120 v); 3121 builder.create<fir::StoreOp>(loc, plusOne, buffPos); 3122 }; 3123 3124 // Copy the value. 3125 exv.match( 3126 [&](mlir::Value) { doTrivialScalar(exv); }, 3127 [&](const fir::CharBoxValue &v) { 3128 auto buffer = v.getBuffer(); 3129 if (fir::isa_char(buffer.getType())) { 3130 doTrivialScalar(exv, eleSz); 3131 } else { 3132 // Increment the buffer position. 3133 auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one); 3134 3135 // Grow the buffer as needed. 3136 mem = growBuffer(mem, plusOne, limit, buffSize, eleSz); 3137 3138 // Store the element in the buffer. 3139 mlir::Value buff = 3140 builder.createConvert(loc, fir::HeapType::get(resTy), mem); 3141 mlir::Value buffi = computeCoordinate(buff, off); 3142 llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( 3143 builder, loc, memcpyType(), buffi, v.getAddr(), eleSz, 3144 /*volatile=*/builder.createBool(loc, false)); 3145 createCallMemcpy(args); 3146 3147 builder.create<fir::StoreOp>(loc, plusOne, buffPos); 3148 } 3149 }, 3150 [&](const fir::ArrayBoxValue &v) { doAbstractArray(v); }, 3151 [&](const fir::CharArrayBoxValue &v) { doAbstractArray(v); }, 3152 [&](const auto &) { 3153 TODO(loc, "unhandled array constructor expression"); 3154 }); 3155 return mem; 3156 } 3157 3158 // Lower the expr cases in an ac-value-list. 3159 template <typename A> 3160 std::pair<ExtValue, bool> 3161 genArrayCtorInitializer(const Fortran::evaluate::Expr<A> &x, mlir::Type, 3162 mlir::Value, mlir::Value, mlir::Value, 3163 Fortran::lower::StatementContext &stmtCtx) { 3164 if (isArray(x)) 3165 return {lowerNewArrayExpression(converter, symMap, stmtCtx, toEvExpr(x)), 3166 /*needCopy=*/true}; 3167 return {asScalar(x), /*needCopy=*/true}; 3168 } 3169 3170 // Lower an ac-implied-do in an ac-value-list. 3171 template <typename A> 3172 std::pair<ExtValue, bool> 3173 genArrayCtorInitializer(const Fortran::evaluate::ImpliedDo<A> &x, 3174 mlir::Type resTy, mlir::Value mem, 3175 mlir::Value buffPos, mlir::Value buffSize, 3176 Fortran::lower::StatementContext &) { 3177 mlir::Location loc = getLoc(); 3178 mlir::IndexType idxTy = builder.getIndexType(); 3179 mlir::Value lo = 3180 builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.lower()))); 3181 mlir::Value up = 3182 builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.upper()))); 3183 mlir::Value step = 3184 builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.stride()))); 3185 auto seqTy = resTy.template cast<fir::SequenceType>(); 3186 mlir::Type eleTy = fir::unwrapSequenceType(seqTy); 3187 auto loop = 3188 builder.create<fir::DoLoopOp>(loc, lo, up, step, /*unordered=*/false, 3189 /*finalCount=*/false, mem); 3190 // create a new binding for x.name(), to ac-do-variable, to the iteration 3191 // value. 3192 symMap.pushImpliedDoBinding(toStringRef(x.name()), loop.getInductionVar()); 3193 auto insPt = builder.saveInsertionPoint(); 3194 builder.setInsertionPointToStart(loop.getBody()); 3195 // Thread mem inside the loop via loop argument. 3196 mem = loop.getRegionIterArgs()[0]; 3197 3198 mlir::Type eleRefTy = builder.getRefType(eleTy); 3199 3200 // Any temps created in the loop body must be freed inside the loop body. 3201 stmtCtx.pushScope(); 3202 llvm::Optional<mlir::Value> charLen; 3203 for (const Fortran::evaluate::ArrayConstructorValue<A> &acv : x.values()) { 3204 auto [exv, copyNeeded] = std::visit( 3205 [&](const auto &v) { 3206 return genArrayCtorInitializer(v, resTy, mem, buffPos, buffSize, 3207 stmtCtx); 3208 }, 3209 acv.u); 3210 mlir::Value eleSz = computeElementSize(exv, eleTy, resTy); 3211 mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem, 3212 eleSz, eleTy, eleRefTy, resTy) 3213 : fir::getBase(exv); 3214 if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) { 3215 charLen = builder.createTemporary(loc, builder.getI64Type()); 3216 mlir::Value castLen = 3217 builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv)); 3218 builder.create<fir::StoreOp>(loc, castLen, charLen.getValue()); 3219 } 3220 } 3221 stmtCtx.finalize(/*popScope=*/true); 3222 3223 builder.create<fir::ResultOp>(loc, mem); 3224 builder.restoreInsertionPoint(insPt); 3225 mem = loop.getResult(0); 3226 symMap.popImpliedDoBinding(); 3227 llvm::SmallVector<mlir::Value> extents = { 3228 builder.create<fir::LoadOp>(loc, buffPos).getResult()}; 3229 3230 // Convert to extended value. 3231 if (fir::isa_char(seqTy.getEleTy())) { 3232 auto len = builder.create<fir::LoadOp>(loc, charLen.getValue()); 3233 return {fir::CharArrayBoxValue{mem, len, extents}, /*needCopy=*/false}; 3234 } 3235 return {fir::ArrayBoxValue{mem, extents}, /*needCopy=*/false}; 3236 } 3237 3238 // To simplify the handling and interaction between the various cases, array 3239 // constructors are always lowered to the incremental construction code 3240 // pattern, even if the extent of the array value is constant. After the 3241 // MemToReg pass and constant folding, the optimizer should be able to 3242 // determine that all the buffer overrun tests are false when the 3243 // incremental construction wasn't actually required. 3244 template <typename A> 3245 CC genarr(const Fortran::evaluate::ArrayConstructor<A> &x) { 3246 mlir::Location loc = getLoc(); 3247 auto evExpr = toEvExpr(x); 3248 mlir::Type resTy = translateSomeExprToFIRType(converter, evExpr); 3249 mlir::IndexType idxTy = builder.getIndexType(); 3250 auto seqTy = resTy.template cast<fir::SequenceType>(); 3251 mlir::Type eleTy = fir::unwrapSequenceType(resTy); 3252 mlir::Value buffSize = builder.createTemporary(loc, idxTy, ".buff.size"); 3253 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); 3254 mlir::Value buffPos = builder.createTemporary(loc, idxTy, ".buff.pos"); 3255 builder.create<fir::StoreOp>(loc, zero, buffPos); 3256 // Allocate space for the array to be constructed. 3257 mlir::Value mem; 3258 if (fir::hasDynamicSize(resTy)) { 3259 if (fir::hasDynamicSize(eleTy)) { 3260 // The size of each element may depend on a general expression. Defer 3261 // creating the buffer until after the expression is evaluated. 3262 mem = builder.createNullConstant(loc, builder.getRefType(eleTy)); 3263 builder.create<fir::StoreOp>(loc, zero, buffSize); 3264 } else { 3265 mlir::Value initBuffSz = 3266 builder.createIntegerConstant(loc, idxTy, clInitialBufferSize); 3267 mem = builder.create<fir::AllocMemOp>( 3268 loc, eleTy, /*typeparams=*/llvm::None, initBuffSz); 3269 builder.create<fir::StoreOp>(loc, initBuffSz, buffSize); 3270 } 3271 } else { 3272 mem = builder.create<fir::AllocMemOp>(loc, resTy); 3273 int64_t buffSz = 1; 3274 for (auto extent : seqTy.getShape()) 3275 buffSz *= extent; 3276 mlir::Value initBuffSz = 3277 builder.createIntegerConstant(loc, idxTy, buffSz); 3278 builder.create<fir::StoreOp>(loc, initBuffSz, buffSize); 3279 } 3280 // Compute size of element 3281 mlir::Type eleRefTy = builder.getRefType(eleTy); 3282 3283 // Populate the buffer with the elements, growing as necessary. 3284 llvm::Optional<mlir::Value> charLen; 3285 for (const auto &expr : x) { 3286 auto [exv, copyNeeded] = std::visit( 3287 [&](const auto &e) { 3288 return genArrayCtorInitializer(e, resTy, mem, buffPos, buffSize, 3289 stmtCtx); 3290 }, 3291 expr.u); 3292 mlir::Value eleSz = computeElementSize(exv, eleTy, resTy); 3293 mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem, 3294 eleSz, eleTy, eleRefTy, resTy) 3295 : fir::getBase(exv); 3296 if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) { 3297 charLen = builder.createTemporary(loc, builder.getI64Type()); 3298 mlir::Value castLen = 3299 builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv)); 3300 builder.create<fir::StoreOp>(loc, castLen, charLen.getValue()); 3301 } 3302 } 3303 mem = builder.createConvert(loc, fir::HeapType::get(resTy), mem); 3304 llvm::SmallVector<mlir::Value> extents = { 3305 builder.create<fir::LoadOp>(loc, buffPos)}; 3306 3307 // Cleanup the temporary. 3308 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 3309 stmtCtx.attachCleanup( 3310 [bldr, loc, mem]() { bldr->create<fir::FreeMemOp>(loc, mem); }); 3311 3312 // Return the continuation. 3313 if (fir::isa_char(seqTy.getEleTy())) { 3314 if (charLen.hasValue()) { 3315 auto len = builder.create<fir::LoadOp>(loc, charLen.getValue()); 3316 return genarr(fir::CharArrayBoxValue{mem, len, extents}); 3317 } 3318 return genarr(fir::CharArrayBoxValue{mem, zero, extents}); 3319 } 3320 return genarr(fir::ArrayBoxValue{mem, extents}); 3321 } 3322 3323 CC genarr(const Fortran::evaluate::ImpliedDoIndex &) { 3324 TODO(getLoc(), "genarr ImpliedDoIndex"); 3325 } 3326 3327 CC genarr(const Fortran::evaluate::TypeParamInquiry &x) { 3328 TODO(getLoc(), "genarr TypeParamInquiry"); 3329 } 3330 3331 CC genarr(const Fortran::evaluate::DescriptorInquiry &x) { 3332 TODO(getLoc(), "genarr DescriptorInquiry"); 3333 } 3334 3335 CC genarr(const Fortran::evaluate::StructureConstructor &x) { 3336 TODO(getLoc(), "genarr StructureConstructor"); 3337 } 3338 3339 template <int KIND> 3340 CC genarr(const Fortran::evaluate::Not<KIND> &x) { 3341 TODO(getLoc(), "genarr Not"); 3342 } 3343 3344 template <int KIND> 3345 CC genarr(const Fortran::evaluate::LogicalOperation<KIND> &x) { 3346 TODO(getLoc(), "genarr LogicalOperation"); 3347 } 3348 3349 template <int KIND> 3350 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 3351 Fortran::common::TypeCategory::Integer, KIND>> &x) { 3352 TODO(getLoc(), "genarr Relational Integer"); 3353 } 3354 template <int KIND> 3355 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 3356 Fortran::common::TypeCategory::Character, KIND>> &x) { 3357 TODO(getLoc(), "genarr Relational Character"); 3358 } 3359 template <int KIND> 3360 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 3361 Fortran::common::TypeCategory::Real, KIND>> &x) { 3362 TODO(getLoc(), "genarr Relational Real"); 3363 } 3364 template <int KIND> 3365 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 3366 Fortran::common::TypeCategory::Complex, KIND>> &x) { 3367 TODO(getLoc(), "genarr Relational Complex"); 3368 } 3369 CC genarr( 3370 const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) { 3371 TODO(getLoc(), "genarr Relational SomeType"); 3372 } 3373 3374 template <typename A> 3375 CC genarr(const Fortran::evaluate::Designator<A> &des) { 3376 ComponentPath components(des.Rank() > 0); 3377 return std::visit([&](const auto &x) { return genarr(x, components); }, 3378 des.u); 3379 } 3380 3381 template <typename T> 3382 CC genarr(const Fortran::evaluate::FunctionRef<T> &funRef) { 3383 // Note that it's possible that the function being called returns either an 3384 // array or a scalar. In the first case, use the element type of the array. 3385 return genProcRef( 3386 funRef, fir::unwrapSequenceType(converter.genType(toEvExpr(funRef)))); 3387 } 3388 3389 template <typename A> 3390 CC genImplicitArrayAccess(const A &x, ComponentPath &components) { 3391 components.reversePath.push_back(ImplicitSubscripts{}); 3392 ExtValue exv = asScalarRef(x); 3393 // lowerPath(exv, components); 3394 auto lambda = genarr(exv, components); 3395 return [=](IterSpace iters) { return lambda(components.pc(iters)); }; 3396 } 3397 CC genImplicitArrayAccess(const Fortran::evaluate::NamedEntity &x, 3398 ComponentPath &components) { 3399 if (x.IsSymbol()) 3400 return genImplicitArrayAccess(x.GetFirstSymbol(), components); 3401 return genImplicitArrayAccess(x.GetComponent(), components); 3402 } 3403 3404 template <typename A> 3405 CC genAsScalar(const A &x) { 3406 mlir::Location loc = getLoc(); 3407 if (isProjectedCopyInCopyOut()) { 3408 return [=, &x, builder = &converter.getFirOpBuilder()]( 3409 IterSpace iters) -> ExtValue { 3410 ExtValue exv = asScalarRef(x); 3411 mlir::Value val = fir::getBase(exv); 3412 mlir::Type eleTy = fir::unwrapRefType(val.getType()); 3413 if (isAdjustedArrayElementType(eleTy)) { 3414 if (fir::isa_char(eleTy)) { 3415 TODO(getLoc(), "assignment of character type"); 3416 } else if (fir::isa_derived(eleTy)) { 3417 TODO(loc, "assignment of derived type"); 3418 } else { 3419 fir::emitFatalError(loc, "array type not expected in scalar"); 3420 } 3421 } else { 3422 builder->create<fir::StoreOp>(loc, iters.getElement(), val); 3423 } 3424 return exv; 3425 }; 3426 } 3427 return [=, &x](IterSpace) { return asScalar(x); }; 3428 } 3429 3430 CC genarr(const Fortran::semantics::Symbol &x, ComponentPath &components) { 3431 if (explicitSpaceIsActive()) { 3432 TODO(getLoc(), "genarr Symbol explicitSpace"); 3433 } else { 3434 return genImplicitArrayAccess(x, components); 3435 } 3436 } 3437 3438 CC genarr(const Fortran::evaluate::Component &x, ComponentPath &components) { 3439 TODO(getLoc(), "genarr Component"); 3440 } 3441 3442 /// Array reference with subscripts. If this has rank > 0, this is a form 3443 /// of an array section (slice). 3444 /// 3445 /// There are two "slicing" primitives that may be applied on a dimension by 3446 /// dimension basis: (1) triple notation and (2) vector addressing. Since 3447 /// dimensions can be selectively sliced, some dimensions may contain 3448 /// regular scalar expressions and those dimensions do not participate in 3449 /// the array expression evaluation. 3450 CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) { 3451 if (explicitSpaceIsActive()) { 3452 TODO(getLoc(), "genarr ArrayRef explicitSpace"); 3453 } else { 3454 if (Fortran::lower::isRankedArrayAccess(x)) { 3455 components.reversePath.push_back(&x); 3456 return genImplicitArrayAccess(x.base(), components); 3457 } 3458 } 3459 bool atEnd = pathIsEmpty(components); 3460 components.reversePath.push_back(&x); 3461 auto result = genarr(x.base(), components); 3462 if (components.applied) 3463 return result; 3464 mlir::Location loc = getLoc(); 3465 if (atEnd) { 3466 if (x.Rank() == 0) 3467 return genAsScalar(x); 3468 fir::emitFatalError(loc, "expected scalar"); 3469 } 3470 return [=](IterSpace) -> ExtValue { 3471 fir::emitFatalError(loc, "reached arrayref with path"); 3472 }; 3473 } 3474 3475 CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) { 3476 TODO(getLoc(), "coarray reference"); 3477 } 3478 3479 CC genarr(const Fortran::evaluate::NamedEntity &x, 3480 ComponentPath &components) { 3481 return x.IsSymbol() ? genarr(x.GetFirstSymbol(), components) 3482 : genarr(x.GetComponent(), components); 3483 } 3484 3485 CC genarr(const Fortran::evaluate::DataRef &x, ComponentPath &components) { 3486 return std::visit([&](const auto &v) { return genarr(v, components); }, 3487 x.u); 3488 } 3489 3490 bool pathIsEmpty(const ComponentPath &components) { 3491 return components.reversePath.empty(); 3492 } 3493 3494 /// Given an optional fir.box, returns an fir.box that is the original one if 3495 /// it is present and it otherwise an unallocated box. 3496 /// Absent fir.box are implemented as a null pointer descriptor. Generated 3497 /// code may need to unconditionally read a fir.box that can be absent. 3498 /// This helper allows creating a fir.box that can be read in all cases 3499 /// outside of a fir.if (isPresent) region. However, the usages of the value 3500 /// read from such box should still only be done in a fir.if(isPresent). 3501 static fir::ExtendedValue 3502 absentBoxToUnalllocatedBox(fir::FirOpBuilder &builder, mlir::Location loc, 3503 const fir::ExtendedValue &exv, 3504 mlir::Value isPresent) { 3505 mlir::Value box = fir::getBase(exv); 3506 mlir::Type boxType = box.getType(); 3507 assert(boxType.isa<fir::BoxType>() && "argument must be a fir.box"); 3508 mlir::Value emptyBox = 3509 fir::factory::createUnallocatedBox(builder, loc, boxType, llvm::None); 3510 auto safeToReadBox = 3511 builder.create<mlir::arith::SelectOp>(loc, isPresent, box, emptyBox); 3512 return fir::substBase(exv, safeToReadBox); 3513 } 3514 3515 std::tuple<CC, mlir::Value, mlir::Type> 3516 genOptionalArrayFetch(const Fortran::lower::SomeExpr &expr) { 3517 assert(expr.Rank() > 0 && "expr must be an array"); 3518 mlir::Location loc = getLoc(); 3519 ExtValue optionalArg = asInquired(expr); 3520 mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg); 3521 // Generate an array load and access to an array that may be an absent 3522 // optional or an unallocated optional. 3523 mlir::Value base = getBase(optionalArg); 3524 const bool hasOptionalAttr = 3525 fir::valueHasFirAttribute(base, fir::getOptionalAttrName()); 3526 mlir::Type baseType = fir::unwrapRefType(base.getType()); 3527 const bool isBox = baseType.isa<fir::BoxType>(); 3528 const bool isAllocOrPtr = Fortran::evaluate::IsAllocatableOrPointerObject( 3529 expr, converter.getFoldingContext()); 3530 mlir::Type arrType = fir::unwrapPassByRefType(baseType); 3531 mlir::Type eleType = fir::unwrapSequenceType(arrType); 3532 ExtValue exv = optionalArg; 3533 if (hasOptionalAttr && isBox && !isAllocOrPtr) { 3534 // Elemental argument cannot be allocatable or pointers (C15100). 3535 // Hence, per 15.5.2.12 3 (8) and (9), the provided Allocatable and 3536 // Pointer optional arrays cannot be absent. The only kind of entities 3537 // that can get here are optional assumed shape and polymorphic entities. 3538 exv = absentBoxToUnalllocatedBox(builder, loc, exv, isPresent); 3539 } 3540 // All the properties can be read from any fir.box but the read values may 3541 // be undefined and should only be used inside a fir.if (canBeRead) region. 3542 if (const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>()) 3543 exv = fir::factory::genMutableBoxRead(builder, loc, *mutableBox); 3544 3545 mlir::Value memref = fir::getBase(exv); 3546 mlir::Value shape = builder.createShape(loc, exv); 3547 mlir::Value noSlice; 3548 auto arrLoad = builder.create<fir::ArrayLoadOp>( 3549 loc, arrType, memref, shape, noSlice, fir::getTypeParams(exv)); 3550 mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams(); 3551 mlir::Value arrLd = arrLoad.getResult(); 3552 // Mark the load to tell later passes it is unsafe to use this array_load 3553 // shape unconditionally. 3554 arrLoad->setAttr(fir::getOptionalAttrName(), builder.getUnitAttr()); 3555 3556 // Place the array as optional on the arrayOperands stack so that its 3557 // shape will only be used as a fallback to induce the implicit loop nest 3558 // (that is if there is no non optional array arguments). 3559 arrayOperands.push_back( 3560 ArrayOperand{memref, shape, noSlice, /*mayBeAbsent=*/true}); 3561 3562 // By value semantics. 3563 auto cc = [=](IterSpace iters) -> ExtValue { 3564 auto arrFetch = builder.create<fir::ArrayFetchOp>( 3565 loc, eleType, arrLd, iters.iterVec(), arrLdTypeParams); 3566 return fir::factory::arraySectionElementToExtendedValue( 3567 builder, loc, exv, arrFetch, noSlice); 3568 }; 3569 return {cc, isPresent, eleType}; 3570 } 3571 3572 /// Generate a continuation to pass \p expr to an OPTIONAL argument of an 3573 /// elemental procedure. This is meant to handle the cases where \p expr might 3574 /// be dynamically absent (i.e. when it is a POINTER, an ALLOCATABLE or an 3575 /// OPTIONAL variable). If p\ expr is guaranteed to be present genarr() can 3576 /// directly be called instead. 3577 CC genarrForwardOptionalArgumentToCall(const Fortran::lower::SomeExpr &expr) { 3578 mlir::Location loc = getLoc(); 3579 // Only by-value numerical and logical so far. 3580 if (semant != ConstituentSemantics::RefTransparent) 3581 TODO(loc, "optional arguments in user defined elemental procedures"); 3582 3583 // Handle scalar argument case (the if-then-else is generated outside of the 3584 // implicit loop nest). 3585 if (expr.Rank() == 0) { 3586 ExtValue optionalArg = asInquired(expr); 3587 mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg); 3588 mlir::Value elementValue = 3589 fir::getBase(genOptionalValue(builder, loc, optionalArg, isPresent)); 3590 return [=](IterSpace iters) -> ExtValue { return elementValue; }; 3591 } 3592 3593 CC cc; 3594 mlir::Value isPresent; 3595 mlir::Type eleType; 3596 std::tie(cc, isPresent, eleType) = genOptionalArrayFetch(expr); 3597 return [=](IterSpace iters) -> ExtValue { 3598 mlir::Value elementValue = 3599 builder 3600 .genIfOp(loc, {eleType}, isPresent, 3601 /*withElseRegion=*/true) 3602 .genThen([&]() { 3603 builder.create<fir::ResultOp>(loc, fir::getBase(cc(iters))); 3604 }) 3605 .genElse([&]() { 3606 mlir::Value zero = 3607 fir::factory::createZeroValue(builder, loc, eleType); 3608 builder.create<fir::ResultOp>(loc, zero); 3609 }) 3610 .getResults()[0]; 3611 return elementValue; 3612 }; 3613 } 3614 3615 CC genarr(const Fortran::evaluate::ComplexPart &x, 3616 ComponentPath &components) { 3617 TODO(getLoc(), "genarr ComplexPart"); 3618 } 3619 3620 CC genarr(const Fortran::evaluate::StaticDataObject::Pointer &, 3621 ComponentPath &components) { 3622 TODO(getLoc(), "genarr StaticDataObject::Pointer"); 3623 } 3624 3625 /// Substrings (see 9.4.1) 3626 CC genarr(const Fortran::evaluate::Substring &x, ComponentPath &components) { 3627 TODO(getLoc(), "genarr Substring"); 3628 } 3629 3630 /// Base case of generating an array reference, 3631 CC genarr(const ExtValue &extMemref, ComponentPath &components) { 3632 mlir::Location loc = getLoc(); 3633 mlir::Value memref = fir::getBase(extMemref); 3634 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(memref.getType()); 3635 assert(arrTy.isa<fir::SequenceType>() && "memory ref must be an array"); 3636 mlir::Value shape = builder.createShape(loc, extMemref); 3637 mlir::Value slice; 3638 if (components.isSlice()) { 3639 TODO(loc, "genarr with Slices"); 3640 } 3641 arrayOperands.push_back(ArrayOperand{memref, shape, slice}); 3642 if (destShape.empty()) 3643 destShape = getShape(arrayOperands.back()); 3644 if (isBoxValue()) { 3645 // Semantics are a reference to a boxed array. 3646 // This case just requires that an embox operation be created to box the 3647 // value. The value of the box is forwarded in the continuation. 3648 mlir::Type reduceTy = reduceRank(arrTy, slice); 3649 auto boxTy = fir::BoxType::get(reduceTy); 3650 if (components.substring) { 3651 // Adjust char length to substring size. 3652 fir::CharacterType charTy = 3653 fir::factory::CharacterExprHelper::getCharType(reduceTy); 3654 auto seqTy = reduceTy.cast<fir::SequenceType>(); 3655 // TODO: Use a constant for fir.char LEN if we can compute it. 3656 boxTy = fir::BoxType::get( 3657 fir::SequenceType::get(fir::CharacterType::getUnknownLen( 3658 builder.getContext(), charTy.getFKind()), 3659 seqTy.getDimension())); 3660 } 3661 mlir::Value embox = 3662 memref.getType().isa<fir::BoxType>() 3663 ? builder.create<fir::ReboxOp>(loc, boxTy, memref, shape, slice) 3664 .getResult() 3665 : builder 3666 .create<fir::EmboxOp>(loc, boxTy, memref, shape, slice, 3667 fir::getTypeParams(extMemref)) 3668 .getResult(); 3669 return [=](IterSpace) -> ExtValue { return fir::BoxValue(embox); }; 3670 } 3671 if (isReferentiallyOpaque()) { 3672 TODO(loc, "genarr isReferentiallyOpaque"); 3673 } 3674 auto arrLoad = builder.create<fir::ArrayLoadOp>( 3675 loc, arrTy, memref, shape, slice, fir::getTypeParams(extMemref)); 3676 mlir::Value arrLd = arrLoad.getResult(); 3677 if (isProjectedCopyInCopyOut()) { 3678 // Semantics are projected copy-in copy-out. 3679 // The backing store of the destination of an array expression may be 3680 // partially modified. These updates are recorded in FIR by forwarding a 3681 // continuation that generates an `array_update` Op. The destination is 3682 // always loaded at the beginning of the statement and merged at the 3683 // end. 3684 destination = arrLoad; 3685 auto lambda = ccStoreToDest.hasValue() 3686 ? ccStoreToDest.getValue() 3687 : defaultStoreToDestination(components.substring); 3688 return [=](IterSpace iters) -> ExtValue { return lambda(iters); }; 3689 } 3690 if (isCustomCopyInCopyOut()) { 3691 TODO(loc, "isCustomCopyInCopyOut"); 3692 } 3693 if (isCopyInCopyOut()) { 3694 // Semantics are copy-in copy-out. 3695 // The continuation simply forwards the result of the `array_load` Op, 3696 // which is the value of the array as it was when loaded. All data 3697 // references with rank > 0 in an array expression typically have 3698 // copy-in copy-out semantics. 3699 return [=](IterSpace) -> ExtValue { return arrLd; }; 3700 } 3701 mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams(); 3702 if (isValueAttribute()) { 3703 // Semantics are value attribute. 3704 // Here the continuation will `array_fetch` a value from an array and 3705 // then store that value in a temporary. One can thus imitate pass by 3706 // value even when the call is pass by reference. 3707 return [=](IterSpace iters) -> ExtValue { 3708 mlir::Value base; 3709 mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec()); 3710 if (isAdjustedArrayElementType(eleTy)) { 3711 mlir::Type eleRefTy = builder.getRefType(eleTy); 3712 base = builder.create<fir::ArrayAccessOp>( 3713 loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams); 3714 } else { 3715 base = builder.create<fir::ArrayFetchOp>( 3716 loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams); 3717 } 3718 mlir::Value temp = builder.createTemporary( 3719 loc, base.getType(), 3720 llvm::ArrayRef<mlir::NamedAttribute>{ 3721 Fortran::lower::getAdaptToByRefAttr(builder)}); 3722 builder.create<fir::StoreOp>(loc, base, temp); 3723 return fir::factory::arraySectionElementToExtendedValue( 3724 builder, loc, extMemref, temp, slice); 3725 }; 3726 } 3727 // In the default case, the array reference forwards an `array_fetch` or 3728 // `array_access` Op in the continuation. 3729 return [=](IterSpace iters) -> ExtValue { 3730 mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec()); 3731 if (isAdjustedArrayElementType(eleTy)) { 3732 mlir::Type eleRefTy = builder.getRefType(eleTy); 3733 mlir::Value arrayOp = builder.create<fir::ArrayAccessOp>( 3734 loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams); 3735 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 3736 llvm::SmallVector<mlir::Value> substringBounds; 3737 populateBounds(substringBounds, components.substring); 3738 if (!substringBounds.empty()) { 3739 // mlir::Value dstLen = fir::factory::genLenOfCharacter( 3740 // builder, loc, arrLoad, iters.iterVec(), substringBounds); 3741 // fir::CharBoxValue dstChar(arrayOp, dstLen); 3742 // return fir::factory::CharacterExprHelper{builder, loc} 3743 // .createSubstring(dstChar, substringBounds); 3744 } 3745 } 3746 return fir::factory::arraySectionElementToExtendedValue( 3747 builder, loc, extMemref, arrayOp, slice); 3748 } 3749 auto arrFetch = builder.create<fir::ArrayFetchOp>( 3750 loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams); 3751 return fir::factory::arraySectionElementToExtendedValue( 3752 builder, loc, extMemref, arrFetch, slice); 3753 }; 3754 } 3755 3756 /// Reduce the rank of a array to be boxed based on the slice's operands. 3757 static mlir::Type reduceRank(mlir::Type arrTy, mlir::Value slice) { 3758 if (slice) { 3759 auto slOp = mlir::dyn_cast<fir::SliceOp>(slice.getDefiningOp()); 3760 assert(slOp && "expected slice op"); 3761 auto seqTy = arrTy.dyn_cast<fir::SequenceType>(); 3762 assert(seqTy && "expected array type"); 3763 mlir::Operation::operand_range triples = slOp.getTriples(); 3764 fir::SequenceType::Shape shape; 3765 // reduce the rank for each invariant dimension 3766 for (unsigned i = 1, end = triples.size(); i < end; i += 3) 3767 if (!mlir::isa_and_nonnull<fir::UndefOp>(triples[i].getDefiningOp())) 3768 shape.push_back(fir::SequenceType::getUnknownExtent()); 3769 return fir::SequenceType::get(shape, seqTy.getEleTy()); 3770 } 3771 // not sliced, so no change in rank 3772 return arrTy; 3773 } 3774 3775 private: 3776 void determineShapeOfDest(const fir::ExtendedValue &lhs) { 3777 destShape = fir::factory::getExtents(builder, getLoc(), lhs); 3778 } 3779 3780 void determineShapeOfDest(const Fortran::lower::SomeExpr &lhs) { 3781 if (!destShape.empty()) 3782 return; 3783 // if (explicitSpaceIsActive() && determineShapeWithSlice(lhs)) 3784 // return; 3785 mlir::Type idxTy = builder.getIndexType(); 3786 mlir::Location loc = getLoc(); 3787 if (std::optional<Fortran::evaluate::ConstantSubscripts> constantShape = 3788 Fortran::evaluate::GetConstantExtents(converter.getFoldingContext(), 3789 lhs)) 3790 for (Fortran::common::ConstantSubscript extent : *constantShape) 3791 destShape.push_back(builder.createIntegerConstant(loc, idxTy, extent)); 3792 } 3793 3794 ExtValue lowerArrayExpression(const Fortran::lower::SomeExpr &exp) { 3795 mlir::Type resTy = converter.genType(exp); 3796 return std::visit( 3797 [&](const auto &e) { return lowerArrayExpression(genarr(e), resTy); }, 3798 exp.u); 3799 } 3800 ExtValue lowerArrayExpression(const ExtValue &exv) { 3801 assert(!explicitSpace); 3802 mlir::Type resTy = fir::unwrapPassByRefType(fir::getBase(exv).getType()); 3803 return lowerArrayExpression(genarr(exv), resTy); 3804 } 3805 3806 void populateBounds(llvm::SmallVectorImpl<mlir::Value> &bounds, 3807 const Fortran::evaluate::Substring *substring) { 3808 if (!substring) 3809 return; 3810 bounds.push_back(fir::getBase(asScalar(substring->lower()))); 3811 if (auto upper = substring->upper()) 3812 bounds.push_back(fir::getBase(asScalar(*upper))); 3813 } 3814 3815 /// Default store to destination implementation. 3816 /// This implements the default case, which is to assign the value in 3817 /// `iters.element` into the destination array, `iters.innerArgument`. Handles 3818 /// by value and by reference assignment. 3819 CC defaultStoreToDestination(const Fortran::evaluate::Substring *substring) { 3820 return [=](IterSpace iterSpace) -> ExtValue { 3821 mlir::Location loc = getLoc(); 3822 mlir::Value innerArg = iterSpace.innerArgument(); 3823 fir::ExtendedValue exv = iterSpace.elementExv(); 3824 mlir::Type arrTy = innerArg.getType(); 3825 mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec()); 3826 if (isAdjustedArrayElementType(eleTy)) { 3827 // The elemental update is in the memref domain. Under this semantics, 3828 // we must always copy the computed new element from its location in 3829 // memory into the destination array. 3830 mlir::Type resRefTy = builder.getRefType(eleTy); 3831 // Get a reference to the array element to be amended. 3832 auto arrayOp = builder.create<fir::ArrayAccessOp>( 3833 loc, resRefTy, innerArg, iterSpace.iterVec(), 3834 destination.getTypeparams()); 3835 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 3836 llvm::SmallVector<mlir::Value> substringBounds; 3837 populateBounds(substringBounds, substring); 3838 mlir::Value dstLen = fir::factory::genLenOfCharacter( 3839 builder, loc, destination, iterSpace.iterVec(), substringBounds); 3840 fir::ArrayAmendOp amend = createCharArrayAmend( 3841 loc, builder, arrayOp, dstLen, exv, innerArg, substringBounds); 3842 return abstractArrayExtValue(amend, dstLen); 3843 } 3844 if (fir::isa_derived(eleTy)) { 3845 fir::ArrayAmendOp amend = createDerivedArrayAmend( 3846 loc, destination, builder, arrayOp, exv, eleTy, innerArg); 3847 return abstractArrayExtValue(amend /*FIXME: typeparams?*/); 3848 } 3849 assert(eleTy.isa<fir::SequenceType>() && "must be an array"); 3850 TODO(loc, "array (as element) assignment"); 3851 } 3852 // By value semantics. The element is being assigned by value. 3853 mlir::Value ele = builder.createConvert(loc, eleTy, fir::getBase(exv)); 3854 auto update = builder.create<fir::ArrayUpdateOp>( 3855 loc, arrTy, innerArg, ele, iterSpace.iterVec(), 3856 destination.getTypeparams()); 3857 return abstractArrayExtValue(update); 3858 }; 3859 } 3860 3861 /// For an elemental array expression. 3862 /// 1. Lower the scalars and array loads. 3863 /// 2. Create the iteration space. 3864 /// 3. Create the element-by-element computation in the loop. 3865 /// 4. Return the resulting array value. 3866 /// If no destination was set in the array context, a temporary of 3867 /// \p resultTy will be created to hold the evaluated expression. 3868 /// Otherwise, \p resultTy is ignored and the expression is evaluated 3869 /// in the destination. \p f is a continuation built from an 3870 /// evaluate::Expr or an ExtendedValue. 3871 ExtValue lowerArrayExpression(CC f, mlir::Type resultTy) { 3872 mlir::Location loc = getLoc(); 3873 auto [iterSpace, insPt] = genIterSpace(resultTy); 3874 auto exv = f(iterSpace); 3875 iterSpace.setElement(std::move(exv)); 3876 auto lambda = ccStoreToDest.hasValue() 3877 ? ccStoreToDest.getValue() 3878 : defaultStoreToDestination(/*substring=*/nullptr); 3879 mlir::Value updVal = fir::getBase(lambda(iterSpace)); 3880 finalizeElementCtx(); 3881 builder.create<fir::ResultOp>(loc, updVal); 3882 builder.restoreInsertionPoint(insPt); 3883 return abstractArrayExtValue(iterSpace.outerResult()); 3884 } 3885 3886 /// Get the shape from an ArrayOperand. The shape of the array is adjusted if 3887 /// the array was sliced. 3888 llvm::SmallVector<mlir::Value> getShape(ArrayOperand array) { 3889 // if (array.slice) 3890 // return computeSliceShape(array.slice); 3891 if (array.memref.getType().isa<fir::BoxType>()) 3892 return fir::factory::readExtents(builder, getLoc(), 3893 fir::BoxValue{array.memref}); 3894 std::vector<mlir::Value, std::allocator<mlir::Value>> extents = 3895 fir::factory::getExtents(array.shape); 3896 return {extents.begin(), extents.end()}; 3897 } 3898 3899 /// Get the shape from an ArrayLoad. 3900 llvm::SmallVector<mlir::Value> getShape(fir::ArrayLoadOp arrayLoad) { 3901 return getShape(ArrayOperand{arrayLoad.getMemref(), arrayLoad.getShape(), 3902 arrayLoad.getSlice()}); 3903 } 3904 3905 /// Returns the first array operand that may not be absent. If all 3906 /// array operands may be absent, return the first one. 3907 const ArrayOperand &getInducingShapeArrayOperand() const { 3908 assert(!arrayOperands.empty()); 3909 for (const ArrayOperand &op : arrayOperands) 3910 if (!op.mayBeAbsent) 3911 return op; 3912 // If all arrays operand appears in optional position, then none of them 3913 // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the 3914 // first operands. 3915 // TODO: There is an opportunity to add a runtime check here that 3916 // this array is present as required. 3917 return arrayOperands[0]; 3918 } 3919 3920 /// Generate the shape of the iteration space over the array expression. The 3921 /// iteration space may be implicit, explicit, or both. If it is implied it is 3922 /// based on the destination and operand array loads, or an optional 3923 /// Fortran::evaluate::Shape from the front end. If the shape is explicit, 3924 /// this returns any implicit shape component, if it exists. 3925 llvm::SmallVector<mlir::Value> genIterationShape() { 3926 // Use the precomputed destination shape. 3927 if (!destShape.empty()) 3928 return destShape; 3929 // Otherwise, use the destination's shape. 3930 if (destination) 3931 return getShape(destination); 3932 // Otherwise, use the first ArrayLoad operand shape. 3933 if (!arrayOperands.empty()) 3934 return getShape(getInducingShapeArrayOperand()); 3935 fir::emitFatalError(getLoc(), 3936 "failed to compute the array expression shape"); 3937 } 3938 3939 bool explicitSpaceIsActive() const { 3940 return explicitSpace && explicitSpace->isActive(); 3941 } 3942 3943 bool implicitSpaceHasMasks() const { 3944 return implicitSpace && !implicitSpace->empty(); 3945 } 3946 3947 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter, 3948 Fortran::lower::StatementContext &stmtCtx, 3949 Fortran::lower::SymMap &symMap) 3950 : converter{converter}, builder{converter.getFirOpBuilder()}, 3951 stmtCtx{stmtCtx}, symMap{symMap} {} 3952 3953 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter, 3954 Fortran::lower::StatementContext &stmtCtx, 3955 Fortran::lower::SymMap &symMap, 3956 ConstituentSemantics sem) 3957 : converter{converter}, builder{converter.getFirOpBuilder()}, 3958 stmtCtx{stmtCtx}, symMap{symMap}, semant{sem} {} 3959 3960 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter, 3961 Fortran::lower::StatementContext &stmtCtx, 3962 Fortran::lower::SymMap &symMap, 3963 ConstituentSemantics sem, 3964 Fortran::lower::ExplicitIterSpace *expSpace, 3965 Fortran::lower::ImplicitIterSpace *impSpace) 3966 : converter{converter}, builder{converter.getFirOpBuilder()}, 3967 stmtCtx{stmtCtx}, symMap{symMap}, 3968 explicitSpace(expSpace->isActive() ? expSpace : nullptr), 3969 implicitSpace(impSpace->empty() ? nullptr : impSpace), semant{sem} { 3970 // Generate any mask expressions, as necessary. This is the compute step 3971 // that creates the effective masks. See 10.2.3.2 in particular. 3972 // genMasks(); 3973 } 3974 3975 mlir::Location getLoc() { return converter.getCurrentLocation(); } 3976 3977 /// Array appears in a lhs context such that it is assigned after the rhs is 3978 /// fully evaluated. 3979 inline bool isCopyInCopyOut() { 3980 return semant == ConstituentSemantics::CopyInCopyOut; 3981 } 3982 3983 /// Array appears in a lhs (or temp) context such that a projected, 3984 /// discontiguous subspace of the array is assigned after the rhs is fully 3985 /// evaluated. That is, the rhs array value is merged into a section of the 3986 /// lhs array. 3987 inline bool isProjectedCopyInCopyOut() { 3988 return semant == ConstituentSemantics::ProjectedCopyInCopyOut; 3989 } 3990 3991 inline bool isCustomCopyInCopyOut() { 3992 return semant == ConstituentSemantics::CustomCopyInCopyOut; 3993 } 3994 3995 /// Array appears in a context where it must be boxed. 3996 inline bool isBoxValue() { return semant == ConstituentSemantics::BoxValue; } 3997 3998 /// Array appears in a context where differences in the memory reference can 3999 /// be observable in the computational results. For example, an array 4000 /// element is passed to an impure procedure. 4001 inline bool isReferentiallyOpaque() { 4002 return semant == ConstituentSemantics::RefOpaque; 4003 } 4004 4005 /// Array appears in a context where it is passed as a VALUE argument. 4006 inline bool isValueAttribute() { 4007 return semant == ConstituentSemantics::ByValueArg; 4008 } 4009 4010 /// Can the loops over the expression be unordered? 4011 inline bool isUnordered() const { return unordered; } 4012 4013 void setUnordered(bool b) { unordered = b; } 4014 4015 Fortran::lower::AbstractConverter &converter; 4016 fir::FirOpBuilder &builder; 4017 Fortran::lower::StatementContext &stmtCtx; 4018 bool elementCtx = false; 4019 Fortran::lower::SymMap &symMap; 4020 /// The continuation to generate code to update the destination. 4021 llvm::Optional<CC> ccStoreToDest; 4022 llvm::Optional<std::function<void(llvm::ArrayRef<mlir::Value>)>> ccPrelude; 4023 llvm::Optional<std::function<fir::ArrayLoadOp(llvm::ArrayRef<mlir::Value>)>> 4024 ccLoadDest; 4025 /// The destination is the loaded array into which the results will be 4026 /// merged. 4027 fir::ArrayLoadOp destination; 4028 /// The shape of the destination. 4029 llvm::SmallVector<mlir::Value> destShape; 4030 /// List of arrays in the expression that have been loaded. 4031 llvm::SmallVector<ArrayOperand> arrayOperands; 4032 /// If there is a user-defined iteration space, explicitShape will hold the 4033 /// information from the front end. 4034 Fortran::lower::ExplicitIterSpace *explicitSpace = nullptr; 4035 Fortran::lower::ImplicitIterSpace *implicitSpace = nullptr; 4036 ConstituentSemantics semant = ConstituentSemantics::RefTransparent; 4037 // Can the array expression be evaluated in any order? 4038 // Will be set to false if any of the expression parts prevent this. 4039 bool unordered = true; 4040 }; 4041 } // namespace 4042 4043 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression( 4044 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 4045 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 4046 Fortran::lower::StatementContext &stmtCtx) { 4047 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n'); 4048 return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr); 4049 } 4050 4051 fir::GlobalOp Fortran::lower::createDenseGlobal( 4052 mlir::Location loc, mlir::Type symTy, llvm::StringRef globalName, 4053 mlir::StringAttr linkage, bool isConst, 4054 const Fortran::lower::SomeExpr &expr, 4055 Fortran::lower::AbstractConverter &converter) { 4056 4057 Fortran::lower::StatementContext stmtCtx(/*prohibited=*/true); 4058 Fortran::lower::SymMap emptyMap; 4059 InitializerData initData(/*genRawVals=*/true); 4060 ScalarExprLowering sel(loc, converter, emptyMap, stmtCtx, 4061 /*initializer=*/&initData); 4062 sel.genval(expr); 4063 4064 size_t sz = initData.rawVals.size(); 4065 llvm::ArrayRef<mlir::Attribute> ar = {initData.rawVals.data(), sz}; 4066 4067 mlir::RankedTensorType tensorTy; 4068 auto &builder = converter.getFirOpBuilder(); 4069 mlir::Type iTy = initData.rawType; 4070 if (!iTy) 4071 return 0; // array extent is probably 0 in this case, so just return 0. 4072 tensorTy = mlir::RankedTensorType::get(sz, iTy); 4073 auto init = mlir::DenseElementsAttr::get(tensorTy, ar); 4074 return builder.createGlobal(loc, symTy, globalName, linkage, init, isConst); 4075 } 4076 4077 fir::ExtendedValue Fortran::lower::createSomeInitializerExpression( 4078 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 4079 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 4080 Fortran::lower::StatementContext &stmtCtx) { 4081 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n'); 4082 InitializerData initData; // needed for initializations 4083 return ScalarExprLowering{loc, converter, symMap, stmtCtx, 4084 /*initializer=*/&initData} 4085 .genval(expr); 4086 } 4087 4088 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress( 4089 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 4090 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 4091 Fortran::lower::StatementContext &stmtCtx) { 4092 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n'); 4093 return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr); 4094 } 4095 4096 fir::ExtendedValue Fortran::lower::createInitializerAddress( 4097 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 4098 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 4099 Fortran::lower::StatementContext &stmtCtx) { 4100 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n'); 4101 InitializerData init; 4102 return ScalarExprLowering(loc, converter, symMap, stmtCtx, &init).gen(expr); 4103 } 4104 4105 fir::ExtendedValue 4106 Fortran::lower::createSomeArrayBox(Fortran::lower::AbstractConverter &converter, 4107 const Fortran::lower::SomeExpr &expr, 4108 Fortran::lower::SymMap &symMap, 4109 Fortran::lower::StatementContext &stmtCtx) { 4110 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "box designator: ") << '\n'); 4111 return ArrayExprLowering::lowerBoxedArrayExpression(converter, symMap, 4112 stmtCtx, expr); 4113 } 4114 4115 fir::MutableBoxValue Fortran::lower::createMutableBox( 4116 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 4117 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) { 4118 // MutableBox lowering StatementContext does not need to be propagated 4119 // to the caller because the result value is a variable, not a temporary 4120 // expression. The StatementContext clean-up can occur before using the 4121 // resulting MutableBoxValue. Variables of all other types are handled in the 4122 // bridge. 4123 Fortran::lower::StatementContext dummyStmtCtx; 4124 return ScalarExprLowering{loc, converter, symMap, dummyStmtCtx} 4125 .genMutableBoxValue(expr); 4126 } 4127 4128 mlir::Value Fortran::lower::createSubroutineCall( 4129 AbstractConverter &converter, const evaluate::ProcedureRef &call, 4130 SymMap &symMap, StatementContext &stmtCtx) { 4131 mlir::Location loc = converter.getCurrentLocation(); 4132 4133 // Simple subroutine call, with potential alternate return. 4134 auto res = Fortran::lower::createSomeExtendedExpression( 4135 loc, converter, toEvExpr(call), symMap, stmtCtx); 4136 return fir::getBase(res); 4137 } 4138 4139 void Fortran::lower::createSomeArrayAssignment( 4140 Fortran::lower::AbstractConverter &converter, 4141 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 4142 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 4143 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n'; 4144 rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';); 4145 ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs); 4146 } 4147 4148 void Fortran::lower::createSomeArrayAssignment( 4149 Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs, 4150 const fir::ExtendedValue &rhs, Fortran::lower::SymMap &symMap, 4151 Fortran::lower::StatementContext &stmtCtx) { 4152 LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n'; 4153 llvm::dbgs() << "assign expression: " << rhs << '\n';); 4154 ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs); 4155 } 4156 4157 void Fortran::lower::createAllocatableArrayAssignment( 4158 Fortran::lower::AbstractConverter &converter, 4159 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 4160 Fortran::lower::ExplicitIterSpace &explicitSpace, 4161 Fortran::lower::ImplicitIterSpace &implicitSpace, 4162 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 4163 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining array: ") << '\n'; 4164 rhs.AsFortran(llvm::dbgs() << "assign expression: ") 4165 << " given the explicit iteration space:\n" 4166 << explicitSpace << "\n and implied mask conditions:\n" 4167 << implicitSpace << '\n';); 4168 ArrayExprLowering::lowerAllocatableArrayAssignment( 4169 converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace); 4170 } 4171 4172 fir::ExtendedValue Fortran::lower::createSomeArrayTempValue( 4173 Fortran::lower::AbstractConverter &converter, 4174 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 4175 Fortran::lower::StatementContext &stmtCtx) { 4176 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n'); 4177 return ArrayExprLowering::lowerNewArrayExpression(converter, symMap, stmtCtx, 4178 expr); 4179 } 4180 4181 mlir::Value Fortran::lower::genMaxWithZero(fir::FirOpBuilder &builder, 4182 mlir::Location loc, 4183 mlir::Value value) { 4184 mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0); 4185 if (mlir::Operation *definingOp = value.getDefiningOp()) 4186 if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp)) 4187 if (auto intAttr = cst.getValue().dyn_cast<mlir::IntegerAttr>()) 4188 return intAttr.getInt() < 0 ? zero : value; 4189 return Fortran::lower::genMax(builder, loc, 4190 llvm::SmallVector<mlir::Value>{value, zero}); 4191 } 4192