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