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