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/DumpEvaluateExpr.h" 22 #include "flang/Lower/IntrinsicCall.h" 23 #include "flang/Lower/StatementContext.h" 24 #include "flang/Lower/SymbolMap.h" 25 #include "flang/Lower/Todo.h" 26 #include "flang/Optimizer/Builder/Complex.h" 27 #include "flang/Optimizer/Builder/Factory.h" 28 #include "flang/Optimizer/Builder/MutableBox.h" 29 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 30 #include "flang/Semantics/expression.h" 31 #include "flang/Semantics/symbol.h" 32 #include "flang/Semantics/tools.h" 33 #include "flang/Semantics/type.h" 34 #include "mlir/Dialect/StandardOps/IR/Ops.h" 35 #include "llvm/Support/Debug.h" 36 37 #define DEBUG_TYPE "flang-lower-expr" 38 39 //===----------------------------------------------------------------------===// 40 // The composition and structure of Fortran::evaluate::Expr is defined in 41 // the various header files in include/flang/Evaluate. You are referred 42 // there for more information on these data structures. Generally speaking, 43 // these data structures are a strongly typed family of abstract data types 44 // that, composed as trees, describe the syntax of Fortran expressions. 45 // 46 // This part of the bridge can traverse these tree structures and lower them 47 // to the correct FIR representation in SSA form. 48 //===----------------------------------------------------------------------===// 49 50 /// The various semantics of a program constituent (or a part thereof) as it may 51 /// appear in an expression. 52 /// 53 /// Given the following Fortran declarations. 54 /// ```fortran 55 /// REAL :: v1, v2, v3 56 /// REAL, POINTER :: vp1 57 /// REAL :: a1(c), a2(c) 58 /// REAL ELEMENTAL FUNCTION f1(arg) ! array -> array 59 /// FUNCTION f2(arg) ! array -> array 60 /// vp1 => v3 ! 1 61 /// v1 = v2 * vp1 ! 2 62 /// a1 = a1 + a2 ! 3 63 /// a1 = f1(a2) ! 4 64 /// a1 = f2(a2) ! 5 65 /// ``` 66 /// 67 /// In line 1, `vp1` is a BoxAddr to copy a box value into. The box value is 68 /// constructed from the DataAddr of `v3`. 69 /// In line 2, `v1` is a DataAddr to copy a value into. The value is constructed 70 /// from the DataValue of `v2` and `vp1`. DataValue is implicitly a double 71 /// dereference in the `vp1` case. 72 /// In line 3, `a1` and `a2` on the rhs are RefTransparent. The `a1` on the lhs 73 /// is CopyInCopyOut as `a1` is replaced elementally by the additions. 74 /// In line 4, `a2` can be RefTransparent, ByValueArg, RefOpaque, or BoxAddr if 75 /// `arg` is declared as C-like pass-by-value, VALUE, INTENT(?), or ALLOCATABLE/ 76 /// POINTER, respectively. `a1` on the lhs is CopyInCopyOut. 77 /// In line 5, `a2` may be DataAddr or BoxAddr assuming f2 is transformational. 78 /// `a1` on the lhs is again CopyInCopyOut. 79 enum class ConstituentSemantics { 80 // Scalar data reference semantics. 81 // 82 // For these let `v` be the location in memory of a variable with value `x` 83 DataValue, // refers to the value `x` 84 DataAddr, // refers to the address `v` 85 BoxValue, // refers to a box value containing `v` 86 BoxAddr, // refers to the address of a box value containing `v` 87 88 // Array data reference semantics. 89 // 90 // For these let `a` be the location in memory of a sequence of value `[xs]`. 91 // Let `x_i` be the `i`-th value in the sequence `[xs]`. 92 93 // Referentially transparent. Refers to the array's value, `[xs]`. 94 RefTransparent, 95 // Refers to an ephemeral address `tmp` containing value `x_i` (15.5.2.3.p7 96 // note 2). (Passing a copy by reference to simulate pass-by-value.) 97 ByValueArg, 98 // Refers to the merge of array value `[xs]` with another array value `[ys]`. 99 // This merged array value will be written into memory location `a`. 100 CopyInCopyOut, 101 // Similar to CopyInCopyOut but `a` may be a transient projection (rather than 102 // a whole array). 103 ProjectedCopyInCopyOut, 104 // Similar to ProjectedCopyInCopyOut, except the merge value is not assigned 105 // automatically by the framework. Instead, and address for `[xs]` is made 106 // accessible so that custom assignments to `[xs]` can be implemented. 107 CustomCopyInCopyOut, 108 // Referentially opaque. Refers to the address of `x_i`. 109 RefOpaque 110 }; 111 112 /// Convert parser's INTEGER relational operators to MLIR. TODO: using 113 /// unordered, but we may want to cons ordered in certain situation. 114 static mlir::arith::CmpIPredicate 115 translateRelational(Fortran::common::RelationalOperator rop) { 116 switch (rop) { 117 case Fortran::common::RelationalOperator::LT: 118 return mlir::arith::CmpIPredicate::slt; 119 case Fortran::common::RelationalOperator::LE: 120 return mlir::arith::CmpIPredicate::sle; 121 case Fortran::common::RelationalOperator::EQ: 122 return mlir::arith::CmpIPredicate::eq; 123 case Fortran::common::RelationalOperator::NE: 124 return mlir::arith::CmpIPredicate::ne; 125 case Fortran::common::RelationalOperator::GT: 126 return mlir::arith::CmpIPredicate::sgt; 127 case Fortran::common::RelationalOperator::GE: 128 return mlir::arith::CmpIPredicate::sge; 129 } 130 llvm_unreachable("unhandled INTEGER relational operator"); 131 } 132 133 /// Convert parser's REAL relational operators to MLIR. 134 /// The choice of order (O prefix) vs unorder (U prefix) follows Fortran 2018 135 /// requirements in the IEEE context (table 17.1 of F2018). This choice is 136 /// also applied in other contexts because it is easier and in line with 137 /// other Fortran compilers. 138 /// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not 139 /// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee 140 /// whether the comparison will signal or not in case of quiet NaN argument. 141 static mlir::arith::CmpFPredicate 142 translateFloatRelational(Fortran::common::RelationalOperator rop) { 143 switch (rop) { 144 case Fortran::common::RelationalOperator::LT: 145 return mlir::arith::CmpFPredicate::OLT; 146 case Fortran::common::RelationalOperator::LE: 147 return mlir::arith::CmpFPredicate::OLE; 148 case Fortran::common::RelationalOperator::EQ: 149 return mlir::arith::CmpFPredicate::OEQ; 150 case Fortran::common::RelationalOperator::NE: 151 return mlir::arith::CmpFPredicate::UNE; 152 case Fortran::common::RelationalOperator::GT: 153 return mlir::arith::CmpFPredicate::OGT; 154 case Fortran::common::RelationalOperator::GE: 155 return mlir::arith::CmpFPredicate::OGE; 156 } 157 llvm_unreachable("unhandled REAL relational operator"); 158 } 159 160 /// Place \p exv in memory if it is not already a memory reference. If 161 /// \p forceValueType is provided, the value is first casted to the provided 162 /// type before being stored (this is mainly intended for logicals whose value 163 /// may be `i1` but needed to be stored as Fortran logicals). 164 static fir::ExtendedValue 165 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc, 166 const fir::ExtendedValue &exv, 167 mlir::Type storageType) { 168 mlir::Value valBase = fir::getBase(exv); 169 if (fir::conformsWithPassByRef(valBase.getType())) 170 return exv; 171 172 assert(!fir::hasDynamicSize(storageType) && 173 "only expect statically sized scalars to be by value"); 174 175 // Since `a` is not itself a valid referent, determine its value and 176 // create a temporary location at the beginning of the function for 177 // referencing. 178 mlir::Value val = builder.createConvert(loc, storageType, valBase); 179 mlir::Value temp = builder.createTemporary( 180 loc, storageType, 181 llvm::ArrayRef<mlir::NamedAttribute>{ 182 Fortran::lower::getAdaptToByRefAttr(builder)}); 183 builder.create<fir::StoreOp>(loc, val, temp); 184 return fir::substBase(exv, temp); 185 } 186 187 /// Is this a variable wrapped in parentheses? 188 template <typename A> 189 static bool isParenthesizedVariable(const A &) { 190 return false; 191 } 192 template <typename T> 193 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) { 194 using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u); 195 using Parentheses = Fortran::evaluate::Parentheses<T>; 196 if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) { 197 if (const auto *parentheses = std::get_if<Parentheses>(&expr.u)) 198 return Fortran::evaluate::IsVariable(parentheses->left()); 199 return false; 200 } else { 201 return std::visit([&](const auto &x) { return isParenthesizedVariable(x); }, 202 expr.u); 203 } 204 } 205 206 /// Generate a load of a value from an address. Beware that this will lose 207 /// any dynamic type information for polymorphic entities (note that unlimited 208 /// polymorphic cannot be loaded and must not be provided here). 209 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder, 210 mlir::Location loc, 211 const fir::ExtendedValue &addr) { 212 return addr.match( 213 [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; }, 214 [&](const fir::UnboxedValue &v) -> fir::ExtendedValue { 215 if (fir::unwrapRefType(fir::getBase(v).getType()) 216 .isa<fir::RecordType>()) 217 return v; 218 return builder.create<fir::LoadOp>(loc, fir::getBase(v)); 219 }, 220 [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue { 221 TODO(loc, "genLoad for MutableBoxValue"); 222 }, 223 [&](const fir::BoxValue &box) -> fir::ExtendedValue { 224 TODO(loc, "genLoad for BoxValue"); 225 }, 226 [&](const auto &) -> fir::ExtendedValue { 227 fir::emitFatalError( 228 loc, "attempting to load whole array or procedure address"); 229 }); 230 } 231 232 /// Is this a call to an elemental procedure with at least one array argument? 233 static bool 234 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) { 235 if (procRef.IsElemental()) 236 for (const std::optional<Fortran::evaluate::ActualArgument> &arg : 237 procRef.arguments()) 238 if (arg && arg->Rank() != 0) 239 return true; 240 return false; 241 } 242 template <typename T> 243 static bool isElementalProcWithArrayArgs(const Fortran::evaluate::Expr<T> &) { 244 return false; 245 } 246 template <> 247 bool isElementalProcWithArrayArgs(const Fortran::lower::SomeExpr &x) { 248 if (const auto *procRef = std::get_if<Fortran::evaluate::ProcedureRef>(&x.u)) 249 return isElementalProcWithArrayArgs(*procRef); 250 return false; 251 } 252 253 /// If \p arg is the address of a function with a denoted host-association tuple 254 /// argument, then return the host-associations tuple value of the current 255 /// procedure. Otherwise, return nullptr. 256 static mlir::Value 257 argumentHostAssocs(Fortran::lower::AbstractConverter &converter, 258 mlir::Value arg) { 259 if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) { 260 auto &builder = converter.getFirOpBuilder(); 261 if (auto funcOp = builder.getNamedFunction(addr.getSymbol())) 262 if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName())) 263 return converter.hostAssocTupleValue(); 264 } 265 return {}; 266 } 267 268 namespace { 269 270 /// Lowering of Fortran::evaluate::Expr<T> expressions 271 class ScalarExprLowering { 272 public: 273 using ExtValue = fir::ExtendedValue; 274 275 explicit ScalarExprLowering(mlir::Location loc, 276 Fortran::lower::AbstractConverter &converter, 277 Fortran::lower::SymMap &symMap, 278 Fortran::lower::StatementContext &stmtCtx) 279 : location{loc}, converter{converter}, 280 builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap} { 281 } 282 283 ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) { 284 return gen(expr); 285 } 286 287 /// Lower `expr` to be passed as a fir.box argument. Do not create a temp 288 /// for the expr if it is a variable that can be described as a fir.box. 289 ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) { 290 bool saveUseBoxArg = useBoxArg; 291 useBoxArg = true; 292 ExtValue result = gen(expr); 293 useBoxArg = saveUseBoxArg; 294 return result; 295 } 296 297 ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) { 298 return genval(expr); 299 } 300 301 /// Lower an expression that is a pointer or an allocatable to a 302 /// MutableBoxValue. 303 fir::MutableBoxValue 304 genMutableBoxValue(const Fortran::lower::SomeExpr &expr) { 305 // Pointers and allocatables can only be: 306 // - a simple designator "x" 307 // - a component designator "a%b(i,j)%x" 308 // - a function reference "foo()" 309 // - result of NULL() or NULL(MOLD) intrinsic. 310 // NULL() requires some context to be lowered, so it is not handled 311 // here and must be lowered according to the context where it appears. 312 ExtValue exv = std::visit( 313 [&](const auto &x) { return genMutableBoxValueImpl(x); }, expr.u); 314 const fir::MutableBoxValue *mutableBox = 315 exv.getBoxOf<fir::MutableBoxValue>(); 316 if (!mutableBox) 317 fir::emitFatalError(getLoc(), "expr was not lowered to MutableBoxValue"); 318 return *mutableBox; 319 } 320 321 template <typename T> 322 ExtValue genMutableBoxValueImpl(const T &) { 323 // NULL() case should not be handled here. 324 fir::emitFatalError(getLoc(), "NULL() must be lowered in its context"); 325 } 326 327 template <typename T> 328 ExtValue 329 genMutableBoxValueImpl(const Fortran::evaluate::FunctionRef<T> &funRef) { 330 return genRawProcedureRef(funRef, converter.genType(toEvExpr(funRef))); 331 } 332 333 template <typename T> 334 ExtValue 335 genMutableBoxValueImpl(const Fortran::evaluate::Designator<T> &designator) { 336 return std::visit( 337 Fortran::common::visitors{ 338 [&](const Fortran::evaluate::SymbolRef &sym) -> ExtValue { 339 return symMap.lookupSymbol(*sym).toExtendedValue(); 340 }, 341 [&](const Fortran::evaluate::Component &comp) -> ExtValue { 342 return genComponent(comp); 343 }, 344 [&](const auto &) -> ExtValue { 345 fir::emitFatalError(getLoc(), 346 "not an allocatable or pointer designator"); 347 }}, 348 designator.u); 349 } 350 351 template <typename T> 352 ExtValue genMutableBoxValueImpl(const Fortran::evaluate::Expr<T> &expr) { 353 return std::visit([&](const auto &x) { return genMutableBoxValueImpl(x); }, 354 expr.u); 355 } 356 357 mlir::Location getLoc() { return location; } 358 359 template <typename A> 360 mlir::Value genunbox(const A &expr) { 361 ExtValue e = genval(expr); 362 if (const fir::UnboxedValue *r = e.getUnboxed()) 363 return *r; 364 fir::emitFatalError(getLoc(), "unboxed expression expected"); 365 } 366 367 /// Generate an integral constant of `value` 368 template <int KIND> 369 mlir::Value genIntegerConstant(mlir::MLIRContext *context, 370 std::int64_t value) { 371 mlir::Type type = 372 converter.genType(Fortran::common::TypeCategory::Integer, KIND); 373 return builder.createIntegerConstant(getLoc(), type, value); 374 } 375 376 /// Generate a logical/boolean constant of `value` 377 mlir::Value genBoolConstant(bool value) { 378 return builder.createBool(getLoc(), value); 379 } 380 381 /// Generate a real constant with a value `value`. 382 template <int KIND> 383 mlir::Value genRealConstant(mlir::MLIRContext *context, 384 const llvm::APFloat &value) { 385 mlir::Type fltTy = Fortran::lower::convertReal(context, KIND); 386 return builder.createRealConstant(getLoc(), fltTy, value); 387 } 388 389 template <typename OpTy> 390 mlir::Value createCompareOp(mlir::arith::CmpIPredicate pred, 391 const ExtValue &left, const ExtValue &right) { 392 if (const fir::UnboxedValue *lhs = left.getUnboxed()) 393 if (const fir::UnboxedValue *rhs = right.getUnboxed()) 394 return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs); 395 fir::emitFatalError(getLoc(), "array compare should be handled in genarr"); 396 } 397 template <typename OpTy, typename A> 398 mlir::Value createCompareOp(const A &ex, mlir::arith::CmpIPredicate pred) { 399 ExtValue left = genval(ex.left()); 400 return createCompareOp<OpTy>(pred, left, genval(ex.right())); 401 } 402 403 template <typename OpTy> 404 mlir::Value createFltCmpOp(mlir::arith::CmpFPredicate pred, 405 const ExtValue &left, const ExtValue &right) { 406 if (const fir::UnboxedValue *lhs = left.getUnboxed()) 407 if (const fir::UnboxedValue *rhs = right.getUnboxed()) 408 return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs); 409 fir::emitFatalError(getLoc(), "array compare should be handled in genarr"); 410 } 411 template <typename OpTy, typename A> 412 mlir::Value createFltCmpOp(const A &ex, mlir::arith::CmpFPredicate pred) { 413 ExtValue left = genval(ex.left()); 414 return createFltCmpOp<OpTy>(pred, left, genval(ex.right())); 415 } 416 417 /// Returns a reference to a symbol or its box/boxChar descriptor if it has 418 /// one. 419 ExtValue gen(Fortran::semantics::SymbolRef sym) { 420 if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym)) 421 return val.match([&val](auto &) { return val.toExtendedValue(); }); 422 LLVM_DEBUG(llvm::dbgs() 423 << "unknown symbol: " << sym << "\nmap: " << symMap << '\n'); 424 fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value"); 425 } 426 427 ExtValue genLoad(const ExtValue &exv) { 428 return ::genLoad(builder, getLoc(), exv); 429 } 430 431 ExtValue genval(Fortran::semantics::SymbolRef sym) { 432 ExtValue var = gen(sym); 433 if (const fir::UnboxedValue *s = var.getUnboxed()) 434 if (fir::isReferenceLike(s->getType())) 435 return genLoad(*s); 436 return var; 437 } 438 439 ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) { 440 TODO(getLoc(), "genval BOZ"); 441 } 442 443 /// Return indirection to function designated in ProcedureDesignator. 444 /// The type of the function indirection is not guaranteed to match the one 445 /// of the ProcedureDesignator due to Fortran implicit typing rules. 446 ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) { 447 TODO(getLoc(), "genval ProcedureDesignator"); 448 } 449 450 ExtValue genval(const Fortran::evaluate::NullPointer &) { 451 TODO(getLoc(), "genval NullPointer"); 452 } 453 454 ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) { 455 TODO(getLoc(), "genval StructureConstructor"); 456 } 457 458 /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol. 459 ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) { 460 TODO(getLoc(), "genval ImpliedDoIndex"); 461 } 462 463 ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) { 464 TODO(getLoc(), "genval DescriptorInquiry"); 465 } 466 467 ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) { 468 TODO(getLoc(), "genval TypeParamInquiry"); 469 } 470 471 template <int KIND> 472 ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) { 473 TODO(getLoc(), "genval ComplexComponent"); 474 } 475 476 template <int KIND> 477 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 478 Fortran::common::TypeCategory::Integer, KIND>> &op) { 479 mlir::Value input = genunbox(op.left()); 480 // Like LLVM, integer negation is the binary op "0 - value" 481 mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0); 482 return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input); 483 } 484 485 template <int KIND> 486 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 487 Fortran::common::TypeCategory::Real, KIND>> &op) { 488 return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left())); 489 } 490 template <int KIND> 491 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 492 Fortran::common::TypeCategory::Complex, KIND>> &op) { 493 return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left())); 494 } 495 496 template <typename OpTy> 497 mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) { 498 assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right)); 499 mlir::Value lhs = fir::getBase(left); 500 mlir::Value rhs = fir::getBase(right); 501 assert(lhs.getType() == rhs.getType() && "types must be the same"); 502 return builder.create<OpTy>(getLoc(), lhs, rhs); 503 } 504 505 template <typename OpTy, typename A> 506 mlir::Value createBinaryOp(const A &ex) { 507 ExtValue left = genval(ex.left()); 508 return createBinaryOp<OpTy>(left, genval(ex.right())); 509 } 510 511 #undef GENBIN 512 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \ 513 template <int KIND> \ 514 ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \ 515 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \ 516 return createBinaryOp<GenBinFirOp>(x); \ 517 } 518 519 GENBIN(Add, Integer, mlir::arith::AddIOp) 520 GENBIN(Add, Real, mlir::arith::AddFOp) 521 GENBIN(Add, Complex, fir::AddcOp) 522 GENBIN(Subtract, Integer, mlir::arith::SubIOp) 523 GENBIN(Subtract, Real, mlir::arith::SubFOp) 524 GENBIN(Subtract, Complex, fir::SubcOp) 525 GENBIN(Multiply, Integer, mlir::arith::MulIOp) 526 GENBIN(Multiply, Real, mlir::arith::MulFOp) 527 GENBIN(Multiply, Complex, fir::MulcOp) 528 GENBIN(Divide, Integer, mlir::arith::DivSIOp) 529 GENBIN(Divide, Real, mlir::arith::DivFOp) 530 GENBIN(Divide, Complex, fir::DivcOp) 531 532 template <Fortran::common::TypeCategory TC, int KIND> 533 ExtValue genval( 534 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) { 535 TODO(getLoc(), "genval Power"); 536 } 537 538 template <Fortran::common::TypeCategory TC, int KIND> 539 ExtValue genval( 540 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>> 541 &op) { 542 TODO(getLoc(), "genval RealToInt"); 543 } 544 545 template <int KIND> 546 ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) { 547 mlir::Value realPartValue = genunbox(op.left()); 548 return fir::factory::Complex{builder, getLoc()}.createComplex( 549 KIND, realPartValue, genunbox(op.right())); 550 } 551 552 template <int KIND> 553 ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) { 554 TODO(getLoc(), "genval Concat<KIND>"); 555 } 556 557 /// MIN and MAX operations 558 template <Fortran::common::TypeCategory TC, int KIND> 559 ExtValue 560 genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> 561 &op) { 562 TODO(getLoc(), "genval Extremum<TC, KIND>"); 563 } 564 565 template <int KIND> 566 ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) { 567 TODO(getLoc(), "genval SetLength<KIND>"); 568 } 569 570 template <int KIND> 571 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 572 Fortran::common::TypeCategory::Integer, KIND>> &op) { 573 return createCompareOp<mlir::arith::CmpIOp>(op, 574 translateRelational(op.opr)); 575 } 576 template <int KIND> 577 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 578 Fortran::common::TypeCategory::Real, KIND>> &op) { 579 return createFltCmpOp<mlir::arith::CmpFOp>( 580 op, translateFloatRelational(op.opr)); 581 } 582 template <int KIND> 583 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 584 Fortran::common::TypeCategory::Complex, KIND>> &op) { 585 TODO(getLoc(), "genval complex comparison"); 586 } 587 template <int KIND> 588 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 589 Fortran::common::TypeCategory::Character, KIND>> &op) { 590 TODO(getLoc(), "genval char comparison"); 591 } 592 593 ExtValue 594 genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) { 595 return std::visit([&](const auto &x) { return genval(x); }, op.u); 596 } 597 598 template <Fortran::common::TypeCategory TC1, int KIND, 599 Fortran::common::TypeCategory TC2> 600 ExtValue 601 genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, 602 TC2> &convert) { 603 mlir::Type ty = converter.genType(TC1, KIND); 604 mlir::Value operand = genunbox(convert.left()); 605 return builder.convertWithSemantics(getLoc(), ty, operand); 606 } 607 608 template <typename A> 609 ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) { 610 TODO(getLoc(), "genval parentheses<A>"); 611 } 612 613 template <int KIND> 614 ExtValue genval(const Fortran::evaluate::Not<KIND> &op) { 615 mlir::Value logical = genunbox(op.left()); 616 mlir::Value one = genBoolConstant(true); 617 mlir::Value val = 618 builder.createConvert(getLoc(), builder.getI1Type(), logical); 619 return builder.create<mlir::arith::XOrIOp>(getLoc(), val, one); 620 } 621 622 template <int KIND> 623 ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) { 624 mlir::IntegerType i1Type = builder.getI1Type(); 625 mlir::Value slhs = genunbox(op.left()); 626 mlir::Value srhs = genunbox(op.right()); 627 mlir::Value lhs = builder.createConvert(getLoc(), i1Type, slhs); 628 mlir::Value rhs = builder.createConvert(getLoc(), i1Type, srhs); 629 switch (op.logicalOperator) { 630 case Fortran::evaluate::LogicalOperator::And: 631 return createBinaryOp<mlir::arith::AndIOp>(lhs, rhs); 632 case Fortran::evaluate::LogicalOperator::Or: 633 return createBinaryOp<mlir::arith::OrIOp>(lhs, rhs); 634 case Fortran::evaluate::LogicalOperator::Eqv: 635 return createCompareOp<mlir::arith::CmpIOp>( 636 mlir::arith::CmpIPredicate::eq, lhs, rhs); 637 case Fortran::evaluate::LogicalOperator::Neqv: 638 return createCompareOp<mlir::arith::CmpIOp>( 639 mlir::arith::CmpIPredicate::ne, lhs, rhs); 640 case Fortran::evaluate::LogicalOperator::Not: 641 // lib/evaluate expression for .NOT. is Fortran::evaluate::Not<KIND>. 642 llvm_unreachable(".NOT. is not a binary operator"); 643 } 644 llvm_unreachable("unhandled logical operation"); 645 } 646 647 /// Convert a scalar literal constant to IR. 648 template <Fortran::common::TypeCategory TC, int KIND> 649 ExtValue genScalarLit( 650 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> 651 &value) { 652 if constexpr (TC == Fortran::common::TypeCategory::Integer) { 653 return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64()); 654 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) { 655 return genBoolConstant(value.IsTrue()); 656 } else if constexpr (TC == Fortran::common::TypeCategory::Real) { 657 std::string str = value.DumpHexadecimal(); 658 if constexpr (KIND == 2) { 659 llvm::APFloat floatVal{llvm::APFloatBase::IEEEhalf(), str}; 660 return genRealConstant<KIND>(builder.getContext(), floatVal); 661 } else if constexpr (KIND == 3) { 662 llvm::APFloat floatVal{llvm::APFloatBase::BFloat(), str}; 663 return genRealConstant<KIND>(builder.getContext(), floatVal); 664 } else if constexpr (KIND == 4) { 665 llvm::APFloat floatVal{llvm::APFloatBase::IEEEsingle(), str}; 666 return genRealConstant<KIND>(builder.getContext(), floatVal); 667 } else if constexpr (KIND == 10) { 668 llvm::APFloat floatVal{llvm::APFloatBase::x87DoubleExtended(), str}; 669 return genRealConstant<KIND>(builder.getContext(), floatVal); 670 } else if constexpr (KIND == 16) { 671 llvm::APFloat floatVal{llvm::APFloatBase::IEEEquad(), str}; 672 return genRealConstant<KIND>(builder.getContext(), floatVal); 673 } else { 674 // convert everything else to double 675 llvm::APFloat floatVal{llvm::APFloatBase::IEEEdouble(), str}; 676 return genRealConstant<KIND>(builder.getContext(), floatVal); 677 } 678 } else if constexpr (TC == Fortran::common::TypeCategory::Complex) { 679 using TR = 680 Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>; 681 Fortran::evaluate::ComplexConstructor<KIND> ctor( 682 Fortran::evaluate::Expr<TR>{ 683 Fortran::evaluate::Constant<TR>{value.REAL()}}, 684 Fortran::evaluate::Expr<TR>{ 685 Fortran::evaluate::Constant<TR>{value.AIMAG()}}); 686 return genunbox(ctor); 687 } else /*constexpr*/ { 688 llvm_unreachable("unhandled constant"); 689 } 690 } 691 692 /// Convert a ascii scalar literal CHARACTER to IR. (specialization) 693 ExtValue 694 genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type< 695 Fortran::common::TypeCategory::Character, 1>> &value, 696 int64_t len) { 697 assert(value.size() == static_cast<std::uint64_t>(len) && 698 "value.size() doesn't match with len"); 699 return fir::factory::createStringLiteral(builder, getLoc(), value); 700 } 701 702 template <Fortran::common::TypeCategory TC, int KIND> 703 ExtValue 704 genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>> 705 &con) { 706 if (con.Rank() > 0) 707 TODO(getLoc(), "genval array constant"); 708 std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>> 709 opt = con.GetScalarValue(); 710 assert(opt.has_value() && "constant has no value"); 711 if constexpr (TC == Fortran::common::TypeCategory::Character) { 712 if constexpr (KIND == 1) 713 return genAsciiScalarLit(opt.value(), con.LEN()); 714 TODO(getLoc(), "genval for Character with KIND != 1"); 715 } else { 716 return genScalarLit<TC, KIND>(opt.value()); 717 } 718 } 719 720 fir::ExtendedValue genval( 721 const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) { 722 TODO(getLoc(), "genval constant derived"); 723 } 724 725 template <typename A> 726 ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) { 727 TODO(getLoc(), "genval ArrayConstructor<A>"); 728 } 729 730 ExtValue gen(const Fortran::evaluate::ComplexPart &x) { 731 TODO(getLoc(), "gen ComplexPart"); 732 } 733 ExtValue genval(const Fortran::evaluate::ComplexPart &x) { 734 TODO(getLoc(), "genval ComplexPart"); 735 } 736 737 ExtValue gen(const Fortran::evaluate::Substring &s) { 738 TODO(getLoc(), "gen Substring"); 739 } 740 ExtValue genval(const Fortran::evaluate::Substring &ss) { 741 TODO(getLoc(), "genval Substring"); 742 } 743 744 ExtValue genval(const Fortran::evaluate::Subscript &subs) { 745 if (auto *s = std::get_if<Fortran::evaluate::IndirectSubscriptIntegerExpr>( 746 &subs.u)) { 747 if (s->value().Rank() > 0) 748 fir::emitFatalError(getLoc(), "vector subscript is not scalar"); 749 return {genval(s->value())}; 750 } 751 fir::emitFatalError(getLoc(), "subscript triple notation is not scalar"); 752 } 753 754 ExtValue genSubscript(const Fortran::evaluate::Subscript &subs) { 755 return genval(subs); 756 } 757 758 ExtValue gen(const Fortran::evaluate::DataRef &dref) { 759 TODO(getLoc(), "gen DataRef"); 760 } 761 ExtValue genval(const Fortran::evaluate::DataRef &dref) { 762 TODO(getLoc(), "genval DataRef"); 763 } 764 765 // Helper function to turn the Component structure into a list of nested 766 // components, ordered from largest/leftmost to smallest/rightmost: 767 // - where only the smallest/rightmost item may be allocatable or a pointer 768 // (nested allocatable/pointer components require nested coordinate_of ops) 769 // - that does not contain any parent components 770 // (the front end places parent components directly in the object) 771 // Return the object used as the base coordinate for the component chain. 772 static Fortran::evaluate::DataRef const * 773 reverseComponents(const Fortran::evaluate::Component &cmpt, 774 std::list<const Fortran::evaluate::Component *> &list) { 775 if (!cmpt.GetLastSymbol().test( 776 Fortran::semantics::Symbol::Flag::ParentComp)) 777 list.push_front(&cmpt); 778 return std::visit( 779 Fortran::common::visitors{ 780 [&](const Fortran::evaluate::Component &x) { 781 if (Fortran::semantics::IsAllocatableOrPointer(x.GetLastSymbol())) 782 return &cmpt.base(); 783 return reverseComponents(x, list); 784 }, 785 [&](auto &) { return &cmpt.base(); }, 786 }, 787 cmpt.base().u); 788 } 789 790 // Return the coordinate of the component reference 791 ExtValue genComponent(const Fortran::evaluate::Component &cmpt) { 792 std::list<const Fortran::evaluate::Component *> list; 793 const Fortran::evaluate::DataRef *base = reverseComponents(cmpt, list); 794 llvm::SmallVector<mlir::Value> coorArgs; 795 ExtValue obj = gen(*base); 796 mlir::Type ty = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(obj).getType()); 797 mlir::Location loc = getLoc(); 798 auto fldTy = fir::FieldType::get(&converter.getMLIRContext()); 799 // FIXME: need to thread the LEN type parameters here. 800 for (const Fortran::evaluate::Component *field : list) { 801 auto recTy = ty.cast<fir::RecordType>(); 802 const Fortran::semantics::Symbol &sym = field->GetLastSymbol(); 803 llvm::StringRef name = toStringRef(sym.name()); 804 coorArgs.push_back(builder.create<fir::FieldIndexOp>( 805 loc, fldTy, name, recTy, fir::getTypeParams(obj))); 806 ty = recTy.getType(name); 807 } 808 ty = builder.getRefType(ty); 809 return fir::factory::componentToExtendedValue( 810 builder, loc, 811 builder.create<fir::CoordinateOp>(loc, ty, fir::getBase(obj), 812 coorArgs)); 813 } 814 815 ExtValue gen(const Fortran::evaluate::Component &cmpt) { 816 TODO(getLoc(), "gen Component"); 817 } 818 ExtValue genval(const Fortran::evaluate::Component &cmpt) { 819 TODO(getLoc(), "genval Component"); 820 } 821 822 ExtValue genval(const Fortran::semantics::Bound &bound) { 823 TODO(getLoc(), "genval Bound"); 824 } 825 826 /// Return lower bounds of \p box in dimension \p dim. The returned value 827 /// has type \ty. 828 mlir::Value getLBound(const ExtValue &box, unsigned dim, mlir::Type ty) { 829 assert(box.rank() > 0 && "must be an array"); 830 mlir::Location loc = getLoc(); 831 mlir::Value one = builder.createIntegerConstant(loc, ty, 1); 832 mlir::Value lb = fir::factory::readLowerBound(builder, loc, box, dim, one); 833 return builder.createConvert(loc, ty, lb); 834 } 835 836 /// Lower an ArrayRef to a fir.coordinate_of given its lowered base. 837 ExtValue genCoordinateOp(const ExtValue &array, 838 const Fortran::evaluate::ArrayRef &aref) { 839 mlir::Location loc = getLoc(); 840 // References to array of rank > 1 with non constant shape that are not 841 // fir.box must be collapsed into an offset computation in lowering already. 842 // The same is needed with dynamic length character arrays of all ranks. 843 mlir::Type baseType = 844 fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(array).getType()); 845 if ((array.rank() > 1 && fir::hasDynamicSize(baseType)) || 846 fir::characterWithDynamicLen(fir::unwrapSequenceType(baseType))) 847 if (!array.getBoxOf<fir::BoxValue>()) 848 TODO(getLoc(), "genOffsetAndCoordinateOp"); 849 // Generate a fir.coordinate_of with zero based array indexes. 850 llvm::SmallVector<mlir::Value> args; 851 for (const auto &subsc : llvm::enumerate(aref.subscript())) { 852 ExtValue subVal = genSubscript(subsc.value()); 853 assert(fir::isUnboxedValue(subVal) && "subscript must be simple scalar"); 854 mlir::Value val = fir::getBase(subVal); 855 mlir::Type ty = val.getType(); 856 mlir::Value lb = getLBound(array, subsc.index(), ty); 857 args.push_back(builder.create<mlir::arith::SubIOp>(loc, ty, val, lb)); 858 } 859 860 mlir::Value base = fir::getBase(array); 861 auto seqTy = 862 fir::dyn_cast_ptrOrBoxEleTy(base.getType()).cast<fir::SequenceType>(); 863 assert(args.size() == seqTy.getDimension()); 864 mlir::Type ty = builder.getRefType(seqTy.getEleTy()); 865 auto addr = builder.create<fir::CoordinateOp>(loc, ty, base, args); 866 return fir::factory::arrayElementToExtendedValue(builder, loc, array, addr); 867 } 868 869 ExtValue gen(const Fortran::evaluate::ArrayRef &aref) { 870 ExtValue base = aref.base().IsSymbol() ? gen(aref.base().GetFirstSymbol()) 871 : gen(aref.base().GetComponent()); 872 return genCoordinateOp(base, aref); 873 } 874 ExtValue genval(const Fortran::evaluate::ArrayRef &aref) { 875 TODO(getLoc(), "genval ArrayRef"); 876 } 877 878 ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) { 879 TODO(getLoc(), "gen CoarrayRef"); 880 } 881 ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) { 882 TODO(getLoc(), "genval CoarrayRef"); 883 } 884 885 template <typename A> 886 ExtValue gen(const Fortran::evaluate::Designator<A> &des) { 887 return std::visit([&](const auto &x) { return gen(x); }, des.u); 888 } 889 template <typename A> 890 ExtValue genval(const Fortran::evaluate::Designator<A> &des) { 891 return std::visit([&](const auto &x) { return genval(x); }, des.u); 892 } 893 894 mlir::Type genType(const Fortran::evaluate::DynamicType &dt) { 895 if (dt.category() != Fortran::common::TypeCategory::Derived) 896 return converter.genType(dt.category(), dt.kind()); 897 TODO(getLoc(), "genType Derived Type"); 898 } 899 900 /// Lower a function reference 901 template <typename A> 902 ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) { 903 if (!funcRef.GetType().has_value()) 904 fir::emitFatalError(getLoc(), "internal: a function must have a type"); 905 mlir::Type resTy = genType(*funcRef.GetType()); 906 return genProcedureRef(funcRef, {resTy}); 907 } 908 909 /// Lower function call `funcRef` and return a reference to the resultant 910 /// value. This is required for lowering expressions such as `f1(f2(v))`. 911 template <typename A> 912 ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) { 913 TODO(getLoc(), "gen FunctionRef<A>"); 914 } 915 916 /// helper to detect statement functions 917 static bool 918 isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) { 919 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol()) 920 if (const auto *details = 921 symbol->detailsIf<Fortran::semantics::SubprogramDetails>()) 922 return details->stmtFunction().has_value(); 923 return false; 924 } 925 926 /// Helper to package a Value and its properties into an ExtendedValue. 927 static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base, 928 llvm::ArrayRef<mlir::Value> extents, 929 llvm::ArrayRef<mlir::Value> lengths) { 930 mlir::Type type = base.getType(); 931 if (type.isa<fir::BoxType>()) 932 return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents); 933 type = fir::unwrapRefType(type); 934 if (type.isa<fir::BoxType>()) 935 return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {}); 936 if (auto seqTy = type.dyn_cast<fir::SequenceType>()) { 937 if (seqTy.getDimension() != extents.size()) 938 fir::emitFatalError(loc, "incorrect number of extents for array"); 939 if (seqTy.getEleTy().isa<fir::CharacterType>()) { 940 if (lengths.empty()) 941 fir::emitFatalError(loc, "missing length for character"); 942 assert(lengths.size() == 1); 943 return fir::CharArrayBoxValue(base, lengths[0], extents); 944 } 945 return fir::ArrayBoxValue(base, extents); 946 } 947 if (type.isa<fir::CharacterType>()) { 948 if (lengths.empty()) 949 fir::emitFatalError(loc, "missing length for character"); 950 assert(lengths.size() == 1); 951 return fir::CharBoxValue(base, lengths[0]); 952 } 953 return base; 954 } 955 956 // Find the argument that corresponds to the host associations. 957 // Verify some assumptions about how the signature was built here. 958 [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::FuncOp fn) { 959 // Scan the argument list from last to first as the host associations are 960 // appended for now. 961 for (unsigned i = fn.getNumArguments(); i > 0; --i) 962 if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) { 963 // Host assoc tuple must be last argument (for now). 964 assert(i == fn.getNumArguments() && "tuple must be last"); 965 return i - 1; 966 } 967 llvm_unreachable("anyFuncArgsHaveAttr failed"); 968 } 969 970 /// Lower a non-elemental procedure reference and read allocatable and pointer 971 /// results into normal values. 972 ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef, 973 llvm::Optional<mlir::Type> resultType) { 974 ExtValue res = genRawProcedureRef(procRef, resultType); 975 return res; 976 } 977 978 /// Given a call site for which the arguments were already lowered, generate 979 /// the call and return the result. This function deals with explicit result 980 /// allocation and lowering if needed. It also deals with passing the host 981 /// link to internal procedures. 982 ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller, 983 mlir::FunctionType callSiteType, 984 llvm::Optional<mlir::Type> resultType) { 985 mlir::Location loc = getLoc(); 986 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 987 // Handle cases where caller must allocate the result or a fir.box for it. 988 bool mustPopSymMap = false; 989 if (caller.mustMapInterfaceSymbols()) { 990 symMap.pushScope(); 991 mustPopSymMap = true; 992 Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap); 993 } 994 // If this is an indirect call, retrieve the function address. Also retrieve 995 // the result length if this is a character function (note that this length 996 // will be used only if there is no explicit length in the local interface). 997 mlir::Value funcPointer; 998 mlir::Value charFuncPointerLength; 999 if (caller.getIfIndirectCallSymbol()) { 1000 TODO(loc, "genCallOpAndResult indirect call"); 1001 } 1002 1003 mlir::IndexType idxTy = builder.getIndexType(); 1004 auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value { 1005 return builder.createConvert( 1006 loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx))); 1007 }; 1008 llvm::SmallVector<mlir::Value> resultLengths; 1009 auto allocatedResult = [&]() -> llvm::Optional<ExtValue> { 1010 llvm::SmallVector<mlir::Value> extents; 1011 llvm::SmallVector<mlir::Value> lengths; 1012 if (!caller.callerAllocateResult()) 1013 return {}; 1014 mlir::Type type = caller.getResultStorageType(); 1015 if (type.isa<fir::SequenceType>()) 1016 caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) { 1017 extents.emplace_back(lowerSpecExpr(e)); 1018 }); 1019 caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) { 1020 lengths.emplace_back(lowerSpecExpr(e)); 1021 }); 1022 1023 // Result length parameters should not be provided to box storage 1024 // allocation and save_results, but they are still useful information to 1025 // keep in the ExtendedValue if non-deferred. 1026 if (!type.isa<fir::BoxType>()) { 1027 if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) { 1028 // Calling an assumed length function. This is only possible if this 1029 // is a call to a character dummy procedure. 1030 if (!charFuncPointerLength) 1031 fir::emitFatalError(loc, "failed to retrieve character function " 1032 "length while calling it"); 1033 lengths.push_back(charFuncPointerLength); 1034 } 1035 resultLengths = lengths; 1036 } 1037 1038 if (!extents.empty() || !lengths.empty()) { 1039 TODO(loc, "genCallOpResult extents and length"); 1040 } 1041 mlir::Value temp = 1042 builder.createTemporary(loc, type, ".result", extents, resultLengths); 1043 return toExtendedValue(loc, temp, extents, lengths); 1044 }(); 1045 1046 if (mustPopSymMap) 1047 symMap.popScope(); 1048 1049 // Place allocated result or prepare the fir.save_result arguments. 1050 mlir::Value arrayResultShape; 1051 if (allocatedResult) { 1052 if (std::optional<Fortran::lower::CallInterface< 1053 Fortran::lower::CallerInterface>::PassedEntity> 1054 resultArg = caller.getPassedResult()) { 1055 if (resultArg->passBy == PassBy::AddressAndLength) 1056 caller.placeAddressAndLengthInput(*resultArg, 1057 fir::getBase(*allocatedResult), 1058 fir::getLen(*allocatedResult)); 1059 else if (resultArg->passBy == PassBy::BaseAddress) 1060 caller.placeInput(*resultArg, fir::getBase(*allocatedResult)); 1061 else 1062 fir::emitFatalError( 1063 loc, "only expect character scalar result to be passed by ref"); 1064 } else { 1065 assert(caller.mustSaveResult()); 1066 arrayResultShape = allocatedResult->match( 1067 [&](const fir::CharArrayBoxValue &) { 1068 return builder.createShape(loc, *allocatedResult); 1069 }, 1070 [&](const fir::ArrayBoxValue &) { 1071 return builder.createShape(loc, *allocatedResult); 1072 }, 1073 [&](const auto &) { return mlir::Value{}; }); 1074 } 1075 } 1076 1077 // In older Fortran, procedure argument types are inferred. This may lead 1078 // different view of what the function signature is in different locations. 1079 // Casts are inserted as needed below to accommodate this. 1080 1081 // The mlir::FuncOp type prevails, unless it has a different number of 1082 // arguments which can happen in legal program if it was passed as a dummy 1083 // procedure argument earlier with no further type information. 1084 mlir::SymbolRefAttr funcSymbolAttr; 1085 bool addHostAssociations = false; 1086 if (!funcPointer) { 1087 mlir::FunctionType funcOpType = caller.getFuncOp().getType(); 1088 mlir::SymbolRefAttr symbolAttr = 1089 builder.getSymbolRefAttr(caller.getMangledName()); 1090 if (callSiteType.getNumResults() == funcOpType.getNumResults() && 1091 callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() && 1092 fir::anyFuncArgsHaveAttr(caller.getFuncOp(), 1093 fir::getHostAssocAttrName())) { 1094 // The number of arguments is off by one, and we're lowering a function 1095 // with host associations. Modify call to include host associations 1096 // argument by appending the value at the end of the operands. 1097 assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) == 1098 converter.hostAssocTupleValue().getType()); 1099 addHostAssociations = true; 1100 } 1101 if (!addHostAssociations && 1102 (callSiteType.getNumResults() != funcOpType.getNumResults() || 1103 callSiteType.getNumInputs() != funcOpType.getNumInputs())) { 1104 // Deal with argument number mismatch by making a function pointer so 1105 // that function type cast can be inserted. Do not emit a warning here 1106 // because this can happen in legal program if the function is not 1107 // defined here and it was first passed as an argument without any more 1108 // information. 1109 funcPointer = 1110 builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr); 1111 } else if (callSiteType.getResults() != funcOpType.getResults()) { 1112 // Implicit interface result type mismatch are not standard Fortran, but 1113 // some compilers are not complaining about it. The front end is not 1114 // protecting lowering from this currently. Support this with a 1115 // discouraging warning. 1116 LLVM_DEBUG(mlir::emitWarning( 1117 loc, "a return type mismatch is not standard compliant and may " 1118 "lead to undefined behavior.")); 1119 // Cast the actual function to the current caller implicit type because 1120 // that is the behavior we would get if we could not see the definition. 1121 funcPointer = 1122 builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr); 1123 } else { 1124 funcSymbolAttr = symbolAttr; 1125 } 1126 } 1127 1128 mlir::FunctionType funcType = 1129 funcPointer ? callSiteType : caller.getFuncOp().getType(); 1130 llvm::SmallVector<mlir::Value> operands; 1131 // First operand of indirect call is the function pointer. Cast it to 1132 // required function type for the call to handle procedures that have a 1133 // compatible interface in Fortran, but that have different signatures in 1134 // FIR. 1135 if (funcPointer) { 1136 operands.push_back( 1137 funcPointer.getType().isa<fir::BoxProcType>() 1138 ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer) 1139 : builder.createConvert(loc, funcType, funcPointer)); 1140 } 1141 1142 // Deal with potential mismatches in arguments types. Passing an array to a 1143 // scalar argument should for instance be tolerated here. 1144 bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface(); 1145 for (auto [fst, snd] : 1146 llvm::zip(caller.getInputs(), funcType.getInputs())) { 1147 // When passing arguments to a procedure that can be called an implicit 1148 // interface, allow character actual arguments to be passed to dummy 1149 // arguments of any type and vice versa 1150 mlir::Value cast; 1151 auto *context = builder.getContext(); 1152 if (snd.isa<fir::BoxProcType>() && 1153 fst.getType().isa<mlir::FunctionType>()) { 1154 auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None); 1155 auto boxProcTy = builder.getBoxProcType(funcTy); 1156 if (mlir::Value host = argumentHostAssocs(converter, fst)) { 1157 cast = builder.create<fir::EmboxProcOp>( 1158 loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host}); 1159 } else { 1160 cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst); 1161 } 1162 } else { 1163 cast = builder.convertWithSemantics(loc, snd, fst, 1164 callingImplicitInterface); 1165 } 1166 operands.push_back(cast); 1167 } 1168 1169 // Add host associations as necessary. 1170 if (addHostAssociations) 1171 operands.push_back(converter.hostAssocTupleValue()); 1172 1173 auto call = builder.create<fir::CallOp>(loc, funcType.getResults(), 1174 funcSymbolAttr, operands); 1175 1176 if (caller.mustSaveResult()) 1177 builder.create<fir::SaveResultOp>( 1178 loc, call.getResult(0), fir::getBase(allocatedResult.getValue()), 1179 arrayResultShape, resultLengths); 1180 1181 if (allocatedResult) { 1182 allocatedResult->match( 1183 [&](const fir::MutableBoxValue &box) { 1184 if (box.isAllocatable()) { 1185 TODO(loc, "allocatedResult for allocatable"); 1186 } 1187 }, 1188 [](const auto &) {}); 1189 return *allocatedResult; 1190 } 1191 1192 if (!resultType.hasValue()) 1193 return mlir::Value{}; // subroutine call 1194 // For now, Fortran return values are implemented with a single MLIR 1195 // function return value. 1196 assert(call.getNumResults() == 1 && 1197 "Expected exactly one result in FUNCTION call"); 1198 return call.getResult(0); 1199 } 1200 1201 /// Like genExtAddr, but ensure the address returned is a temporary even if \p 1202 /// expr is variable inside parentheses. 1203 ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) { 1204 // In general, genExtAddr might not create a temp for variable inside 1205 // parentheses to avoid creating array temporary in sub-expressions. It only 1206 // ensures the sub-expression is not re-associated with other parts of the 1207 // expression. In the call semantics, there is a difference between expr and 1208 // variable (see R1524). For expressions, a variable storage must not be 1209 // argument associated since it could be modified inside the call, or the 1210 // variable could also be modified by other means during the call. 1211 if (!isParenthesizedVariable(expr)) 1212 return genExtAddr(expr); 1213 mlir::Location loc = getLoc(); 1214 if (expr.Rank() > 0) 1215 TODO(loc, "genTempExtAddr array"); 1216 return genExtValue(expr).match( 1217 [&](const fir::CharBoxValue &boxChar) -> ExtValue { 1218 TODO(loc, "genTempExtAddr CharBoxValue"); 1219 }, 1220 [&](const fir::UnboxedValue &v) -> ExtValue { 1221 mlir::Type type = v.getType(); 1222 mlir::Value value = v; 1223 if (fir::isa_ref_type(type)) 1224 value = builder.create<fir::LoadOp>(loc, value); 1225 mlir::Value temp = builder.createTemporary(loc, value.getType()); 1226 builder.create<fir::StoreOp>(loc, value, temp); 1227 return temp; 1228 }, 1229 [&](const fir::BoxValue &x) -> ExtValue { 1230 // Derived type scalar that may be polymorphic. 1231 assert(!x.hasRank() && x.isDerived()); 1232 if (x.isDerivedWithLengthParameters()) 1233 fir::emitFatalError( 1234 loc, "making temps for derived type with length parameters"); 1235 // TODO: polymorphic aspects should be kept but for now the temp 1236 // created always has the declared type. 1237 mlir::Value var = 1238 fir::getBase(fir::factory::readBoxValue(builder, loc, x)); 1239 auto value = builder.create<fir::LoadOp>(loc, var); 1240 mlir::Value temp = builder.createTemporary(loc, value.getType()); 1241 builder.create<fir::StoreOp>(loc, value, temp); 1242 return temp; 1243 }, 1244 [&](const auto &) -> ExtValue { 1245 fir::emitFatalError(loc, "expr is not a scalar value"); 1246 }); 1247 } 1248 1249 /// Helper structure to track potential copy-in of non contiguous variable 1250 /// argument into a contiguous temp. It is used to deallocate the temp that 1251 /// may have been created as well as to the copy-out from the temp to the 1252 /// variable after the call. 1253 struct CopyOutPair { 1254 ExtValue var; 1255 ExtValue temp; 1256 // Flag to indicate if the argument may have been modified by the 1257 // callee, in which case it must be copied-out to the variable. 1258 bool argMayBeModifiedByCall; 1259 // Optional boolean value that, if present and false, prevents 1260 // the copy-out and temp deallocation. 1261 llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime; 1262 }; 1263 using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>; 1264 1265 /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories 1266 /// not based on fir.box. 1267 /// This will lose any non contiguous stride information and dynamic type and 1268 /// should only be called if \p exv is known to be contiguous or if its base 1269 /// address will be replaced by a contiguous one. If \p exv is not a 1270 /// fir::BoxValue, this is a no-op. 1271 ExtValue readIfBoxValue(const ExtValue &exv) { 1272 if (const auto *box = exv.getBoxOf<fir::BoxValue>()) 1273 return fir::factory::readBoxValue(builder, getLoc(), *box); 1274 return exv; 1275 } 1276 1277 /// Lower a non-elemental procedure reference. 1278 ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef, 1279 llvm::Optional<mlir::Type> resultType) { 1280 mlir::Location loc = getLoc(); 1281 if (isElementalProcWithArrayArgs(procRef)) 1282 fir::emitFatalError(loc, "trying to lower elemental procedure with array " 1283 "arguments as normal procedure"); 1284 if (const Fortran::evaluate::SpecificIntrinsic *intrinsic = 1285 procRef.proc().GetSpecificIntrinsic()) 1286 return genIntrinsicRef(procRef, *intrinsic, resultType); 1287 1288 if (isStatementFunctionCall(procRef)) 1289 TODO(loc, "Lower statement function call"); 1290 1291 Fortran::lower::CallerInterface caller(procRef, converter); 1292 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 1293 1294 llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall; 1295 // List of <var, temp> where temp must be copied into var after the call. 1296 CopyOutPairs copyOutPairs; 1297 1298 mlir::FunctionType callSiteType = caller.genFunctionType(); 1299 1300 // Lower the actual arguments and map the lowered values to the dummy 1301 // arguments. 1302 for (const Fortran::lower::CallInterface< 1303 Fortran::lower::CallerInterface>::PassedEntity &arg : 1304 caller.getPassedArguments()) { 1305 const auto *actual = arg.entity; 1306 mlir::Type argTy = callSiteType.getInput(arg.firArgument); 1307 if (!actual) { 1308 // Optional dummy argument for which there is no actual argument. 1309 caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy)); 1310 continue; 1311 } 1312 const auto *expr = actual->UnwrapExpr(); 1313 if (!expr) 1314 TODO(loc, "assumed type actual argument lowering"); 1315 1316 if (arg.passBy == PassBy::Value) { 1317 ExtValue argVal = genval(*expr); 1318 if (!fir::isUnboxedValue(argVal)) 1319 fir::emitFatalError( 1320 loc, "internal error: passing non trivial value by value"); 1321 caller.placeInput(arg, fir::getBase(argVal)); 1322 continue; 1323 } 1324 1325 if (arg.passBy == PassBy::MutableBox) { 1326 TODO(loc, "arg passby MutableBox"); 1327 } 1328 const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr); 1329 if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) { 1330 auto argAddr = [&]() -> ExtValue { 1331 ExtValue baseAddr; 1332 if (actualArgIsVariable && arg.isOptional()) { 1333 if (Fortran::evaluate::IsAllocatableOrPointerObject( 1334 *expr, converter.getFoldingContext())) { 1335 TODO(loc, "Allocatable or pointer argument"); 1336 } 1337 if (const Fortran::semantics::Symbol *wholeSymbol = 1338 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef( 1339 *expr)) 1340 if (Fortran::semantics::IsOptional(*wholeSymbol)) { 1341 TODO(loc, "procedureref optional arg"); 1342 } 1343 // Fall through: The actual argument can safely be 1344 // copied-in/copied-out without any care if needed. 1345 } 1346 if (actualArgIsVariable && expr->Rank() > 0) { 1347 TODO(loc, "procedureref arrays"); 1348 } 1349 // Actual argument is a non optional/non pointer/non allocatable 1350 // scalar. 1351 if (actualArgIsVariable) 1352 return genExtAddr(*expr); 1353 // Actual argument is not a variable. Make sure a variable address is 1354 // not passed. 1355 return genTempExtAddr(*expr); 1356 }(); 1357 // Scalar and contiguous expressions may be lowered to a fir.box, 1358 // either to account for potential polymorphism, or because lowering 1359 // did not account for some contiguity hints. 1360 // Here, polymorphism does not matter (an entity of the declared type 1361 // is passed, not one of the dynamic type), and the expr is known to 1362 // be simply contiguous, so it is safe to unbox it and pass the 1363 // address without making a copy. 1364 argAddr = readIfBoxValue(argAddr); 1365 1366 if (arg.passBy == PassBy::BaseAddress) { 1367 caller.placeInput(arg, fir::getBase(argAddr)); 1368 } else { 1369 TODO(loc, "procedureref PassBy::BoxChar"); 1370 } 1371 } else if (arg.passBy == PassBy::Box) { 1372 // Before lowering to an address, handle the allocatable/pointer actual 1373 // argument to optional fir.box dummy. It is legal to pass 1374 // unallocated/disassociated entity to an optional. In this case, an 1375 // absent fir.box must be created instead of a fir.box with a null value 1376 // (Fortran 2018 15.5.2.12 point 1). 1377 if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject( 1378 *expr, converter.getFoldingContext())) { 1379 TODO(loc, "optional allocatable or pointer argument"); 1380 } else { 1381 // Make sure a variable address is only passed if the expression is 1382 // actually a variable. 1383 mlir::Value box = 1384 actualArgIsVariable 1385 ? builder.createBox(loc, genBoxArg(*expr)) 1386 : builder.createBox(getLoc(), genTempExtAddr(*expr)); 1387 caller.placeInput(arg, box); 1388 } 1389 } else if (arg.passBy == PassBy::AddressAndLength) { 1390 ExtValue argRef = genExtAddr(*expr); 1391 caller.placeAddressAndLengthInput(arg, fir::getBase(argRef), 1392 fir::getLen(argRef)); 1393 } else if (arg.passBy == PassBy::CharProcTuple) { 1394 TODO(loc, "procedureref CharProcTuple"); 1395 } else { 1396 TODO(loc, "pass by value in non elemental function call"); 1397 } 1398 } 1399 1400 ExtValue result = genCallOpAndResult(caller, callSiteType, resultType); 1401 1402 // // Copy-out temps that were created for non contiguous variable arguments 1403 // if 1404 // // needed. 1405 // for (const auto ©OutPair : copyOutPairs) 1406 // genCopyOut(copyOutPair); 1407 1408 return result; 1409 } 1410 1411 template <typename A> 1412 ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) { 1413 ExtValue result = genFunctionRef(funcRef); 1414 if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType())) 1415 return genLoad(result); 1416 return result; 1417 } 1418 1419 ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) { 1420 llvm::Optional<mlir::Type> resTy; 1421 if (procRef.hasAlternateReturns()) 1422 resTy = builder.getIndexType(); 1423 return genProcedureRef(procRef, resTy); 1424 } 1425 1426 /// Generate a call to an intrinsic function. 1427 ExtValue 1428 genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef, 1429 const Fortran::evaluate::SpecificIntrinsic &intrinsic, 1430 llvm::Optional<mlir::Type> resultType) { 1431 llvm::SmallVector<ExtValue> operands; 1432 1433 llvm::StringRef name = intrinsic.name; 1434 mlir::Location loc = getLoc(); 1435 1436 const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering = 1437 Fortran::lower::getIntrinsicArgumentLowering(name); 1438 for (const auto &[arg, dummy] : 1439 llvm::zip(procRef.arguments(), 1440 intrinsic.characteristics.value().dummyArguments)) { 1441 auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg); 1442 if (!expr) { 1443 // Absent optional. 1444 operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument()); 1445 continue; 1446 } 1447 if (!argLowering) { 1448 // No argument lowering instruction, lower by value. 1449 operands.emplace_back(genval(*expr)); 1450 continue; 1451 } 1452 // Ad-hoc argument lowering handling. 1453 Fortran::lower::ArgLoweringRule argRules = 1454 Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering, 1455 dummy.name); 1456 switch (argRules.lowerAs) { 1457 case Fortran::lower::LowerIntrinsicArgAs::Value: 1458 operands.emplace_back(genval(*expr)); 1459 continue; 1460 case Fortran::lower::LowerIntrinsicArgAs::Addr: 1461 TODO(getLoc(), "argument lowering for Addr"); 1462 continue; 1463 case Fortran::lower::LowerIntrinsicArgAs::Box: 1464 TODO(getLoc(), "argument lowering for Box"); 1465 continue; 1466 case Fortran::lower::LowerIntrinsicArgAs::Inquired: 1467 TODO(getLoc(), "argument lowering for Inquired"); 1468 continue; 1469 } 1470 llvm_unreachable("bad switch"); 1471 } 1472 // Let the intrinsic library lower the intrinsic procedure call 1473 return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType, 1474 operands); 1475 } 1476 1477 template <typename A> 1478 ExtValue genval(const Fortran::evaluate::Expr<A> &x) { 1479 if (isScalar(x)) 1480 return std::visit([&](const auto &e) { return genval(e); }, x.u); 1481 TODO(getLoc(), "genval Expr<A> arrays"); 1482 } 1483 1484 /// Helper to detect Transformational function reference. 1485 template <typename T> 1486 bool isTransformationalRef(const T &) { 1487 return false; 1488 } 1489 template <typename T> 1490 bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) { 1491 return !funcRef.IsElemental() && funcRef.Rank(); 1492 } 1493 template <typename T> 1494 bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) { 1495 return std::visit([&](const auto &e) { return isTransformationalRef(e); }, 1496 expr.u); 1497 } 1498 1499 template <typename A> 1500 ExtValue gen(const Fortran::evaluate::Expr<A> &x) { 1501 // Whole array symbols or components, and results of transformational 1502 // functions already have a storage and the scalar expression lowering path 1503 // is used to not create a new temporary storage. 1504 if (isScalar(x) || 1505 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) || 1506 isTransformationalRef(x)) 1507 return std::visit([&](const auto &e) { return genref(e); }, x.u); 1508 TODO(getLoc(), "gen Expr non-scalar"); 1509 } 1510 1511 template <typename A> 1512 bool isScalar(const A &x) { 1513 return x.Rank() == 0; 1514 } 1515 1516 template <int KIND> 1517 ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type< 1518 Fortran::common::TypeCategory::Logical, KIND>> &exp) { 1519 return std::visit([&](const auto &e) { return genval(e); }, exp.u); 1520 } 1521 1522 using RefSet = 1523 std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring, 1524 Fortran::evaluate::DataRef, Fortran::evaluate::Component, 1525 Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef, 1526 Fortran::semantics::SymbolRef>; 1527 template <typename A> 1528 static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>; 1529 1530 template <typename A, typename = std::enable_if_t<inRefSet<A>>> 1531 ExtValue genref(const A &a) { 1532 return gen(a); 1533 } 1534 template <typename A> 1535 ExtValue genref(const A &a) { 1536 mlir::Type storageType = converter.genType(toEvExpr(a)); 1537 return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType); 1538 } 1539 1540 template <typename A, template <typename> typename T, 1541 typename B = std::decay_t<T<A>>, 1542 std::enable_if_t< 1543 std::is_same_v<B, Fortran::evaluate::Expr<A>> || 1544 std::is_same_v<B, Fortran::evaluate::Designator<A>> || 1545 std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>, 1546 bool> = true> 1547 ExtValue genref(const T<A> &x) { 1548 return gen(x); 1549 } 1550 1551 private: 1552 mlir::Location location; 1553 Fortran::lower::AbstractConverter &converter; 1554 fir::FirOpBuilder &builder; 1555 Fortran::lower::StatementContext &stmtCtx; 1556 Fortran::lower::SymMap &symMap; 1557 bool useBoxArg = false; // expression lowered as argument 1558 }; 1559 } // namespace 1560 1561 // Helper for changing the semantics in a given context. Preserves the current 1562 // semantics which is resumed when the "push" goes out of scope. 1563 #define PushSemantics(PushVal) \ 1564 [[maybe_unused]] auto pushSemanticsLocalVariable##__LINE__ = \ 1565 Fortran::common::ScopedSet(semant, PushVal); 1566 1567 static bool isAdjustedArrayElementType(mlir::Type t) { 1568 return fir::isa_char(t) || fir::isa_derived(t) || t.isa<fir::SequenceType>(); 1569 } 1570 1571 /// Build an ExtendedValue from a fir.array<?x...?xT> without actually setting 1572 /// the actual extents and lengths. This is only to allow their propagation as 1573 /// ExtendedValue without triggering verifier failures when propagating 1574 /// character/arrays as unboxed values. Only the base of the resulting 1575 /// ExtendedValue should be used, it is undefined to use the length or extents 1576 /// of the extended value returned, 1577 inline static fir::ExtendedValue 1578 convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder, 1579 mlir::Value val, mlir::Value len) { 1580 mlir::Type ty = fir::unwrapRefType(val.getType()); 1581 mlir::IndexType idxTy = builder.getIndexType(); 1582 auto seqTy = ty.cast<fir::SequenceType>(); 1583 auto undef = builder.create<fir::UndefOp>(loc, idxTy); 1584 llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), undef); 1585 if (fir::isa_char(seqTy.getEleTy())) 1586 return fir::CharArrayBoxValue(val, len ? len : undef, extents); 1587 return fir::ArrayBoxValue(val, extents); 1588 } 1589 1590 //===----------------------------------------------------------------------===// 1591 // 1592 // Lowering of array expressions. 1593 // 1594 //===----------------------------------------------------------------------===// 1595 1596 namespace { 1597 class ArrayExprLowering { 1598 using ExtValue = fir::ExtendedValue; 1599 1600 /// Structure to keep track of lowered array operands in the 1601 /// array expression. Useful to later deduce the shape of the 1602 /// array expression. 1603 struct ArrayOperand { 1604 /// Array base (can be a fir.box). 1605 mlir::Value memref; 1606 /// ShapeOp, ShapeShiftOp or ShiftOp 1607 mlir::Value shape; 1608 /// SliceOp 1609 mlir::Value slice; 1610 /// Can this operand be absent ? 1611 bool mayBeAbsent = false; 1612 }; 1613 1614 using ImplicitSubscripts = Fortran::lower::details::ImplicitSubscripts; 1615 using PathComponent = Fortran::lower::PathComponent; 1616 1617 /// Active iteration space. 1618 using IterationSpace = Fortran::lower::IterationSpace; 1619 using IterSpace = const Fortran::lower::IterationSpace &; 1620 1621 /// Current continuation. Function that will generate IR for a single 1622 /// iteration of the pending iterative loop structure. 1623 using CC = Fortran::lower::GenerateElementalArrayFunc; 1624 1625 /// Projection continuation. Function that will project one iteration space 1626 /// into another. 1627 using PC = std::function<IterationSpace(IterSpace)>; 1628 using ArrayBaseTy = 1629 std::variant<std::monostate, const Fortran::evaluate::ArrayRef *, 1630 const Fortran::evaluate::DataRef *>; 1631 using ComponentPath = Fortran::lower::ComponentPath; 1632 1633 public: 1634 //===--------------------------------------------------------------------===// 1635 // Regular array assignment 1636 //===--------------------------------------------------------------------===// 1637 1638 /// Entry point for array assignments. Both the left-hand and right-hand sides 1639 /// can either be ExtendedValue or evaluate::Expr. 1640 template <typename TL, typename TR> 1641 static void lowerArrayAssignment(Fortran::lower::AbstractConverter &converter, 1642 Fortran::lower::SymMap &symMap, 1643 Fortran::lower::StatementContext &stmtCtx, 1644 const TL &lhs, const TR &rhs) { 1645 ArrayExprLowering ael{converter, stmtCtx, symMap, 1646 ConstituentSemantics::CopyInCopyOut}; 1647 ael.lowerArrayAssignment(lhs, rhs); 1648 } 1649 1650 template <typename TL, typename TR> 1651 void lowerArrayAssignment(const TL &lhs, const TR &rhs) { 1652 mlir::Location loc = getLoc(); 1653 /// Here the target subspace is not necessarily contiguous. The ArrayUpdate 1654 /// continuation is implicitly returned in `ccStoreToDest` and the ArrayLoad 1655 /// in `destination`. 1656 PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut); 1657 ccStoreToDest = genarr(lhs); 1658 determineShapeOfDest(lhs); 1659 semant = ConstituentSemantics::RefTransparent; 1660 ExtValue exv = lowerArrayExpression(rhs); 1661 if (explicitSpaceIsActive()) { 1662 explicitSpace->finalizeContext(); 1663 builder.create<fir::ResultOp>(loc, fir::getBase(exv)); 1664 } else { 1665 builder.create<fir::ArrayMergeStoreOp>( 1666 loc, destination, fir::getBase(exv), destination.getMemref(), 1667 destination.getSlice(), destination.getTypeparams()); 1668 } 1669 } 1670 1671 //===--------------------------------------------------------------------===// 1672 // Array assignment to allocatable array 1673 //===--------------------------------------------------------------------===// 1674 1675 /// Entry point for assignment to allocatable array. 1676 static void lowerAllocatableArrayAssignment( 1677 Fortran::lower::AbstractConverter &converter, 1678 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 1679 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 1680 Fortran::lower::ExplicitIterSpace &explicitSpace, 1681 Fortran::lower::ImplicitIterSpace &implicitSpace) { 1682 ArrayExprLowering ael(converter, stmtCtx, symMap, 1683 ConstituentSemantics::CopyInCopyOut, &explicitSpace, 1684 &implicitSpace); 1685 ael.lowerAllocatableArrayAssignment(lhs, rhs); 1686 } 1687 1688 /// Assignment to allocatable array. 1689 /// 1690 /// The semantics are reverse that of a "regular" array assignment. The rhs 1691 /// defines the iteration space of the computation and the lhs is 1692 /// resized/reallocated to fit if necessary. 1693 void lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr &lhs, 1694 const Fortran::lower::SomeExpr &rhs) { 1695 // With assignment to allocatable, we want to lower the rhs first and use 1696 // its shape to determine if we need to reallocate, etc. 1697 mlir::Location loc = getLoc(); 1698 // FIXME: If the lhs is in an explicit iteration space, the assignment may 1699 // be to an array of allocatable arrays rather than a single allocatable 1700 // array. 1701 fir::MutableBoxValue mutableBox = 1702 createMutableBox(loc, converter, lhs, symMap); 1703 mlir::Type resultTy = converter.genType(rhs); 1704 if (rhs.Rank() > 0) 1705 determineShapeOfDest(rhs); 1706 auto rhsCC = [&]() { 1707 PushSemantics(ConstituentSemantics::RefTransparent); 1708 return genarr(rhs); 1709 }(); 1710 1711 llvm::SmallVector<mlir::Value> lengthParams; 1712 // Currently no safe way to gather length from rhs (at least for 1713 // character, it cannot be taken from array_loads since it may be 1714 // changed by concatenations). 1715 if ((mutableBox.isCharacter() && !mutableBox.hasNonDeferredLenParams()) || 1716 mutableBox.isDerivedWithLengthParameters()) 1717 TODO(loc, "gather rhs length parameters in assignment to allocatable"); 1718 1719 // The allocatable must take lower bounds from the expr if it is 1720 // reallocated and the right hand side is not a scalar. 1721 const bool takeLboundsIfRealloc = rhs.Rank() > 0; 1722 llvm::SmallVector<mlir::Value> lbounds; 1723 // When the reallocated LHS takes its lower bounds from the RHS, 1724 // they will be non default only if the RHS is a whole array 1725 // variable. Otherwise, lbounds is left empty and default lower bounds 1726 // will be used. 1727 if (takeLboundsIfRealloc && 1728 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs)) { 1729 assert(arrayOperands.size() == 1 && 1730 "lbounds can only come from one array"); 1731 std::vector<mlir::Value> lbs = 1732 fir::factory::getOrigins(arrayOperands[0].shape); 1733 lbounds.append(lbs.begin(), lbs.end()); 1734 } 1735 fir::factory::MutableBoxReallocation realloc = 1736 fir::factory::genReallocIfNeeded(builder, loc, mutableBox, destShape, 1737 lengthParams); 1738 // Create ArrayLoad for the mutable box and save it into `destination`. 1739 PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut); 1740 ccStoreToDest = genarr(realloc.newValue); 1741 // If the rhs is scalar, get shape from the allocatable ArrayLoad. 1742 if (destShape.empty()) 1743 destShape = getShape(destination); 1744 // Finish lowering the loop nest. 1745 assert(destination && "destination must have been set"); 1746 ExtValue exv = lowerArrayExpression(rhsCC, resultTy); 1747 if (explicitSpaceIsActive()) { 1748 explicitSpace->finalizeContext(); 1749 builder.create<fir::ResultOp>(loc, fir::getBase(exv)); 1750 } else { 1751 builder.create<fir::ArrayMergeStoreOp>( 1752 loc, destination, fir::getBase(exv), destination.getMemref(), 1753 destination.getSlice(), destination.getTypeparams()); 1754 } 1755 fir::factory::finalizeRealloc(builder, loc, mutableBox, lbounds, 1756 takeLboundsIfRealloc, realloc); 1757 } 1758 1759 /// Entry point into lowering an expression with rank. This entry point is for 1760 /// lowering a rhs expression, for example. (RefTransparent semantics.) 1761 static ExtValue 1762 lowerNewArrayExpression(Fortran::lower::AbstractConverter &converter, 1763 Fortran::lower::SymMap &symMap, 1764 Fortran::lower::StatementContext &stmtCtx, 1765 const Fortran::lower::SomeExpr &expr) { 1766 ArrayExprLowering ael{converter, stmtCtx, symMap}; 1767 ael.determineShapeOfDest(expr); 1768 ExtValue loopRes = ael.lowerArrayExpression(expr); 1769 fir::ArrayLoadOp dest = ael.destination; 1770 mlir::Value tempRes = dest.getMemref(); 1771 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 1772 mlir::Location loc = converter.getCurrentLocation(); 1773 builder.create<fir::ArrayMergeStoreOp>(loc, dest, fir::getBase(loopRes), 1774 tempRes, dest.getSlice(), 1775 dest.getTypeparams()); 1776 1777 auto arrTy = 1778 fir::dyn_cast_ptrEleTy(tempRes.getType()).cast<fir::SequenceType>(); 1779 if (auto charTy = 1780 arrTy.getEleTy().template dyn_cast<fir::CharacterType>()) { 1781 if (fir::characterWithDynamicLen(charTy)) 1782 TODO(loc, "CHARACTER does not have constant LEN"); 1783 mlir::Value len = builder.createIntegerConstant( 1784 loc, builder.getCharacterLengthType(), charTy.getLen()); 1785 return fir::CharArrayBoxValue(tempRes, len, dest.getExtents()); 1786 } 1787 return fir::ArrayBoxValue(tempRes, dest.getExtents()); 1788 } 1789 1790 // FIXME: should take multiple inner arguments. 1791 std::pair<IterationSpace, mlir::OpBuilder::InsertPoint> 1792 genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) { 1793 mlir::Location loc = getLoc(); 1794 mlir::IndexType idxTy = builder.getIndexType(); 1795 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); 1796 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); 1797 llvm::SmallVector<mlir::Value> loopUppers; 1798 1799 // Convert any implied shape to closed interval form. The fir.do_loop will 1800 // run from 0 to `extent - 1` inclusive. 1801 for (auto extent : shape) 1802 loopUppers.push_back( 1803 builder.create<mlir::arith::SubIOp>(loc, extent, one)); 1804 1805 // Iteration space is created with outermost columns, innermost rows 1806 llvm::SmallVector<fir::DoLoopOp> loops; 1807 1808 const std::size_t loopDepth = loopUppers.size(); 1809 llvm::SmallVector<mlir::Value> ivars; 1810 1811 for (auto i : llvm::enumerate(llvm::reverse(loopUppers))) { 1812 if (i.index() > 0) { 1813 assert(!loops.empty()); 1814 builder.setInsertionPointToStart(loops.back().getBody()); 1815 } 1816 fir::DoLoopOp loop; 1817 if (innerArg) { 1818 loop = builder.create<fir::DoLoopOp>( 1819 loc, zero, i.value(), one, isUnordered(), 1820 /*finalCount=*/false, mlir::ValueRange{innerArg}); 1821 innerArg = loop.getRegionIterArgs().front(); 1822 if (explicitSpaceIsActive()) 1823 explicitSpace->setInnerArg(0, innerArg); 1824 } else { 1825 loop = builder.create<fir::DoLoopOp>(loc, zero, i.value(), one, 1826 isUnordered(), 1827 /*finalCount=*/false); 1828 } 1829 ivars.push_back(loop.getInductionVar()); 1830 loops.push_back(loop); 1831 } 1832 1833 if (innerArg) 1834 for (std::remove_const_t<decltype(loopDepth)> i = 0; i + 1 < loopDepth; 1835 ++i) { 1836 builder.setInsertionPointToEnd(loops[i].getBody()); 1837 builder.create<fir::ResultOp>(loc, loops[i + 1].getResult(0)); 1838 } 1839 1840 // Move insertion point to the start of the innermost loop in the nest. 1841 builder.setInsertionPointToStart(loops.back().getBody()); 1842 // Set `afterLoopNest` to just after the entire loop nest. 1843 auto currPt = builder.saveInsertionPoint(); 1844 builder.setInsertionPointAfter(loops[0]); 1845 auto afterLoopNest = builder.saveInsertionPoint(); 1846 builder.restoreInsertionPoint(currPt); 1847 1848 // Put the implicit loop variables in row to column order to match FIR's 1849 // Ops. (The loops were constructed from outermost column to innermost 1850 // row.) 1851 mlir::Value outerRes = loops[0].getResult(0); 1852 return {IterationSpace(innerArg, outerRes, llvm::reverse(ivars)), 1853 afterLoopNest}; 1854 } 1855 1856 /// Build the iteration space into which the array expression will be 1857 /// lowered. The resultType is used to create a temporary, if needed. 1858 std::pair<IterationSpace, mlir::OpBuilder::InsertPoint> 1859 genIterSpace(mlir::Type resultType) { 1860 mlir::Location loc = getLoc(); 1861 llvm::SmallVector<mlir::Value> shape = genIterationShape(); 1862 if (!destination) { 1863 // Allocate storage for the result if it is not already provided. 1864 destination = createAndLoadSomeArrayTemp(resultType, shape); 1865 } 1866 1867 // Generate the lazy mask allocation, if one was given. 1868 if (ccPrelude.hasValue()) 1869 ccPrelude.getValue()(shape); 1870 1871 // Now handle the implicit loops. 1872 mlir::Value inner = explicitSpaceIsActive() 1873 ? explicitSpace->getInnerArgs().front() 1874 : destination.getResult(); 1875 auto [iters, afterLoopNest] = genImplicitLoops(shape, inner); 1876 mlir::Value innerArg = iters.innerArgument(); 1877 1878 // Generate the mask conditional structure, if there are masks. Unlike the 1879 // explicit masks, which are interleaved, these mask expression appear in 1880 // the innermost loop. 1881 if (implicitSpaceHasMasks()) { 1882 // Recover the cached condition from the mask buffer. 1883 auto genCond = [&](Fortran::lower::FrontEndExpr e, IterSpace iters) { 1884 return implicitSpace->getBoundClosure(e)(iters); 1885 }; 1886 1887 // Handle the negated conditions in topological order of the WHERE 1888 // clauses. See 10.2.3.2p4 as to why this control structure is produced. 1889 for (llvm::SmallVector<Fortran::lower::FrontEndExpr> maskExprs : 1890 implicitSpace->getMasks()) { 1891 const std::size_t size = maskExprs.size() - 1; 1892 auto genFalseBlock = [&](const auto *e, auto &&cond) { 1893 auto ifOp = builder.create<fir::IfOp>( 1894 loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond), 1895 /*withElseRegion=*/true); 1896 builder.create<fir::ResultOp>(loc, ifOp.getResult(0)); 1897 builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); 1898 builder.create<fir::ResultOp>(loc, innerArg); 1899 builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 1900 }; 1901 auto genTrueBlock = [&](const auto *e, auto &&cond) { 1902 auto ifOp = builder.create<fir::IfOp>( 1903 loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond), 1904 /*withElseRegion=*/true); 1905 builder.create<fir::ResultOp>(loc, ifOp.getResult(0)); 1906 builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 1907 builder.create<fir::ResultOp>(loc, innerArg); 1908 builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); 1909 }; 1910 for (std::remove_const_t<decltype(size)> i = 0; i < size; ++i) 1911 if (const auto *e = maskExprs[i]) 1912 genFalseBlock(e, genCond(e, iters)); 1913 1914 // The last condition is either non-negated or unconditionally negated. 1915 if (const auto *e = maskExprs[size]) 1916 genTrueBlock(e, genCond(e, iters)); 1917 } 1918 } 1919 1920 // We're ready to lower the body (an assignment statement) for this context 1921 // of loop nests at this point. 1922 return {iters, afterLoopNest}; 1923 } 1924 1925 fir::ArrayLoadOp 1926 createAndLoadSomeArrayTemp(mlir::Type type, 1927 llvm::ArrayRef<mlir::Value> shape) { 1928 if (ccLoadDest.hasValue()) 1929 return ccLoadDest.getValue()(shape); 1930 auto seqTy = type.dyn_cast<fir::SequenceType>(); 1931 assert(seqTy && "must be an array"); 1932 mlir::Location loc = getLoc(); 1933 // TODO: Need to thread the length parameters here. For character, they may 1934 // differ from the operands length (e.g concatenation). So the array loads 1935 // type parameters are not enough. 1936 if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>()) 1937 if (charTy.hasDynamicLen()) 1938 TODO(loc, "character array expression temp with dynamic length"); 1939 if (auto recTy = seqTy.getEleTy().dyn_cast<fir::RecordType>()) 1940 if (recTy.getNumLenParams() > 0) 1941 TODO(loc, "derived type array expression temp with length parameters"); 1942 mlir::Value temp = seqTy.hasConstantShape() 1943 ? builder.create<fir::AllocMemOp>(loc, type) 1944 : builder.create<fir::AllocMemOp>( 1945 loc, type, ".array.expr", llvm::None, shape); 1946 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 1947 stmtCtx.attachCleanup( 1948 [bldr, loc, temp]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 1949 mlir::Value shapeOp = genShapeOp(shape); 1950 return builder.create<fir::ArrayLoadOp>(loc, seqTy, temp, shapeOp, 1951 /*slice=*/mlir::Value{}, 1952 llvm::None); 1953 } 1954 1955 static fir::ShapeOp genShapeOp(mlir::Location loc, fir::FirOpBuilder &builder, 1956 llvm::ArrayRef<mlir::Value> shape) { 1957 mlir::IndexType idxTy = builder.getIndexType(); 1958 llvm::SmallVector<mlir::Value> idxShape; 1959 for (auto s : shape) 1960 idxShape.push_back(builder.createConvert(loc, idxTy, s)); 1961 auto shapeTy = fir::ShapeType::get(builder.getContext(), idxShape.size()); 1962 return builder.create<fir::ShapeOp>(loc, shapeTy, idxShape); 1963 } 1964 1965 fir::ShapeOp genShapeOp(llvm::ArrayRef<mlir::Value> shape) { 1966 return genShapeOp(getLoc(), builder, shape); 1967 } 1968 1969 //===--------------------------------------------------------------------===// 1970 // Expression traversal and lowering. 1971 //===--------------------------------------------------------------------===// 1972 1973 /// Lower the expression, \p x, in a scalar context. 1974 template <typename A> 1975 ExtValue asScalar(const A &x) { 1976 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.genval(x); 1977 } 1978 1979 /// Lower the expression in a scalar context to a memory reference. 1980 template <typename A> 1981 ExtValue asScalarRef(const A &x) { 1982 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.gen(x); 1983 } 1984 1985 // An expression with non-zero rank is an array expression. 1986 template <typename A> 1987 bool isArray(const A &x) const { 1988 return x.Rank() != 0; 1989 } 1990 1991 /// If there were temporaries created for this element evaluation, finalize 1992 /// and deallocate the resources now. This should be done just prior the the 1993 /// fir::ResultOp at the end of the innermost loop. 1994 void finalizeElementCtx() { 1995 if (elementCtx) { 1996 stmtCtx.finalize(/*popScope=*/true); 1997 elementCtx = false; 1998 } 1999 } 2000 2001 template <typename A> 2002 CC genScalarAndForwardValue(const A &x) { 2003 ExtValue result = asScalar(x); 2004 return [=](IterSpace) { return result; }; 2005 } 2006 2007 template <typename A, typename = std::enable_if_t<Fortran::common::HasMember< 2008 A, Fortran::evaluate::TypelessExpression>>> 2009 CC genarr(const A &x) { 2010 return genScalarAndForwardValue(x); 2011 } 2012 2013 template <typename A> 2014 CC genarr(const Fortran::evaluate::Expr<A> &x) { 2015 LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(llvm::dbgs(), x)); 2016 if (isArray(x) || explicitSpaceIsActive() || 2017 isElementalProcWithArrayArgs(x)) 2018 return std::visit([&](const auto &e) { return genarr(e); }, x.u); 2019 return genScalarAndForwardValue(x); 2020 } 2021 2022 template <Fortran::common::TypeCategory TC1, int KIND, 2023 Fortran::common::TypeCategory TC2> 2024 CC genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, 2025 TC2> &x) { 2026 TODO(getLoc(), ""); 2027 } 2028 2029 template <int KIND> 2030 CC genarr(const Fortran::evaluate::ComplexComponent<KIND> &x) { 2031 TODO(getLoc(), ""); 2032 } 2033 2034 template <typename T> 2035 CC genarr(const Fortran::evaluate::Parentheses<T> &x) { 2036 TODO(getLoc(), ""); 2037 } 2038 2039 template <int KIND> 2040 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 2041 Fortran::common::TypeCategory::Integer, KIND>> &x) { 2042 TODO(getLoc(), ""); 2043 } 2044 2045 template <int KIND> 2046 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 2047 Fortran::common::TypeCategory::Real, KIND>> &x) { 2048 TODO(getLoc(), ""); 2049 } 2050 template <int KIND> 2051 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 2052 Fortran::common::TypeCategory::Complex, KIND>> &x) { 2053 TODO(getLoc(), ""); 2054 } 2055 2056 #undef GENBIN 2057 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \ 2058 template <int KIND> \ 2059 CC genarr(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \ 2060 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \ 2061 TODO(getLoc(), "genarr Binary"); \ 2062 } 2063 2064 GENBIN(Add, Integer, mlir::arith::AddIOp) 2065 GENBIN(Add, Real, mlir::arith::AddFOp) 2066 GENBIN(Add, Complex, fir::AddcOp) 2067 GENBIN(Subtract, Integer, mlir::arith::SubIOp) 2068 GENBIN(Subtract, Real, mlir::arith::SubFOp) 2069 GENBIN(Subtract, Complex, fir::SubcOp) 2070 GENBIN(Multiply, Integer, mlir::arith::MulIOp) 2071 GENBIN(Multiply, Real, mlir::arith::MulFOp) 2072 GENBIN(Multiply, Complex, fir::MulcOp) 2073 GENBIN(Divide, Integer, mlir::arith::DivSIOp) 2074 GENBIN(Divide, Real, mlir::arith::DivFOp) 2075 GENBIN(Divide, Complex, fir::DivcOp) 2076 2077 template <Fortran::common::TypeCategory TC, int KIND> 2078 CC genarr( 2079 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) { 2080 TODO(getLoc(), "genarr "); 2081 } 2082 template <Fortran::common::TypeCategory TC, int KIND> 2083 CC genarr( 2084 const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) { 2085 TODO(getLoc(), "genarr "); 2086 } 2087 template <Fortran::common::TypeCategory TC, int KIND> 2088 CC genarr( 2089 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>> 2090 &x) { 2091 TODO(getLoc(), "genarr "); 2092 } 2093 template <int KIND> 2094 CC genarr(const Fortran::evaluate::ComplexConstructor<KIND> &x) { 2095 TODO(getLoc(), "genarr "); 2096 } 2097 2098 template <int KIND> 2099 CC genarr(const Fortran::evaluate::Concat<KIND> &x) { 2100 TODO(getLoc(), "genarr "); 2101 } 2102 2103 template <int KIND> 2104 CC genarr(const Fortran::evaluate::SetLength<KIND> &x) { 2105 TODO(getLoc(), "genarr "); 2106 } 2107 2108 template <typename A> 2109 CC genarr(const Fortran::evaluate::Constant<A> &x) { 2110 TODO(getLoc(), "genarr "); 2111 } 2112 2113 CC genarr(const Fortran::semantics::SymbolRef &sym, 2114 ComponentPath &components) { 2115 return genarr(sym.get(), components); 2116 } 2117 2118 ExtValue abstractArrayExtValue(mlir::Value val, mlir::Value len = {}) { 2119 return convertToArrayBoxValue(getLoc(), builder, val, len); 2120 } 2121 2122 CC genarr(const ExtValue &extMemref) { 2123 ComponentPath dummy(/*isImplicit=*/true); 2124 return genarr(extMemref, dummy); 2125 } 2126 2127 template <typename A> 2128 CC genarr(const Fortran::evaluate::ArrayConstructor<A> &x) { 2129 TODO(getLoc(), "genarr ArrayConstructor<A>"); 2130 } 2131 2132 CC genarr(const Fortran::evaluate::ImpliedDoIndex &) { 2133 TODO(getLoc(), "genarr ImpliedDoIndex"); 2134 } 2135 2136 CC genarr(const Fortran::evaluate::TypeParamInquiry &x) { 2137 TODO(getLoc(), "genarr TypeParamInquiry"); 2138 } 2139 2140 CC genarr(const Fortran::evaluate::DescriptorInquiry &x) { 2141 TODO(getLoc(), "genarr DescriptorInquiry"); 2142 } 2143 2144 CC genarr(const Fortran::evaluate::StructureConstructor &x) { 2145 TODO(getLoc(), "genarr StructureConstructor"); 2146 } 2147 2148 template <int KIND> 2149 CC genarr(const Fortran::evaluate::Not<KIND> &x) { 2150 TODO(getLoc(), "genarr Not"); 2151 } 2152 2153 template <int KIND> 2154 CC genarr(const Fortran::evaluate::LogicalOperation<KIND> &x) { 2155 TODO(getLoc(), "genarr LogicalOperation"); 2156 } 2157 2158 template <int KIND> 2159 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 2160 Fortran::common::TypeCategory::Integer, KIND>> &x) { 2161 TODO(getLoc(), "genarr Relational Integer"); 2162 } 2163 template <int KIND> 2164 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 2165 Fortran::common::TypeCategory::Character, KIND>> &x) { 2166 TODO(getLoc(), "genarr Relational Character"); 2167 } 2168 template <int KIND> 2169 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 2170 Fortran::common::TypeCategory::Real, KIND>> &x) { 2171 TODO(getLoc(), "genarr Relational Real"); 2172 } 2173 template <int KIND> 2174 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 2175 Fortran::common::TypeCategory::Complex, KIND>> &x) { 2176 TODO(getLoc(), "genarr Relational Complex"); 2177 } 2178 CC genarr( 2179 const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) { 2180 TODO(getLoc(), "genarr Relational SomeType"); 2181 } 2182 2183 template <typename A> 2184 CC genarr(const Fortran::evaluate::Designator<A> &des) { 2185 ComponentPath components(des.Rank() > 0); 2186 return std::visit([&](const auto &x) { return genarr(x, components); }, 2187 des.u); 2188 } 2189 2190 template <typename T> 2191 CC genarr(const Fortran::evaluate::FunctionRef<T> &funRef) { 2192 TODO(getLoc(), "genarr FunctionRef"); 2193 } 2194 2195 template <typename A> 2196 CC genImplicitArrayAccess(const A &x, ComponentPath &components) { 2197 components.reversePath.push_back(ImplicitSubscripts{}); 2198 ExtValue exv = asScalarRef(x); 2199 // lowerPath(exv, components); 2200 auto lambda = genarr(exv, components); 2201 return [=](IterSpace iters) { return lambda(components.pc(iters)); }; 2202 } 2203 CC genImplicitArrayAccess(const Fortran::evaluate::NamedEntity &x, 2204 ComponentPath &components) { 2205 if (x.IsSymbol()) 2206 return genImplicitArrayAccess(x.GetFirstSymbol(), components); 2207 return genImplicitArrayAccess(x.GetComponent(), components); 2208 } 2209 2210 template <typename A> 2211 CC genAsScalar(const A &x) { 2212 mlir::Location loc = getLoc(); 2213 if (isProjectedCopyInCopyOut()) { 2214 return [=, &x, builder = &converter.getFirOpBuilder()]( 2215 IterSpace iters) -> ExtValue { 2216 ExtValue exv = asScalarRef(x); 2217 mlir::Value val = fir::getBase(exv); 2218 mlir::Type eleTy = fir::unwrapRefType(val.getType()); 2219 if (isAdjustedArrayElementType(eleTy)) { 2220 if (fir::isa_char(eleTy)) { 2221 TODO(getLoc(), "assignment of character type"); 2222 } else if (fir::isa_derived(eleTy)) { 2223 TODO(loc, "assignment of derived type"); 2224 } else { 2225 fir::emitFatalError(loc, "array type not expected in scalar"); 2226 } 2227 } else { 2228 builder->create<fir::StoreOp>(loc, iters.getElement(), val); 2229 } 2230 return exv; 2231 }; 2232 } 2233 return [=, &x](IterSpace) { return asScalar(x); }; 2234 } 2235 2236 CC genarr(const Fortran::semantics::Symbol &x, ComponentPath &components) { 2237 if (explicitSpaceIsActive()) { 2238 TODO(getLoc(), "genarr Symbol explicitSpace"); 2239 } else { 2240 return genImplicitArrayAccess(x, components); 2241 } 2242 } 2243 2244 CC genarr(const Fortran::evaluate::Component &x, ComponentPath &components) { 2245 TODO(getLoc(), "genarr Component"); 2246 } 2247 2248 CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) { 2249 TODO(getLoc(), "genar ArrayRef"); 2250 } 2251 2252 CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) { 2253 TODO(getLoc(), "coarray reference"); 2254 } 2255 2256 CC genarr(const Fortran::evaluate::NamedEntity &x, 2257 ComponentPath &components) { 2258 return x.IsSymbol() ? genarr(x.GetFirstSymbol(), components) 2259 : genarr(x.GetComponent(), components); 2260 } 2261 2262 CC genarr(const Fortran::evaluate::DataRef &x, ComponentPath &components) { 2263 return std::visit([&](const auto &v) { return genarr(v, components); }, 2264 x.u); 2265 } 2266 2267 CC genarr(const Fortran::evaluate::ComplexPart &x, 2268 ComponentPath &components) { 2269 TODO(getLoc(), "genarr ComplexPart"); 2270 } 2271 2272 CC genarr(const Fortran::evaluate::StaticDataObject::Pointer &, 2273 ComponentPath &components) { 2274 TODO(getLoc(), "genarr StaticDataObject::Pointer"); 2275 } 2276 2277 /// Substrings (see 9.4.1) 2278 CC genarr(const Fortran::evaluate::Substring &x, ComponentPath &components) { 2279 TODO(getLoc(), "genarr Substring"); 2280 } 2281 2282 /// Base case of generating an array reference, 2283 CC genarr(const ExtValue &extMemref, ComponentPath &components) { 2284 mlir::Location loc = getLoc(); 2285 mlir::Value memref = fir::getBase(extMemref); 2286 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(memref.getType()); 2287 assert(arrTy.isa<fir::SequenceType>() && "memory ref must be an array"); 2288 mlir::Value shape = builder.createShape(loc, extMemref); 2289 mlir::Value slice; 2290 if (components.isSlice()) { 2291 TODO(loc, "genarr with Slices"); 2292 } 2293 arrayOperands.push_back(ArrayOperand{memref, shape, slice}); 2294 if (destShape.empty()) 2295 destShape = getShape(arrayOperands.back()); 2296 if (isBoxValue()) { 2297 TODO(loc, "genarr BoxValue"); 2298 } 2299 if (isReferentiallyOpaque()) { 2300 TODO(loc, "genarr isReferentiallyOpaque"); 2301 } 2302 auto arrLoad = builder.create<fir::ArrayLoadOp>( 2303 loc, arrTy, memref, shape, slice, fir::getTypeParams(extMemref)); 2304 mlir::Value arrLd = arrLoad.getResult(); 2305 if (isProjectedCopyInCopyOut()) { 2306 // Semantics are projected copy-in copy-out. 2307 // The backing store of the destination of an array expression may be 2308 // partially modified. These updates are recorded in FIR by forwarding a 2309 // continuation that generates an `array_update` Op. The destination is 2310 // always loaded at the beginning of the statement and merged at the 2311 // end. 2312 destination = arrLoad; 2313 auto lambda = ccStoreToDest.hasValue() 2314 ? ccStoreToDest.getValue() 2315 : defaultStoreToDestination(components.substring); 2316 return [=](IterSpace iters) -> ExtValue { return lambda(iters); }; 2317 } 2318 if (isCustomCopyInCopyOut()) { 2319 TODO(loc, "isCustomCopyInCopyOut"); 2320 } 2321 if (isCopyInCopyOut()) { 2322 // Semantics are copy-in copy-out. 2323 // The continuation simply forwards the result of the `array_load` Op, 2324 // which is the value of the array as it was when loaded. All data 2325 // references with rank > 0 in an array expression typically have 2326 // copy-in copy-out semantics. 2327 return [=](IterSpace) -> ExtValue { return arrLd; }; 2328 } 2329 mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams(); 2330 if (isValueAttribute()) { 2331 // Semantics are value attribute. 2332 // Here the continuation will `array_fetch` a value from an array and 2333 // then store that value in a temporary. One can thus imitate pass by 2334 // value even when the call is pass by reference. 2335 return [=](IterSpace iters) -> ExtValue { 2336 mlir::Value base; 2337 mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec()); 2338 if (isAdjustedArrayElementType(eleTy)) { 2339 mlir::Type eleRefTy = builder.getRefType(eleTy); 2340 base = builder.create<fir::ArrayAccessOp>( 2341 loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams); 2342 } else { 2343 base = builder.create<fir::ArrayFetchOp>( 2344 loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams); 2345 } 2346 mlir::Value temp = builder.createTemporary( 2347 loc, base.getType(), 2348 llvm::ArrayRef<mlir::NamedAttribute>{ 2349 Fortran::lower::getAdaptToByRefAttr(builder)}); 2350 builder.create<fir::StoreOp>(loc, base, temp); 2351 return fir::factory::arraySectionElementToExtendedValue( 2352 builder, loc, extMemref, temp, slice); 2353 }; 2354 } 2355 // In the default case, the array reference forwards an `array_fetch` or 2356 // `array_access` Op in the continuation. 2357 return [=](IterSpace iters) -> ExtValue { 2358 mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec()); 2359 if (isAdjustedArrayElementType(eleTy)) { 2360 mlir::Type eleRefTy = builder.getRefType(eleTy); 2361 mlir::Value arrayOp = builder.create<fir::ArrayAccessOp>( 2362 loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams); 2363 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 2364 llvm::SmallVector<mlir::Value> substringBounds; 2365 populateBounds(substringBounds, components.substring); 2366 if (!substringBounds.empty()) { 2367 // mlir::Value dstLen = fir::factory::genLenOfCharacter( 2368 // builder, loc, arrLoad, iters.iterVec(), substringBounds); 2369 // fir::CharBoxValue dstChar(arrayOp, dstLen); 2370 // return fir::factory::CharacterExprHelper{builder, loc} 2371 // .createSubstring(dstChar, substringBounds); 2372 } 2373 } 2374 return fir::factory::arraySectionElementToExtendedValue( 2375 builder, loc, extMemref, arrayOp, slice); 2376 } 2377 auto arrFetch = builder.create<fir::ArrayFetchOp>( 2378 loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams); 2379 return fir::factory::arraySectionElementToExtendedValue( 2380 builder, loc, extMemref, arrFetch, slice); 2381 }; 2382 } 2383 2384 /// Reduce the rank of a array to be boxed based on the slice's operands. 2385 static mlir::Type reduceRank(mlir::Type arrTy, mlir::Value slice) { 2386 if (slice) { 2387 auto slOp = mlir::dyn_cast<fir::SliceOp>(slice.getDefiningOp()); 2388 assert(slOp && "expected slice op"); 2389 auto seqTy = arrTy.dyn_cast<fir::SequenceType>(); 2390 assert(seqTy && "expected array type"); 2391 mlir::Operation::operand_range triples = slOp.getTriples(); 2392 fir::SequenceType::Shape shape; 2393 // reduce the rank for each invariant dimension 2394 for (unsigned i = 1, end = triples.size(); i < end; i += 3) 2395 if (!mlir::isa_and_nonnull<fir::UndefOp>(triples[i].getDefiningOp())) 2396 shape.push_back(fir::SequenceType::getUnknownExtent()); 2397 return fir::SequenceType::get(shape, seqTy.getEleTy()); 2398 } 2399 // not sliced, so no change in rank 2400 return arrTy; 2401 } 2402 2403 private: 2404 void determineShapeOfDest(const fir::ExtendedValue &lhs) { 2405 destShape = fir::factory::getExtents(builder, getLoc(), lhs); 2406 } 2407 2408 void determineShapeOfDest(const Fortran::lower::SomeExpr &lhs) { 2409 if (!destShape.empty()) 2410 return; 2411 // if (explicitSpaceIsActive() && determineShapeWithSlice(lhs)) 2412 // return; 2413 mlir::Type idxTy = builder.getIndexType(); 2414 mlir::Location loc = getLoc(); 2415 if (std::optional<Fortran::evaluate::ConstantSubscripts> constantShape = 2416 Fortran::evaluate::GetConstantExtents(converter.getFoldingContext(), 2417 lhs)) 2418 for (Fortran::common::ConstantSubscript extent : *constantShape) 2419 destShape.push_back(builder.createIntegerConstant(loc, idxTy, extent)); 2420 } 2421 2422 ExtValue lowerArrayExpression(const Fortran::lower::SomeExpr &exp) { 2423 mlir::Type resTy = converter.genType(exp); 2424 return std::visit( 2425 [&](const auto &e) { return lowerArrayExpression(genarr(e), resTy); }, 2426 exp.u); 2427 } 2428 ExtValue lowerArrayExpression(const ExtValue &exv) { 2429 assert(!explicitSpace); 2430 mlir::Type resTy = fir::unwrapPassByRefType(fir::getBase(exv).getType()); 2431 return lowerArrayExpression(genarr(exv), resTy); 2432 } 2433 2434 void populateBounds(llvm::SmallVectorImpl<mlir::Value> &bounds, 2435 const Fortran::evaluate::Substring *substring) { 2436 if (!substring) 2437 return; 2438 bounds.push_back(fir::getBase(asScalar(substring->lower()))); 2439 if (auto upper = substring->upper()) 2440 bounds.push_back(fir::getBase(asScalar(*upper))); 2441 } 2442 2443 /// Default store to destination implementation. 2444 /// This implements the default case, which is to assign the value in 2445 /// `iters.element` into the destination array, `iters.innerArgument`. Handles 2446 /// by value and by reference assignment. 2447 CC defaultStoreToDestination(const Fortran::evaluate::Substring *substring) { 2448 return [=](IterSpace iterSpace) -> ExtValue { 2449 mlir::Location loc = getLoc(); 2450 mlir::Value innerArg = iterSpace.innerArgument(); 2451 fir::ExtendedValue exv = iterSpace.elementExv(); 2452 mlir::Type arrTy = innerArg.getType(); 2453 mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec()); 2454 if (isAdjustedArrayElementType(eleTy)) { 2455 TODO(loc, "isAdjustedArrayElementType"); 2456 } 2457 // By value semantics. The element is being assigned by value. 2458 mlir::Value ele = builder.createConvert(loc, eleTy, fir::getBase(exv)); 2459 auto update = builder.create<fir::ArrayUpdateOp>( 2460 loc, arrTy, innerArg, ele, iterSpace.iterVec(), 2461 destination.getTypeparams()); 2462 return abstractArrayExtValue(update); 2463 }; 2464 } 2465 2466 /// For an elemental array expression. 2467 /// 1. Lower the scalars and array loads. 2468 /// 2. Create the iteration space. 2469 /// 3. Create the element-by-element computation in the loop. 2470 /// 4. Return the resulting array value. 2471 /// If no destination was set in the array context, a temporary of 2472 /// \p resultTy will be created to hold the evaluated expression. 2473 /// Otherwise, \p resultTy is ignored and the expression is evaluated 2474 /// in the destination. \p f is a continuation built from an 2475 /// evaluate::Expr or an ExtendedValue. 2476 ExtValue lowerArrayExpression(CC f, mlir::Type resultTy) { 2477 mlir::Location loc = getLoc(); 2478 auto [iterSpace, insPt] = genIterSpace(resultTy); 2479 auto exv = f(iterSpace); 2480 iterSpace.setElement(std::move(exv)); 2481 auto lambda = ccStoreToDest.hasValue() 2482 ? ccStoreToDest.getValue() 2483 : defaultStoreToDestination(/*substring=*/nullptr); 2484 mlir::Value updVal = fir::getBase(lambda(iterSpace)); 2485 finalizeElementCtx(); 2486 builder.create<fir::ResultOp>(loc, updVal); 2487 builder.restoreInsertionPoint(insPt); 2488 return abstractArrayExtValue(iterSpace.outerResult()); 2489 } 2490 2491 /// Get the shape from an ArrayOperand. The shape of the array is adjusted if 2492 /// the array was sliced. 2493 llvm::SmallVector<mlir::Value> getShape(ArrayOperand array) { 2494 // if (array.slice) 2495 // return computeSliceShape(array.slice); 2496 if (array.memref.getType().isa<fir::BoxType>()) 2497 return fir::factory::readExtents(builder, getLoc(), 2498 fir::BoxValue{array.memref}); 2499 std::vector<mlir::Value, std::allocator<mlir::Value>> extents = 2500 fir::factory::getExtents(array.shape); 2501 return {extents.begin(), extents.end()}; 2502 } 2503 2504 /// Get the shape from an ArrayLoad. 2505 llvm::SmallVector<mlir::Value> getShape(fir::ArrayLoadOp arrayLoad) { 2506 return getShape(ArrayOperand{arrayLoad.getMemref(), arrayLoad.getShape(), 2507 arrayLoad.getSlice()}); 2508 } 2509 2510 /// Returns the first array operand that may not be absent. If all 2511 /// array operands may be absent, return the first one. 2512 const ArrayOperand &getInducingShapeArrayOperand() const { 2513 assert(!arrayOperands.empty()); 2514 for (const ArrayOperand &op : arrayOperands) 2515 if (!op.mayBeAbsent) 2516 return op; 2517 // If all arrays operand appears in optional position, then none of them 2518 // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the 2519 // first operands. 2520 // TODO: There is an opportunity to add a runtime check here that 2521 // this array is present as required. 2522 return arrayOperands[0]; 2523 } 2524 2525 /// Generate the shape of the iteration space over the array expression. The 2526 /// iteration space may be implicit, explicit, or both. If it is implied it is 2527 /// based on the destination and operand array loads, or an optional 2528 /// Fortran::evaluate::Shape from the front end. If the shape is explicit, 2529 /// this returns any implicit shape component, if it exists. 2530 llvm::SmallVector<mlir::Value> genIterationShape() { 2531 // Use the precomputed destination shape. 2532 if (!destShape.empty()) 2533 return destShape; 2534 // Otherwise, use the destination's shape. 2535 if (destination) 2536 return getShape(destination); 2537 // Otherwise, use the first ArrayLoad operand shape. 2538 if (!arrayOperands.empty()) 2539 return getShape(getInducingShapeArrayOperand()); 2540 fir::emitFatalError(getLoc(), 2541 "failed to compute the array expression shape"); 2542 } 2543 2544 bool explicitSpaceIsActive() const { 2545 return explicitSpace && explicitSpace->isActive(); 2546 } 2547 2548 bool implicitSpaceHasMasks() const { 2549 return implicitSpace && !implicitSpace->empty(); 2550 } 2551 2552 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter, 2553 Fortran::lower::StatementContext &stmtCtx, 2554 Fortran::lower::SymMap &symMap) 2555 : converter{converter}, builder{converter.getFirOpBuilder()}, 2556 stmtCtx{stmtCtx}, symMap{symMap} {} 2557 2558 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter, 2559 Fortran::lower::StatementContext &stmtCtx, 2560 Fortran::lower::SymMap &symMap, 2561 ConstituentSemantics sem) 2562 : converter{converter}, builder{converter.getFirOpBuilder()}, 2563 stmtCtx{stmtCtx}, symMap{symMap}, semant{sem} {} 2564 2565 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter, 2566 Fortran::lower::StatementContext &stmtCtx, 2567 Fortran::lower::SymMap &symMap, 2568 ConstituentSemantics sem, 2569 Fortran::lower::ExplicitIterSpace *expSpace, 2570 Fortran::lower::ImplicitIterSpace *impSpace) 2571 : converter{converter}, builder{converter.getFirOpBuilder()}, 2572 stmtCtx{stmtCtx}, symMap{symMap}, 2573 explicitSpace(expSpace->isActive() ? expSpace : nullptr), 2574 implicitSpace(impSpace->empty() ? nullptr : impSpace), semant{sem} { 2575 // Generate any mask expressions, as necessary. This is the compute step 2576 // that creates the effective masks. See 10.2.3.2 in particular. 2577 // genMasks(); 2578 } 2579 2580 mlir::Location getLoc() { return converter.getCurrentLocation(); } 2581 2582 /// Array appears in a lhs context such that it is assigned after the rhs is 2583 /// fully evaluated. 2584 inline bool isCopyInCopyOut() { 2585 return semant == ConstituentSemantics::CopyInCopyOut; 2586 } 2587 2588 /// Array appears in a lhs (or temp) context such that a projected, 2589 /// discontiguous subspace of the array is assigned after the rhs is fully 2590 /// evaluated. That is, the rhs array value is merged into a section of the 2591 /// lhs array. 2592 inline bool isProjectedCopyInCopyOut() { 2593 return semant == ConstituentSemantics::ProjectedCopyInCopyOut; 2594 } 2595 2596 inline bool isCustomCopyInCopyOut() { 2597 return semant == ConstituentSemantics::CustomCopyInCopyOut; 2598 } 2599 2600 /// Array appears in a context where it must be boxed. 2601 inline bool isBoxValue() { return semant == ConstituentSemantics::BoxValue; } 2602 2603 /// Array appears in a context where differences in the memory reference can 2604 /// be observable in the computational results. For example, an array 2605 /// element is passed to an impure procedure. 2606 inline bool isReferentiallyOpaque() { 2607 return semant == ConstituentSemantics::RefOpaque; 2608 } 2609 2610 /// Array appears in a context where it is passed as a VALUE argument. 2611 inline bool isValueAttribute() { 2612 return semant == ConstituentSemantics::ByValueArg; 2613 } 2614 2615 /// Can the loops over the expression be unordered? 2616 inline bool isUnordered() const { return unordered; } 2617 2618 void setUnordered(bool b) { unordered = b; } 2619 2620 Fortran::lower::AbstractConverter &converter; 2621 fir::FirOpBuilder &builder; 2622 Fortran::lower::StatementContext &stmtCtx; 2623 bool elementCtx = false; 2624 Fortran::lower::SymMap &symMap; 2625 /// The continuation to generate code to update the destination. 2626 llvm::Optional<CC> ccStoreToDest; 2627 llvm::Optional<std::function<void(llvm::ArrayRef<mlir::Value>)>> ccPrelude; 2628 llvm::Optional<std::function<fir::ArrayLoadOp(llvm::ArrayRef<mlir::Value>)>> 2629 ccLoadDest; 2630 /// The destination is the loaded array into which the results will be 2631 /// merged. 2632 fir::ArrayLoadOp destination; 2633 /// The shape of the destination. 2634 llvm::SmallVector<mlir::Value> destShape; 2635 /// List of arrays in the expression that have been loaded. 2636 llvm::SmallVector<ArrayOperand> arrayOperands; 2637 /// If there is a user-defined iteration space, explicitShape will hold the 2638 /// information from the front end. 2639 Fortran::lower::ExplicitIterSpace *explicitSpace = nullptr; 2640 Fortran::lower::ImplicitIterSpace *implicitSpace = nullptr; 2641 ConstituentSemantics semant = ConstituentSemantics::RefTransparent; 2642 // Can the array expression be evaluated in any order? 2643 // Will be set to false if any of the expression parts prevent this. 2644 bool unordered = true; 2645 }; 2646 } // namespace 2647 2648 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression( 2649 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 2650 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 2651 Fortran::lower::StatementContext &stmtCtx) { 2652 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n'); 2653 return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr); 2654 } 2655 2656 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress( 2657 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 2658 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 2659 Fortran::lower::StatementContext &stmtCtx) { 2660 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n'); 2661 return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr); 2662 } 2663 2664 fir::MutableBoxValue Fortran::lower::createMutableBox( 2665 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 2666 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) { 2667 // MutableBox lowering StatementContext does not need to be propagated 2668 // to the caller because the result value is a variable, not a temporary 2669 // expression. The StatementContext clean-up can occur before using the 2670 // resulting MutableBoxValue. Variables of all other types are handled in the 2671 // bridge. 2672 Fortran::lower::StatementContext dummyStmtCtx; 2673 return ScalarExprLowering{loc, converter, symMap, dummyStmtCtx} 2674 .genMutableBoxValue(expr); 2675 } 2676 2677 mlir::Value Fortran::lower::createSubroutineCall( 2678 AbstractConverter &converter, const evaluate::ProcedureRef &call, 2679 SymMap &symMap, StatementContext &stmtCtx) { 2680 mlir::Location loc = converter.getCurrentLocation(); 2681 2682 // Simple subroutine call, with potential alternate return. 2683 auto res = Fortran::lower::createSomeExtendedExpression( 2684 loc, converter, toEvExpr(call), symMap, stmtCtx); 2685 return fir::getBase(res); 2686 } 2687 2688 void Fortran::lower::createSomeArrayAssignment( 2689 Fortran::lower::AbstractConverter &converter, 2690 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 2691 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 2692 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n'; 2693 rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';); 2694 ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs); 2695 } 2696 2697 void Fortran::lower::createSomeArrayAssignment( 2698 Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs, 2699 const fir::ExtendedValue &rhs, Fortran::lower::SymMap &symMap, 2700 Fortran::lower::StatementContext &stmtCtx) { 2701 LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n'; 2702 llvm::dbgs() << "assign expression: " << rhs << '\n';); 2703 ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs); 2704 } 2705 2706 void Fortran::lower::createAllocatableArrayAssignment( 2707 Fortran::lower::AbstractConverter &converter, 2708 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs, 2709 Fortran::lower::ExplicitIterSpace &explicitSpace, 2710 Fortran::lower::ImplicitIterSpace &implicitSpace, 2711 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 2712 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining array: ") << '\n'; 2713 rhs.AsFortran(llvm::dbgs() << "assign expression: ") 2714 << " given the explicit iteration space:\n" 2715 << explicitSpace << "\n and implied mask conditions:\n" 2716 << implicitSpace << '\n';); 2717 ArrayExprLowering::lowerAllocatableArrayAssignment( 2718 converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace); 2719 } 2720