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