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