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