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/ConvertType.h" 19 #include "flang/Lower/ConvertVariable.h" 20 #include "flang/Lower/IntrinsicCall.h" 21 #include "flang/Lower/StatementContext.h" 22 #include "flang/Lower/SymbolMap.h" 23 #include "flang/Lower/Todo.h" 24 #include "flang/Optimizer/Builder/Complex.h" 25 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 26 #include "flang/Semantics/expression.h" 27 #include "flang/Semantics/symbol.h" 28 #include "flang/Semantics/tools.h" 29 #include "flang/Semantics/type.h" 30 #include "mlir/Dialect/StandardOps/IR/Ops.h" 31 #include "llvm/Support/Debug.h" 32 33 #define DEBUG_TYPE "flang-lower-expr" 34 35 //===----------------------------------------------------------------------===// 36 // The composition and structure of Fortran::evaluate::Expr is defined in 37 // the various header files in include/flang/Evaluate. You are referred 38 // there for more information on these data structures. Generally speaking, 39 // these data structures are a strongly typed family of abstract data types 40 // that, composed as trees, describe the syntax of Fortran expressions. 41 // 42 // This part of the bridge can traverse these tree structures and lower them 43 // to the correct FIR representation in SSA form. 44 //===----------------------------------------------------------------------===// 45 46 /// Place \p exv in memory if it is not already a memory reference. If 47 /// \p forceValueType is provided, the value is first casted to the provided 48 /// type before being stored (this is mainly intended for logicals whose value 49 /// may be `i1` but needed to be stored as Fortran logicals). 50 static fir::ExtendedValue 51 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc, 52 const fir::ExtendedValue &exv, 53 mlir::Type storageType) { 54 mlir::Value valBase = fir::getBase(exv); 55 if (fir::conformsWithPassByRef(valBase.getType())) 56 return exv; 57 58 assert(!fir::hasDynamicSize(storageType) && 59 "only expect statically sized scalars to be by value"); 60 61 // Since `a` is not itself a valid referent, determine its value and 62 // create a temporary location at the beginning of the function for 63 // referencing. 64 mlir::Value val = builder.createConvert(loc, storageType, valBase); 65 mlir::Value temp = builder.createTemporary( 66 loc, storageType, 67 llvm::ArrayRef<mlir::NamedAttribute>{ 68 Fortran::lower::getAdaptToByRefAttr(builder)}); 69 builder.create<fir::StoreOp>(loc, val, temp); 70 return fir::substBase(exv, temp); 71 } 72 73 /// Is this a variable wrapped in parentheses? 74 template <typename A> 75 static bool isParenthesizedVariable(const A &) { 76 return false; 77 } 78 template <typename T> 79 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) { 80 using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u); 81 using Parentheses = Fortran::evaluate::Parentheses<T>; 82 if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) { 83 if (const auto *parentheses = std::get_if<Parentheses>(&expr.u)) 84 return Fortran::evaluate::IsVariable(parentheses->left()); 85 return false; 86 } else { 87 return std::visit([&](const auto &x) { return isParenthesizedVariable(x); }, 88 expr.u); 89 } 90 } 91 92 /// Generate a load of a value from an address. Beware that this will lose 93 /// any dynamic type information for polymorphic entities (note that unlimited 94 /// polymorphic cannot be loaded and must not be provided here). 95 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder, 96 mlir::Location loc, 97 const fir::ExtendedValue &addr) { 98 return addr.match( 99 [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; }, 100 [&](const fir::UnboxedValue &v) -> fir::ExtendedValue { 101 if (fir::unwrapRefType(fir::getBase(v).getType()) 102 .isa<fir::RecordType>()) 103 return v; 104 return builder.create<fir::LoadOp>(loc, fir::getBase(v)); 105 }, 106 [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue { 107 TODO(loc, "genLoad for MutableBoxValue"); 108 }, 109 [&](const fir::BoxValue &box) -> fir::ExtendedValue { 110 TODO(loc, "genLoad for BoxValue"); 111 }, 112 [&](const auto &) -> fir::ExtendedValue { 113 fir::emitFatalError( 114 loc, "attempting to load whole array or procedure address"); 115 }); 116 } 117 118 /// Is this a call to an elemental procedure with at least one array argument? 119 static bool 120 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) { 121 if (procRef.IsElemental()) 122 for (const std::optional<Fortran::evaluate::ActualArgument> &arg : 123 procRef.arguments()) 124 if (arg && arg->Rank() != 0) 125 return true; 126 return false; 127 } 128 129 /// If \p arg is the address of a function with a denoted host-association tuple 130 /// argument, then return the host-associations tuple value of the current 131 /// procedure. Otherwise, return nullptr. 132 static mlir::Value 133 argumentHostAssocs(Fortran::lower::AbstractConverter &converter, 134 mlir::Value arg) { 135 if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) { 136 auto &builder = converter.getFirOpBuilder(); 137 if (auto funcOp = builder.getNamedFunction(addr.getSymbol())) 138 if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName())) 139 return converter.hostAssocTupleValue(); 140 } 141 return {}; 142 } 143 144 namespace { 145 146 /// Lowering of Fortran::evaluate::Expr<T> expressions 147 class ScalarExprLowering { 148 public: 149 using ExtValue = fir::ExtendedValue; 150 151 explicit ScalarExprLowering(mlir::Location loc, 152 Fortran::lower::AbstractConverter &converter, 153 Fortran::lower::SymMap &symMap, 154 Fortran::lower::StatementContext &stmtCtx) 155 : location{loc}, converter{converter}, 156 builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap} { 157 } 158 159 ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) { 160 return gen(expr); 161 } 162 163 /// Lower `expr` to be passed as a fir.box argument. Do not create a temp 164 /// for the expr if it is a variable that can be described as a fir.box. 165 ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) { 166 bool saveUseBoxArg = useBoxArg; 167 useBoxArg = true; 168 ExtValue result = gen(expr); 169 useBoxArg = saveUseBoxArg; 170 return result; 171 } 172 173 ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) { 174 return genval(expr); 175 } 176 177 mlir::Location getLoc() { return location; } 178 179 template <typename A> 180 mlir::Value genunbox(const A &expr) { 181 ExtValue e = genval(expr); 182 if (const fir::UnboxedValue *r = e.getUnboxed()) 183 return *r; 184 fir::emitFatalError(getLoc(), "unboxed expression expected"); 185 } 186 187 /// Generate an integral constant of `value` 188 template <int KIND> 189 mlir::Value genIntegerConstant(mlir::MLIRContext *context, 190 std::int64_t value) { 191 mlir::Type type = 192 converter.genType(Fortran::common::TypeCategory::Integer, KIND); 193 return builder.createIntegerConstant(getLoc(), type, value); 194 } 195 196 /// Generate a logical/boolean constant of `value` 197 mlir::Value genBoolConstant(bool value) { 198 return builder.createBool(getLoc(), value); 199 } 200 201 /// Generate a real constant with a value `value`. 202 template <int KIND> 203 mlir::Value genRealConstant(mlir::MLIRContext *context, 204 const llvm::APFloat &value) { 205 mlir::Type fltTy = Fortran::lower::convertReal(context, KIND); 206 return builder.createRealConstant(getLoc(), fltTy, value); 207 } 208 209 /// Returns a reference to a symbol or its box/boxChar descriptor if it has 210 /// one. 211 ExtValue gen(Fortran::semantics::SymbolRef sym) { 212 if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym)) 213 return val.match([&val](auto &) { return val.toExtendedValue(); }); 214 LLVM_DEBUG(llvm::dbgs() 215 << "unknown symbol: " << sym << "\nmap: " << symMap << '\n'); 216 fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value"); 217 } 218 219 ExtValue genLoad(const ExtValue &exv) { 220 return ::genLoad(builder, getLoc(), exv); 221 } 222 223 ExtValue genval(Fortran::semantics::SymbolRef sym) { 224 ExtValue var = gen(sym); 225 if (const fir::UnboxedValue *s = var.getUnboxed()) 226 if (fir::isReferenceLike(s->getType())) 227 return genLoad(*s); 228 return var; 229 } 230 231 ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) { 232 TODO(getLoc(), "genval BOZ"); 233 } 234 235 /// Return indirection to function designated in ProcedureDesignator. 236 /// The type of the function indirection is not guaranteed to match the one 237 /// of the ProcedureDesignator due to Fortran implicit typing rules. 238 ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) { 239 TODO(getLoc(), "genval ProcedureDesignator"); 240 } 241 242 ExtValue genval(const Fortran::evaluate::NullPointer &) { 243 TODO(getLoc(), "genval NullPointer"); 244 } 245 246 ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) { 247 TODO(getLoc(), "genval StructureConstructor"); 248 } 249 250 /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol. 251 ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) { 252 TODO(getLoc(), "genval ImpliedDoIndex"); 253 } 254 255 ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) { 256 TODO(getLoc(), "genval DescriptorInquiry"); 257 } 258 259 ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) { 260 TODO(getLoc(), "genval TypeParamInquiry"); 261 } 262 263 template <int KIND> 264 ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) { 265 TODO(getLoc(), "genval ComplexComponent"); 266 } 267 268 template <int KIND> 269 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 270 Fortran::common::TypeCategory::Integer, KIND>> &op) { 271 mlir::Value input = genunbox(op.left()); 272 // Like LLVM, integer negation is the binary op "0 - value" 273 mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0); 274 return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input); 275 } 276 277 template <int KIND> 278 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 279 Fortran::common::TypeCategory::Real, KIND>> &op) { 280 return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left())); 281 } 282 template <int KIND> 283 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 284 Fortran::common::TypeCategory::Complex, KIND>> &op) { 285 return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left())); 286 } 287 288 template <typename OpTy> 289 mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) { 290 assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right)); 291 mlir::Value lhs = fir::getBase(left); 292 mlir::Value rhs = fir::getBase(right); 293 assert(lhs.getType() == rhs.getType() && "types must be the same"); 294 return builder.create<OpTy>(getLoc(), lhs, rhs); 295 } 296 297 template <typename OpTy, typename A> 298 mlir::Value createBinaryOp(const A &ex) { 299 ExtValue left = genval(ex.left()); 300 return createBinaryOp<OpTy>(left, genval(ex.right())); 301 } 302 303 #undef GENBIN 304 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \ 305 template <int KIND> \ 306 ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \ 307 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \ 308 return createBinaryOp<GenBinFirOp>(x); \ 309 } 310 311 GENBIN(Add, Integer, mlir::arith::AddIOp) 312 GENBIN(Add, Real, mlir::arith::AddFOp) 313 GENBIN(Add, Complex, fir::AddcOp) 314 GENBIN(Subtract, Integer, mlir::arith::SubIOp) 315 GENBIN(Subtract, Real, mlir::arith::SubFOp) 316 GENBIN(Subtract, Complex, fir::SubcOp) 317 GENBIN(Multiply, Integer, mlir::arith::MulIOp) 318 GENBIN(Multiply, Real, mlir::arith::MulFOp) 319 GENBIN(Multiply, Complex, fir::MulcOp) 320 GENBIN(Divide, Integer, mlir::arith::DivSIOp) 321 GENBIN(Divide, Real, mlir::arith::DivFOp) 322 GENBIN(Divide, Complex, fir::DivcOp) 323 324 template <Fortran::common::TypeCategory TC, int KIND> 325 ExtValue genval( 326 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) { 327 TODO(getLoc(), "genval Power"); 328 } 329 330 template <Fortran::common::TypeCategory TC, int KIND> 331 ExtValue genval( 332 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>> 333 &op) { 334 TODO(getLoc(), "genval RealToInt"); 335 } 336 337 template <int KIND> 338 ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) { 339 mlir::Value realPartValue = genunbox(op.left()); 340 return fir::factory::Complex{builder, getLoc()}.createComplex( 341 KIND, realPartValue, genunbox(op.right())); 342 } 343 344 template <int KIND> 345 ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) { 346 TODO(getLoc(), "genval Concat<KIND>"); 347 } 348 349 /// MIN and MAX operations 350 template <Fortran::common::TypeCategory TC, int KIND> 351 ExtValue 352 genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> 353 &op) { 354 TODO(getLoc(), "genval Extremum<TC, KIND>"); 355 } 356 357 template <int KIND> 358 ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) { 359 TODO(getLoc(), "genval SetLength<KIND>"); 360 } 361 362 template <int KIND> 363 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 364 Fortran::common::TypeCategory::Integer, KIND>> &op) { 365 TODO(getLoc(), "genval integer comparison"); 366 } 367 template <int KIND> 368 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 369 Fortran::common::TypeCategory::Real, KIND>> &op) { 370 TODO(getLoc(), "genval real comparison"); 371 } 372 template <int KIND> 373 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 374 Fortran::common::TypeCategory::Complex, KIND>> &op) { 375 TODO(getLoc(), "genval complex comparison"); 376 } 377 template <int KIND> 378 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 379 Fortran::common::TypeCategory::Character, KIND>> &op) { 380 TODO(getLoc(), "genval char comparison"); 381 } 382 383 ExtValue 384 genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) { 385 TODO(getLoc(), "genval comparison"); 386 } 387 388 template <Fortran::common::TypeCategory TC1, int KIND, 389 Fortran::common::TypeCategory TC2> 390 ExtValue 391 genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, 392 TC2> &convert) { 393 mlir::Type ty = converter.genType(TC1, KIND); 394 mlir::Value operand = genunbox(convert.left()); 395 return builder.convertWithSemantics(getLoc(), ty, operand); 396 } 397 398 template <typename A> 399 ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) { 400 TODO(getLoc(), "genval parentheses<A>"); 401 } 402 403 template <int KIND> 404 ExtValue genval(const Fortran::evaluate::Not<KIND> &op) { 405 TODO(getLoc(), "genval Not<KIND>"); 406 } 407 408 template <int KIND> 409 ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) { 410 TODO(getLoc(), "genval LogicalOperation<KIND>"); 411 } 412 413 /// Convert a scalar literal constant to IR. 414 template <Fortran::common::TypeCategory TC, int KIND> 415 ExtValue genScalarLit( 416 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> 417 &value) { 418 if constexpr (TC == Fortran::common::TypeCategory::Integer) { 419 return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64()); 420 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) { 421 return genBoolConstant(value.IsTrue()); 422 } else if constexpr (TC == Fortran::common::TypeCategory::Real) { 423 std::string str = value.DumpHexadecimal(); 424 if constexpr (KIND == 2) { 425 llvm::APFloat floatVal{llvm::APFloatBase::IEEEhalf(), str}; 426 return genRealConstant<KIND>(builder.getContext(), floatVal); 427 } else if constexpr (KIND == 3) { 428 llvm::APFloat floatVal{llvm::APFloatBase::BFloat(), str}; 429 return genRealConstant<KIND>(builder.getContext(), floatVal); 430 } else if constexpr (KIND == 4) { 431 llvm::APFloat floatVal{llvm::APFloatBase::IEEEsingle(), str}; 432 return genRealConstant<KIND>(builder.getContext(), floatVal); 433 } else if constexpr (KIND == 10) { 434 llvm::APFloat floatVal{llvm::APFloatBase::x87DoubleExtended(), str}; 435 return genRealConstant<KIND>(builder.getContext(), floatVal); 436 } else if constexpr (KIND == 16) { 437 llvm::APFloat floatVal{llvm::APFloatBase::IEEEquad(), str}; 438 return genRealConstant<KIND>(builder.getContext(), floatVal); 439 } else { 440 // convert everything else to double 441 llvm::APFloat floatVal{llvm::APFloatBase::IEEEdouble(), str}; 442 return genRealConstant<KIND>(builder.getContext(), floatVal); 443 } 444 } else if constexpr (TC == Fortran::common::TypeCategory::Complex) { 445 using TR = 446 Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>; 447 Fortran::evaluate::ComplexConstructor<KIND> ctor( 448 Fortran::evaluate::Expr<TR>{ 449 Fortran::evaluate::Constant<TR>{value.REAL()}}, 450 Fortran::evaluate::Expr<TR>{ 451 Fortran::evaluate::Constant<TR>{value.AIMAG()}}); 452 return genunbox(ctor); 453 } else /*constexpr*/ { 454 llvm_unreachable("unhandled constant"); 455 } 456 } 457 458 /// Convert a ascii scalar literal CHARACTER to IR. (specialization) 459 ExtValue 460 genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type< 461 Fortran::common::TypeCategory::Character, 1>> &value, 462 int64_t len) { 463 assert(value.size() == static_cast<std::uint64_t>(len) && 464 "value.size() doesn't match with len"); 465 return fir::factory::createStringLiteral(builder, getLoc(), value); 466 } 467 468 template <Fortran::common::TypeCategory TC, int KIND> 469 ExtValue 470 genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>> 471 &con) { 472 if (con.Rank() > 0) 473 TODO(getLoc(), "genval array constant"); 474 std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>> 475 opt = con.GetScalarValue(); 476 assert(opt.has_value() && "constant has no value"); 477 if constexpr (TC == Fortran::common::TypeCategory::Character) { 478 if constexpr (KIND == 1) 479 return genAsciiScalarLit(opt.value(), con.LEN()); 480 TODO(getLoc(), "genval for Character with KIND != 1"); 481 } else { 482 return genScalarLit<TC, KIND>(opt.value()); 483 } 484 } 485 486 fir::ExtendedValue genval( 487 const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) { 488 TODO(getLoc(), "genval constant derived"); 489 } 490 491 template <typename A> 492 ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) { 493 TODO(getLoc(), "genval ArrayConstructor<A>"); 494 } 495 496 ExtValue gen(const Fortran::evaluate::ComplexPart &x) { 497 TODO(getLoc(), "gen ComplexPart"); 498 } 499 ExtValue genval(const Fortran::evaluate::ComplexPart &x) { 500 TODO(getLoc(), "genval ComplexPart"); 501 } 502 503 ExtValue gen(const Fortran::evaluate::Substring &s) { 504 TODO(getLoc(), "gen Substring"); 505 } 506 ExtValue genval(const Fortran::evaluate::Substring &ss) { 507 TODO(getLoc(), "genval Substring"); 508 } 509 510 ExtValue genval(const Fortran::evaluate::Subscript &subs) { 511 TODO(getLoc(), "genval Subscript"); 512 } 513 514 ExtValue gen(const Fortran::evaluate::DataRef &dref) { 515 TODO(getLoc(), "gen DataRef"); 516 } 517 ExtValue genval(const Fortran::evaluate::DataRef &dref) { 518 TODO(getLoc(), "genval DataRef"); 519 } 520 521 ExtValue gen(const Fortran::evaluate::Component &cmpt) { 522 TODO(getLoc(), "gen Component"); 523 } 524 ExtValue genval(const Fortran::evaluate::Component &cmpt) { 525 TODO(getLoc(), "genval Component"); 526 } 527 528 ExtValue genval(const Fortran::semantics::Bound &bound) { 529 TODO(getLoc(), "genval Bound"); 530 } 531 532 ExtValue gen(const Fortran::evaluate::ArrayRef &aref) { 533 TODO(getLoc(), "gen ArrayRef"); 534 } 535 ExtValue genval(const Fortran::evaluate::ArrayRef &aref) { 536 TODO(getLoc(), "genval ArrayRef"); 537 } 538 539 ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) { 540 TODO(getLoc(), "gen CoarrayRef"); 541 } 542 ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) { 543 TODO(getLoc(), "genval CoarrayRef"); 544 } 545 546 template <typename A> 547 ExtValue gen(const Fortran::evaluate::Designator<A> &des) { 548 return std::visit([&](const auto &x) { return gen(x); }, des.u); 549 } 550 template <typename A> 551 ExtValue genval(const Fortran::evaluate::Designator<A> &des) { 552 return std::visit([&](const auto &x) { return genval(x); }, des.u); 553 } 554 555 mlir::Type genType(const Fortran::evaluate::DynamicType &dt) { 556 if (dt.category() != Fortran::common::TypeCategory::Derived) 557 return converter.genType(dt.category(), dt.kind()); 558 TODO(getLoc(), "genType Derived Type"); 559 } 560 561 /// Lower a function reference 562 template <typename A> 563 ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) { 564 if (!funcRef.GetType().has_value()) 565 fir::emitFatalError(getLoc(), "internal: a function must have a type"); 566 mlir::Type resTy = genType(*funcRef.GetType()); 567 return genProcedureRef(funcRef, {resTy}); 568 } 569 570 /// Lower function call `funcRef` and return a reference to the resultant 571 /// value. This is required for lowering expressions such as `f1(f2(v))`. 572 template <typename A> 573 ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) { 574 TODO(getLoc(), "gen FunctionRef<A>"); 575 } 576 577 /// helper to detect statement functions 578 static bool 579 isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) { 580 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol()) 581 if (const auto *details = 582 symbol->detailsIf<Fortran::semantics::SubprogramDetails>()) 583 return details->stmtFunction().has_value(); 584 return false; 585 } 586 587 /// Helper to package a Value and its properties into an ExtendedValue. 588 static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base, 589 llvm::ArrayRef<mlir::Value> extents, 590 llvm::ArrayRef<mlir::Value> lengths) { 591 mlir::Type type = base.getType(); 592 if (type.isa<fir::BoxType>()) 593 return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents); 594 type = fir::unwrapRefType(type); 595 if (type.isa<fir::BoxType>()) 596 return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {}); 597 if (auto seqTy = type.dyn_cast<fir::SequenceType>()) { 598 if (seqTy.getDimension() != extents.size()) 599 fir::emitFatalError(loc, "incorrect number of extents for array"); 600 if (seqTy.getEleTy().isa<fir::CharacterType>()) { 601 if (lengths.empty()) 602 fir::emitFatalError(loc, "missing length for character"); 603 assert(lengths.size() == 1); 604 return fir::CharArrayBoxValue(base, lengths[0], extents); 605 } 606 return fir::ArrayBoxValue(base, extents); 607 } 608 if (type.isa<fir::CharacterType>()) { 609 if (lengths.empty()) 610 fir::emitFatalError(loc, "missing length for character"); 611 assert(lengths.size() == 1); 612 return fir::CharBoxValue(base, lengths[0]); 613 } 614 return base; 615 } 616 617 // Find the argument that corresponds to the host associations. 618 // Verify some assumptions about how the signature was built here. 619 [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::FuncOp fn) { 620 // Scan the argument list from last to first as the host associations are 621 // appended for now. 622 for (unsigned i = fn.getNumArguments(); i > 0; --i) 623 if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) { 624 // Host assoc tuple must be last argument (for now). 625 assert(i == fn.getNumArguments() && "tuple must be last"); 626 return i - 1; 627 } 628 llvm_unreachable("anyFuncArgsHaveAttr failed"); 629 } 630 631 /// Lower a non-elemental procedure reference and read allocatable and pointer 632 /// results into normal values. 633 ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef, 634 llvm::Optional<mlir::Type> resultType) { 635 ExtValue res = genRawProcedureRef(procRef, resultType); 636 return res; 637 } 638 639 /// Given a call site for which the arguments were already lowered, generate 640 /// the call and return the result. This function deals with explicit result 641 /// allocation and lowering if needed. It also deals with passing the host 642 /// link to internal procedures. 643 ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller, 644 mlir::FunctionType callSiteType, 645 llvm::Optional<mlir::Type> resultType) { 646 mlir::Location loc = getLoc(); 647 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 648 // Handle cases where caller must allocate the result or a fir.box for it. 649 bool mustPopSymMap = false; 650 if (caller.mustMapInterfaceSymbols()) { 651 symMap.pushScope(); 652 mustPopSymMap = true; 653 Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap); 654 } 655 // If this is an indirect call, retrieve the function address. Also retrieve 656 // the result length if this is a character function (note that this length 657 // will be used only if there is no explicit length in the local interface). 658 mlir::Value funcPointer; 659 mlir::Value charFuncPointerLength; 660 if (caller.getIfIndirectCallSymbol()) { 661 TODO(loc, "genCallOpAndResult indirect call"); 662 } 663 664 mlir::IndexType idxTy = builder.getIndexType(); 665 auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value { 666 return builder.createConvert( 667 loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx))); 668 }; 669 llvm::SmallVector<mlir::Value> resultLengths; 670 auto allocatedResult = [&]() -> llvm::Optional<ExtValue> { 671 llvm::SmallVector<mlir::Value> extents; 672 llvm::SmallVector<mlir::Value> lengths; 673 if (!caller.callerAllocateResult()) 674 return {}; 675 mlir::Type type = caller.getResultStorageType(); 676 if (type.isa<fir::SequenceType>()) 677 caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) { 678 extents.emplace_back(lowerSpecExpr(e)); 679 }); 680 caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) { 681 lengths.emplace_back(lowerSpecExpr(e)); 682 }); 683 684 // Result length parameters should not be provided to box storage 685 // allocation and save_results, but they are still useful information to 686 // keep in the ExtendedValue if non-deferred. 687 if (!type.isa<fir::BoxType>()) { 688 if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) { 689 // Calling an assumed length function. This is only possible if this 690 // is a call to a character dummy procedure. 691 if (!charFuncPointerLength) 692 fir::emitFatalError(loc, "failed to retrieve character function " 693 "length while calling it"); 694 lengths.push_back(charFuncPointerLength); 695 } 696 resultLengths = lengths; 697 } 698 699 if (!extents.empty() || !lengths.empty()) { 700 TODO(loc, "genCallOpResult extents and length"); 701 } 702 mlir::Value temp = 703 builder.createTemporary(loc, type, ".result", extents, resultLengths); 704 return toExtendedValue(loc, temp, extents, lengths); 705 }(); 706 707 if (mustPopSymMap) 708 symMap.popScope(); 709 710 // Place allocated result or prepare the fir.save_result arguments. 711 mlir::Value arrayResultShape; 712 if (allocatedResult) { 713 if (std::optional<Fortran::lower::CallInterface< 714 Fortran::lower::CallerInterface>::PassedEntity> 715 resultArg = caller.getPassedResult()) { 716 if (resultArg->passBy == PassBy::AddressAndLength) 717 caller.placeAddressAndLengthInput(*resultArg, 718 fir::getBase(*allocatedResult), 719 fir::getLen(*allocatedResult)); 720 else if (resultArg->passBy == PassBy::BaseAddress) 721 caller.placeInput(*resultArg, fir::getBase(*allocatedResult)); 722 else 723 fir::emitFatalError( 724 loc, "only expect character scalar result to be passed by ref"); 725 } else { 726 assert(caller.mustSaveResult()); 727 arrayResultShape = allocatedResult->match( 728 [&](const fir::CharArrayBoxValue &) { 729 return builder.createShape(loc, *allocatedResult); 730 }, 731 [&](const fir::ArrayBoxValue &) { 732 return builder.createShape(loc, *allocatedResult); 733 }, 734 [&](const auto &) { return mlir::Value{}; }); 735 } 736 } 737 738 // In older Fortran, procedure argument types are inferred. This may lead 739 // different view of what the function signature is in different locations. 740 // Casts are inserted as needed below to accommodate this. 741 742 // The mlir::FuncOp type prevails, unless it has a different number of 743 // arguments which can happen in legal program if it was passed as a dummy 744 // procedure argument earlier with no further type information. 745 mlir::SymbolRefAttr funcSymbolAttr; 746 bool addHostAssociations = false; 747 if (!funcPointer) { 748 mlir::FunctionType funcOpType = caller.getFuncOp().getType(); 749 mlir::SymbolRefAttr symbolAttr = 750 builder.getSymbolRefAttr(caller.getMangledName()); 751 if (callSiteType.getNumResults() == funcOpType.getNumResults() && 752 callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() && 753 fir::anyFuncArgsHaveAttr(caller.getFuncOp(), 754 fir::getHostAssocAttrName())) { 755 // The number of arguments is off by one, and we're lowering a function 756 // with host associations. Modify call to include host associations 757 // argument by appending the value at the end of the operands. 758 assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) == 759 converter.hostAssocTupleValue().getType()); 760 addHostAssociations = true; 761 } 762 if (!addHostAssociations && 763 (callSiteType.getNumResults() != funcOpType.getNumResults() || 764 callSiteType.getNumInputs() != funcOpType.getNumInputs())) { 765 // Deal with argument number mismatch by making a function pointer so 766 // that function type cast can be inserted. Do not emit a warning here 767 // because this can happen in legal program if the function is not 768 // defined here and it was first passed as an argument without any more 769 // information. 770 funcPointer = 771 builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr); 772 } else if (callSiteType.getResults() != funcOpType.getResults()) { 773 // Implicit interface result type mismatch are not standard Fortran, but 774 // some compilers are not complaining about it. The front end is not 775 // protecting lowering from this currently. Support this with a 776 // discouraging warning. 777 LLVM_DEBUG(mlir::emitWarning( 778 loc, "a return type mismatch is not standard compliant and may " 779 "lead to undefined behavior.")); 780 // Cast the actual function to the current caller implicit type because 781 // that is the behavior we would get if we could not see the definition. 782 funcPointer = 783 builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr); 784 } else { 785 funcSymbolAttr = symbolAttr; 786 } 787 } 788 789 mlir::FunctionType funcType = 790 funcPointer ? callSiteType : caller.getFuncOp().getType(); 791 llvm::SmallVector<mlir::Value> operands; 792 // First operand of indirect call is the function pointer. Cast it to 793 // required function type for the call to handle procedures that have a 794 // compatible interface in Fortran, but that have different signatures in 795 // FIR. 796 if (funcPointer) { 797 operands.push_back( 798 funcPointer.getType().isa<fir::BoxProcType>() 799 ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer) 800 : builder.createConvert(loc, funcType, funcPointer)); 801 } 802 803 // Deal with potential mismatches in arguments types. Passing an array to a 804 // scalar argument should for instance be tolerated here. 805 bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface(); 806 for (auto [fst, snd] : 807 llvm::zip(caller.getInputs(), funcType.getInputs())) { 808 // When passing arguments to a procedure that can be called an implicit 809 // interface, allow character actual arguments to be passed to dummy 810 // arguments of any type and vice versa 811 mlir::Value cast; 812 auto *context = builder.getContext(); 813 if (snd.isa<fir::BoxProcType>() && 814 fst.getType().isa<mlir::FunctionType>()) { 815 auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None); 816 auto boxProcTy = builder.getBoxProcType(funcTy); 817 if (mlir::Value host = argumentHostAssocs(converter, fst)) { 818 cast = builder.create<fir::EmboxProcOp>( 819 loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host}); 820 } else { 821 cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst); 822 } 823 } else { 824 cast = builder.convertWithSemantics(loc, snd, fst, 825 callingImplicitInterface); 826 } 827 operands.push_back(cast); 828 } 829 830 // Add host associations as necessary. 831 if (addHostAssociations) 832 operands.push_back(converter.hostAssocTupleValue()); 833 834 auto call = builder.create<fir::CallOp>(loc, funcType.getResults(), 835 funcSymbolAttr, operands); 836 837 if (caller.mustSaveResult()) 838 builder.create<fir::SaveResultOp>( 839 loc, call.getResult(0), fir::getBase(allocatedResult.getValue()), 840 arrayResultShape, resultLengths); 841 842 if (allocatedResult) { 843 allocatedResult->match( 844 [&](const fir::MutableBoxValue &box) { 845 if (box.isAllocatable()) { 846 TODO(loc, "allocatedResult for allocatable"); 847 } 848 }, 849 [](const auto &) {}); 850 return *allocatedResult; 851 } 852 853 if (!resultType.hasValue()) 854 return mlir::Value{}; // subroutine call 855 // For now, Fortran return values are implemented with a single MLIR 856 // function return value. 857 assert(call.getNumResults() == 1 && 858 "Expected exactly one result in FUNCTION call"); 859 return call.getResult(0); 860 } 861 862 /// Like genExtAddr, but ensure the address returned is a temporary even if \p 863 /// expr is variable inside parentheses. 864 ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) { 865 // In general, genExtAddr might not create a temp for variable inside 866 // parentheses to avoid creating array temporary in sub-expressions. It only 867 // ensures the sub-expression is not re-associated with other parts of the 868 // expression. In the call semantics, there is a difference between expr and 869 // variable (see R1524). For expressions, a variable storage must not be 870 // argument associated since it could be modified inside the call, or the 871 // variable could also be modified by other means during the call. 872 if (!isParenthesizedVariable(expr)) 873 return genExtAddr(expr); 874 mlir::Location loc = getLoc(); 875 if (expr.Rank() > 0) 876 TODO(loc, "genTempExtAddr array"); 877 return genExtValue(expr).match( 878 [&](const fir::CharBoxValue &boxChar) -> ExtValue { 879 TODO(loc, "genTempExtAddr CharBoxValue"); 880 }, 881 [&](const fir::UnboxedValue &v) -> ExtValue { 882 mlir::Type type = v.getType(); 883 mlir::Value value = v; 884 if (fir::isa_ref_type(type)) 885 value = builder.create<fir::LoadOp>(loc, value); 886 mlir::Value temp = builder.createTemporary(loc, value.getType()); 887 builder.create<fir::StoreOp>(loc, value, temp); 888 return temp; 889 }, 890 [&](const fir::BoxValue &x) -> ExtValue { 891 // Derived type scalar that may be polymorphic. 892 assert(!x.hasRank() && x.isDerived()); 893 if (x.isDerivedWithLengthParameters()) 894 fir::emitFatalError( 895 loc, "making temps for derived type with length parameters"); 896 // TODO: polymorphic aspects should be kept but for now the temp 897 // created always has the declared type. 898 mlir::Value var = 899 fir::getBase(fir::factory::readBoxValue(builder, loc, x)); 900 auto value = builder.create<fir::LoadOp>(loc, var); 901 mlir::Value temp = builder.createTemporary(loc, value.getType()); 902 builder.create<fir::StoreOp>(loc, value, temp); 903 return temp; 904 }, 905 [&](const auto &) -> ExtValue { 906 fir::emitFatalError(loc, "expr is not a scalar value"); 907 }); 908 } 909 910 /// Helper structure to track potential copy-in of non contiguous variable 911 /// argument into a contiguous temp. It is used to deallocate the temp that 912 /// may have been created as well as to the copy-out from the temp to the 913 /// variable after the call. 914 struct CopyOutPair { 915 ExtValue var; 916 ExtValue temp; 917 // Flag to indicate if the argument may have been modified by the 918 // callee, in which case it must be copied-out to the variable. 919 bool argMayBeModifiedByCall; 920 // Optional boolean value that, if present and false, prevents 921 // the copy-out and temp deallocation. 922 llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime; 923 }; 924 using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>; 925 926 /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories 927 /// not based on fir.box. 928 /// This will lose any non contiguous stride information and dynamic type and 929 /// should only be called if \p exv is known to be contiguous or if its base 930 /// address will be replaced by a contiguous one. If \p exv is not a 931 /// fir::BoxValue, this is a no-op. 932 ExtValue readIfBoxValue(const ExtValue &exv) { 933 if (const auto *box = exv.getBoxOf<fir::BoxValue>()) 934 return fir::factory::readBoxValue(builder, getLoc(), *box); 935 return exv; 936 } 937 938 /// Lower a non-elemental procedure reference. 939 ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef, 940 llvm::Optional<mlir::Type> resultType) { 941 mlir::Location loc = getLoc(); 942 if (isElementalProcWithArrayArgs(procRef)) 943 fir::emitFatalError(loc, "trying to lower elemental procedure with array " 944 "arguments as normal procedure"); 945 if (const Fortran::evaluate::SpecificIntrinsic *intrinsic = 946 procRef.proc().GetSpecificIntrinsic()) 947 return genIntrinsicRef(procRef, *intrinsic, resultType); 948 949 if (isStatementFunctionCall(procRef)) 950 TODO(loc, "Lower statement function call"); 951 952 Fortran::lower::CallerInterface caller(procRef, converter); 953 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 954 955 llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall; 956 // List of <var, temp> where temp must be copied into var after the call. 957 CopyOutPairs copyOutPairs; 958 959 mlir::FunctionType callSiteType = caller.genFunctionType(); 960 961 // Lower the actual arguments and map the lowered values to the dummy 962 // arguments. 963 for (const Fortran::lower::CallInterface< 964 Fortran::lower::CallerInterface>::PassedEntity &arg : 965 caller.getPassedArguments()) { 966 const auto *actual = arg.entity; 967 mlir::Type argTy = callSiteType.getInput(arg.firArgument); 968 if (!actual) { 969 // Optional dummy argument for which there is no actual argument. 970 caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy)); 971 continue; 972 } 973 const auto *expr = actual->UnwrapExpr(); 974 if (!expr) 975 TODO(loc, "assumed type actual argument lowering"); 976 977 if (arg.passBy == PassBy::Value) { 978 ExtValue argVal = genval(*expr); 979 if (!fir::isUnboxedValue(argVal)) 980 fir::emitFatalError( 981 loc, "internal error: passing non trivial value by value"); 982 caller.placeInput(arg, fir::getBase(argVal)); 983 continue; 984 } 985 986 if (arg.passBy == PassBy::MutableBox) { 987 TODO(loc, "arg passby MutableBox"); 988 } 989 const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr); 990 if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) { 991 auto argAddr = [&]() -> ExtValue { 992 ExtValue baseAddr; 993 if (actualArgIsVariable && arg.isOptional()) { 994 if (Fortran::evaluate::IsAllocatableOrPointerObject( 995 *expr, converter.getFoldingContext())) { 996 TODO(loc, "Allocatable or pointer argument"); 997 } 998 if (const Fortran::semantics::Symbol *wholeSymbol = 999 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef( 1000 *expr)) 1001 if (Fortran::semantics::IsOptional(*wholeSymbol)) { 1002 TODO(loc, "procedureref optional arg"); 1003 } 1004 // Fall through: The actual argument can safely be 1005 // copied-in/copied-out without any care if needed. 1006 } 1007 if (actualArgIsVariable && expr->Rank() > 0) { 1008 TODO(loc, "procedureref arrays"); 1009 } 1010 // Actual argument is a non optional/non pointer/non allocatable 1011 // scalar. 1012 if (actualArgIsVariable) 1013 return genExtAddr(*expr); 1014 // Actual argument is not a variable. Make sure a variable address is 1015 // not passed. 1016 return genTempExtAddr(*expr); 1017 }(); 1018 // Scalar and contiguous expressions may be lowered to a fir.box, 1019 // either to account for potential polymorphism, or because lowering 1020 // did not account for some contiguity hints. 1021 // Here, polymorphism does not matter (an entity of the declared type 1022 // is passed, not one of the dynamic type), and the expr is known to 1023 // be simply contiguous, so it is safe to unbox it and pass the 1024 // address without making a copy. 1025 argAddr = readIfBoxValue(argAddr); 1026 1027 if (arg.passBy == PassBy::BaseAddress) { 1028 caller.placeInput(arg, fir::getBase(argAddr)); 1029 } else { 1030 TODO(loc, "procedureref PassBy::BoxChar"); 1031 } 1032 } else if (arg.passBy == PassBy::Box) { 1033 // Before lowering to an address, handle the allocatable/pointer actual 1034 // argument to optional fir.box dummy. It is legal to pass 1035 // unallocated/disassociated entity to an optional. In this case, an 1036 // absent fir.box must be created instead of a fir.box with a null value 1037 // (Fortran 2018 15.5.2.12 point 1). 1038 if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject( 1039 *expr, converter.getFoldingContext())) { 1040 TODO(loc, "optional allocatable or pointer argument"); 1041 } else { 1042 // Make sure a variable address is only passed if the expression is 1043 // actually a variable. 1044 mlir::Value box = 1045 actualArgIsVariable 1046 ? builder.createBox(loc, genBoxArg(*expr)) 1047 : builder.createBox(getLoc(), genTempExtAddr(*expr)); 1048 caller.placeInput(arg, box); 1049 } 1050 } else if (arg.passBy == PassBy::AddressAndLength) { 1051 ExtValue argRef = genExtAddr(*expr); 1052 caller.placeAddressAndLengthInput(arg, fir::getBase(argRef), 1053 fir::getLen(argRef)); 1054 } else if (arg.passBy == PassBy::CharProcTuple) { 1055 TODO(loc, "procedureref CharProcTuple"); 1056 } else { 1057 TODO(loc, "pass by value in non elemental function call"); 1058 } 1059 } 1060 1061 ExtValue result = genCallOpAndResult(caller, callSiteType, resultType); 1062 1063 // // Copy-out temps that were created for non contiguous variable arguments 1064 // if 1065 // // needed. 1066 // for (const auto ©OutPair : copyOutPairs) 1067 // genCopyOut(copyOutPair); 1068 1069 return result; 1070 } 1071 1072 template <typename A> 1073 ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) { 1074 ExtValue result = genFunctionRef(funcRef); 1075 if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType())) 1076 return genLoad(result); 1077 return result; 1078 } 1079 1080 ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) { 1081 llvm::Optional<mlir::Type> resTy; 1082 if (procRef.hasAlternateReturns()) 1083 resTy = builder.getIndexType(); 1084 return genProcedureRef(procRef, resTy); 1085 } 1086 1087 /// Generate a call to an intrinsic function. 1088 ExtValue 1089 genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef, 1090 const Fortran::evaluate::SpecificIntrinsic &intrinsic, 1091 llvm::Optional<mlir::Type> resultType) { 1092 llvm::SmallVector<ExtValue> operands; 1093 1094 llvm::StringRef name = intrinsic.name; 1095 mlir::Location loc = getLoc(); 1096 1097 const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering = 1098 Fortran::lower::getIntrinsicArgumentLowering(name); 1099 for (const auto &[arg, dummy] : 1100 llvm::zip(procRef.arguments(), 1101 intrinsic.characteristics.value().dummyArguments)) { 1102 auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg); 1103 if (!expr) { 1104 // Absent optional. 1105 operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument()); 1106 continue; 1107 } 1108 if (!argLowering) { 1109 // No argument lowering instruction, lower by value. 1110 operands.emplace_back(genval(*expr)); 1111 continue; 1112 } 1113 // Ad-hoc argument lowering handling. 1114 Fortran::lower::ArgLoweringRule argRules = 1115 Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering, 1116 dummy.name); 1117 switch (argRules.lowerAs) { 1118 case Fortran::lower::LowerIntrinsicArgAs::Value: 1119 operands.emplace_back(genval(*expr)); 1120 continue; 1121 case Fortran::lower::LowerIntrinsicArgAs::Addr: 1122 TODO(getLoc(), "argument lowering for Addr"); 1123 continue; 1124 case Fortran::lower::LowerIntrinsicArgAs::Box: 1125 TODO(getLoc(), "argument lowering for Box"); 1126 continue; 1127 case Fortran::lower::LowerIntrinsicArgAs::Inquired: 1128 TODO(getLoc(), "argument lowering for Inquired"); 1129 continue; 1130 } 1131 llvm_unreachable("bad switch"); 1132 } 1133 // Let the intrinsic library lower the intrinsic procedure call 1134 return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType, 1135 operands); 1136 } 1137 1138 template <typename A> 1139 ExtValue genval(const Fortran::evaluate::Expr<A> &x) { 1140 if (isScalar(x)) 1141 return std::visit([&](const auto &e) { return genval(e); }, x.u); 1142 TODO(getLoc(), "genval Expr<A> arrays"); 1143 } 1144 1145 /// Helper to detect Transformational function reference. 1146 template <typename T> 1147 bool isTransformationalRef(const T &) { 1148 return false; 1149 } 1150 template <typename T> 1151 bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) { 1152 return !funcRef.IsElemental() && funcRef.Rank(); 1153 } 1154 template <typename T> 1155 bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) { 1156 return std::visit([&](const auto &e) { return isTransformationalRef(e); }, 1157 expr.u); 1158 } 1159 1160 template <typename A> 1161 ExtValue gen(const Fortran::evaluate::Expr<A> &x) { 1162 // Whole array symbols or components, and results of transformational 1163 // functions already have a storage and the scalar expression lowering path 1164 // is used to not create a new temporary storage. 1165 if (isScalar(x) || 1166 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) || 1167 isTransformationalRef(x)) 1168 return std::visit([&](const auto &e) { return genref(e); }, x.u); 1169 TODO(getLoc(), "gen Expr non-scalar"); 1170 } 1171 1172 template <typename A> 1173 bool isScalar(const A &x) { 1174 return x.Rank() == 0; 1175 } 1176 1177 template <int KIND> 1178 ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type< 1179 Fortran::common::TypeCategory::Logical, KIND>> &exp) { 1180 return std::visit([&](const auto &e) { return genval(e); }, exp.u); 1181 } 1182 1183 using RefSet = 1184 std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring, 1185 Fortran::evaluate::DataRef, Fortran::evaluate::Component, 1186 Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef, 1187 Fortran::semantics::SymbolRef>; 1188 template <typename A> 1189 static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>; 1190 1191 template <typename A, typename = std::enable_if_t<inRefSet<A>>> 1192 ExtValue genref(const A &a) { 1193 return gen(a); 1194 } 1195 template <typename A> 1196 ExtValue genref(const A &a) { 1197 mlir::Type storageType = converter.genType(toEvExpr(a)); 1198 return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType); 1199 } 1200 1201 template <typename A, template <typename> typename T, 1202 typename B = std::decay_t<T<A>>, 1203 std::enable_if_t< 1204 std::is_same_v<B, Fortran::evaluate::Expr<A>> || 1205 std::is_same_v<B, Fortran::evaluate::Designator<A>> || 1206 std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>, 1207 bool> = true> 1208 ExtValue genref(const T<A> &x) { 1209 return gen(x); 1210 } 1211 1212 private: 1213 mlir::Location location; 1214 Fortran::lower::AbstractConverter &converter; 1215 fir::FirOpBuilder &builder; 1216 Fortran::lower::StatementContext &stmtCtx; 1217 Fortran::lower::SymMap &symMap; 1218 bool useBoxArg = false; // expression lowered as argument 1219 }; 1220 } // namespace 1221 1222 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression( 1223 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 1224 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 1225 Fortran::lower::StatementContext &stmtCtx) { 1226 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n'); 1227 return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr); 1228 } 1229 1230 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress( 1231 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 1232 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, 1233 Fortran::lower::StatementContext &stmtCtx) { 1234 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n'); 1235 return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr); 1236 } 1237 1238 mlir::Value Fortran::lower::createSubroutineCall( 1239 AbstractConverter &converter, const evaluate::ProcedureRef &call, 1240 SymMap &symMap, StatementContext &stmtCtx) { 1241 mlir::Location loc = converter.getCurrentLocation(); 1242 1243 // Simple subroutine call, with potential alternate return. 1244 auto res = Fortran::lower::createSomeExtendedExpression( 1245 loc, converter, toEvExpr(call), symMap, stmtCtx); 1246 return fir::getBase(res); 1247 } 1248