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/real.h" 16 #include "flang/Evaluate/traverse.h" 17 #include "flang/Lower/AbstractConverter.h" 18 #include "flang/Lower/SymbolMap.h" 19 #include "flang/Lower/Todo.h" 20 #include "flang/Semantics/expression.h" 21 #include "flang/Semantics/symbol.h" 22 #include "flang/Semantics/tools.h" 23 #include "flang/Semantics/type.h" 24 #include "mlir/Dialect/StandardOps/IR/Ops.h" 25 #include "llvm/Support/Debug.h" 26 27 #define DEBUG_TYPE "flang-lower-expr" 28 29 //===----------------------------------------------------------------------===// 30 // The composition and structure of Fortran::evaluate::Expr is defined in 31 // the various header files in include/flang/Evaluate. You are referred 32 // there for more information on these data structures. Generally speaking, 33 // these data structures are a strongly typed family of abstract data types 34 // that, composed as trees, describe the syntax of Fortran expressions. 35 // 36 // This part of the bridge can traverse these tree structures and lower them 37 // to the correct FIR representation in SSA form. 38 //===----------------------------------------------------------------------===// 39 40 /// Place \p exv in memory if it is not already a memory reference. If 41 /// \p forceValueType is provided, the value is first casted to the provided 42 /// type before being stored (this is mainly intended for logicals whose value 43 /// may be `i1` but needed to be stored as Fortran logicals). 44 static fir::ExtendedValue 45 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc, 46 const fir::ExtendedValue &exv, 47 mlir::Type storageType) { 48 mlir::Value valBase = fir::getBase(exv); 49 if (fir::conformsWithPassByRef(valBase.getType())) 50 return exv; 51 52 assert(!fir::hasDynamicSize(storageType) && 53 "only expect statically sized scalars to be by value"); 54 55 // Since `a` is not itself a valid referent, determine its value and 56 // create a temporary location at the beginning of the function for 57 // referencing. 58 mlir::Value val = builder.createConvert(loc, storageType, valBase); 59 mlir::Value temp = builder.createTemporary( 60 loc, storageType, 61 llvm::ArrayRef<mlir::NamedAttribute>{ 62 Fortran::lower::getAdaptToByRefAttr(builder)}); 63 builder.create<fir::StoreOp>(loc, val, temp); 64 return fir::substBase(exv, temp); 65 } 66 67 /// Generate a load of a value from an address. Beware that this will lose 68 /// any dynamic type information for polymorphic entities (note that unlimited 69 /// polymorphic cannot be loaded and must not be provided here). 70 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder, 71 mlir::Location loc, 72 const fir::ExtendedValue &addr) { 73 return addr.match( 74 [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; }, 75 [&](const fir::UnboxedValue &v) -> fir::ExtendedValue { 76 if (fir::unwrapRefType(fir::getBase(v).getType()) 77 .isa<fir::RecordType>()) 78 return v; 79 return builder.create<fir::LoadOp>(loc, fir::getBase(v)); 80 }, 81 [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue { 82 TODO(loc, "genLoad for MutableBoxValue"); 83 }, 84 [&](const fir::BoxValue &box) -> fir::ExtendedValue { 85 TODO(loc, "genLoad for BoxValue"); 86 }, 87 [&](const auto &) -> fir::ExtendedValue { 88 fir::emitFatalError( 89 loc, "attempting to load whole array or procedure address"); 90 }); 91 } 92 93 namespace { 94 95 /// Lowering of Fortran::evaluate::Expr<T> expressions 96 class ScalarExprLowering { 97 public: 98 using ExtValue = fir::ExtendedValue; 99 100 explicit ScalarExprLowering(mlir::Location loc, 101 Fortran::lower::AbstractConverter &converter, 102 Fortran::lower::SymMap &symMap) 103 : location{loc}, converter{converter}, 104 builder{converter.getFirOpBuilder()}, symMap{symMap} {} 105 106 mlir::Location getLoc() { return location; } 107 108 template <typename A> 109 mlir::Value genunbox(const A &expr) { 110 ExtValue e = genval(expr); 111 if (const fir::UnboxedValue *r = e.getUnboxed()) 112 return *r; 113 fir::emitFatalError(getLoc(), "unboxed expression expected"); 114 } 115 116 /// Generate an integral constant of `value` 117 template <int KIND> 118 mlir::Value genIntegerConstant(mlir::MLIRContext *context, 119 std::int64_t value) { 120 mlir::Type type = 121 converter.genType(Fortran::common::TypeCategory::Integer, KIND); 122 return builder.createIntegerConstant(getLoc(), type, value); 123 } 124 125 /// Generate a logical/boolean constant of `value` 126 mlir::Value genBoolConstant(bool value) { 127 return builder.createBool(getLoc(), value); 128 } 129 130 /// Returns a reference to a symbol or its box/boxChar descriptor if it has 131 /// one. 132 ExtValue gen(Fortran::semantics::SymbolRef sym) { 133 if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym)) 134 return val.match([&val](auto &) { return val.toExtendedValue(); }); 135 LLVM_DEBUG(llvm::dbgs() 136 << "unknown symbol: " << sym << "\nmap: " << symMap << '\n'); 137 fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value"); 138 } 139 140 ExtValue genLoad(const ExtValue &exv) { 141 return ::genLoad(builder, getLoc(), exv); 142 } 143 144 ExtValue genval(Fortran::semantics::SymbolRef sym) { 145 ExtValue var = gen(sym); 146 if (const fir::UnboxedValue *s = var.getUnboxed()) 147 if (fir::isReferenceLike(s->getType())) 148 return genLoad(*s); 149 return var; 150 } 151 152 ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) { 153 TODO(getLoc(), "genval BOZ"); 154 } 155 156 /// Return indirection to function designated in ProcedureDesignator. 157 /// The type of the function indirection is not guaranteed to match the one 158 /// of the ProcedureDesignator due to Fortran implicit typing rules. 159 ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) { 160 TODO(getLoc(), "genval ProcedureDesignator"); 161 } 162 163 ExtValue genval(const Fortran::evaluate::NullPointer &) { 164 TODO(getLoc(), "genval NullPointer"); 165 } 166 167 ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) { 168 TODO(getLoc(), "genval StructureConstructor"); 169 } 170 171 /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol. 172 ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) { 173 TODO(getLoc(), "genval ImpliedDoIndex"); 174 } 175 176 ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) { 177 TODO(getLoc(), "genval DescriptorInquiry"); 178 } 179 180 ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) { 181 TODO(getLoc(), "genval TypeParamInquiry"); 182 } 183 184 template <int KIND> 185 ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) { 186 TODO(getLoc(), "genval ComplexComponent"); 187 } 188 189 template <int KIND> 190 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 191 Fortran::common::TypeCategory::Integer, KIND>> &op) { 192 TODO(getLoc(), "genval Negate integer"); 193 } 194 195 template <int KIND> 196 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 197 Fortran::common::TypeCategory::Real, KIND>> &op) { 198 TODO(getLoc(), "genval Negate real"); 199 } 200 template <int KIND> 201 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 202 Fortran::common::TypeCategory::Complex, KIND>> &op) { 203 TODO(getLoc(), "genval Negate complex"); 204 } 205 206 #undef GENBIN 207 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \ 208 template <int KIND> \ 209 ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \ 210 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \ 211 TODO(getLoc(), "genval GenBinEvOp"); \ 212 } 213 214 GENBIN(Add, Integer, mlir::arith::AddIOp) 215 GENBIN(Add, Real, mlir::arith::AddFOp) 216 GENBIN(Add, Complex, fir::AddcOp) 217 GENBIN(Subtract, Integer, mlir::arith::SubIOp) 218 GENBIN(Subtract, Real, mlir::arith::SubFOp) 219 GENBIN(Subtract, Complex, fir::SubcOp) 220 GENBIN(Multiply, Integer, mlir::arith::MulIOp) 221 GENBIN(Multiply, Real, mlir::arith::MulFOp) 222 GENBIN(Multiply, Complex, fir::MulcOp) 223 GENBIN(Divide, Integer, mlir::arith::DivSIOp) 224 GENBIN(Divide, Real, mlir::arith::DivFOp) 225 GENBIN(Divide, Complex, fir::DivcOp) 226 227 template <Fortran::common::TypeCategory TC, int KIND> 228 ExtValue genval( 229 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) { 230 TODO(getLoc(), "genval Power"); 231 } 232 233 template <Fortran::common::TypeCategory TC, int KIND> 234 ExtValue genval( 235 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>> 236 &op) { 237 TODO(getLoc(), "genval RealToInt"); 238 } 239 240 template <int KIND> 241 ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) { 242 TODO(getLoc(), "genval ComplexConstructor"); 243 } 244 245 template <int KIND> 246 ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) { 247 TODO(getLoc(), "genval Concat<KIND>"); 248 } 249 250 /// MIN and MAX operations 251 template <Fortran::common::TypeCategory TC, int KIND> 252 ExtValue 253 genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> 254 &op) { 255 TODO(getLoc(), "genval Extremum<TC, KIND>"); 256 } 257 258 template <int KIND> 259 ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) { 260 TODO(getLoc(), "genval SetLength<KIND>"); 261 } 262 263 template <int KIND> 264 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 265 Fortran::common::TypeCategory::Integer, KIND>> &op) { 266 TODO(getLoc(), "genval integer comparison"); 267 } 268 template <int KIND> 269 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 270 Fortran::common::TypeCategory::Real, KIND>> &op) { 271 TODO(getLoc(), "genval real comparison"); 272 } 273 template <int KIND> 274 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 275 Fortran::common::TypeCategory::Complex, KIND>> &op) { 276 TODO(getLoc(), "genval complex comparison"); 277 } 278 template <int KIND> 279 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 280 Fortran::common::TypeCategory::Character, KIND>> &op) { 281 TODO(getLoc(), "genval char comparison"); 282 } 283 284 ExtValue 285 genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) { 286 TODO(getLoc(), "genval comparison"); 287 } 288 289 template <Fortran::common::TypeCategory TC1, int KIND, 290 Fortran::common::TypeCategory TC2> 291 ExtValue 292 genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, 293 TC2> &convert) { 294 mlir::Type ty = converter.genType(TC1, KIND); 295 mlir::Value operand = genunbox(convert.left()); 296 return builder.convertWithSemantics(getLoc(), ty, operand); 297 } 298 299 template <typename A> 300 ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) { 301 TODO(getLoc(), "genval parentheses<A>"); 302 } 303 304 template <int KIND> 305 ExtValue genval(const Fortran::evaluate::Not<KIND> &op) { 306 TODO(getLoc(), "genval Not<KIND>"); 307 } 308 309 template <int KIND> 310 ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) { 311 TODO(getLoc(), "genval LogicalOperation<KIND>"); 312 } 313 314 /// Convert a scalar literal constant to IR. 315 template <Fortran::common::TypeCategory TC, int KIND> 316 ExtValue genScalarLit( 317 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> 318 &value) { 319 if constexpr (TC == Fortran::common::TypeCategory::Integer) { 320 return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64()); 321 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) { 322 return genBoolConstant(value.IsTrue()); 323 } else if constexpr (TC == Fortran::common::TypeCategory::Real) { 324 TODO(getLoc(), "genval real constant"); 325 } else if constexpr (TC == Fortran::common::TypeCategory::Complex) { 326 TODO(getLoc(), "genval complex constant"); 327 } else /*constexpr*/ { 328 llvm_unreachable("unhandled constant"); 329 } 330 } 331 332 /// Convert a ascii scalar literal CHARACTER to IR. (specialization) 333 ExtValue 334 genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type< 335 Fortran::common::TypeCategory::Character, 1>> &value, 336 int64_t len) { 337 assert(value.size() == static_cast<std::uint64_t>(len) && 338 "value.size() doesn't match with len"); 339 return fir::factory::createStringLiteral(builder, getLoc(), value); 340 } 341 342 template <Fortran::common::TypeCategory TC, int KIND> 343 ExtValue 344 genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>> 345 &con) { 346 if (con.Rank() > 0) 347 TODO(getLoc(), "genval array constant"); 348 std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>> 349 opt = con.GetScalarValue(); 350 assert(opt.has_value() && "constant has no value"); 351 if constexpr (TC == Fortran::common::TypeCategory::Character) { 352 if constexpr (KIND == 1) 353 return genAsciiScalarLit(opt.value(), con.LEN()); 354 TODO(getLoc(), "genval for Character with KIND != 1"); 355 } else { 356 return genScalarLit<TC, KIND>(opt.value()); 357 } 358 } 359 360 fir::ExtendedValue genval( 361 const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) { 362 TODO(getLoc(), "genval constant derived"); 363 } 364 365 template <typename A> 366 ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) { 367 TODO(getLoc(), "genval ArrayConstructor<A>"); 368 } 369 370 ExtValue gen(const Fortran::evaluate::ComplexPart &x) { 371 TODO(getLoc(), "gen ComplexPart"); 372 } 373 ExtValue genval(const Fortran::evaluate::ComplexPart &x) { 374 TODO(getLoc(), "genval ComplexPart"); 375 } 376 377 ExtValue gen(const Fortran::evaluate::Substring &s) { 378 TODO(getLoc(), "gen Substring"); 379 } 380 ExtValue genval(const Fortran::evaluate::Substring &ss) { 381 TODO(getLoc(), "genval Substring"); 382 } 383 384 ExtValue genval(const Fortran::evaluate::Subscript &subs) { 385 TODO(getLoc(), "genval Subscript"); 386 } 387 388 ExtValue gen(const Fortran::evaluate::DataRef &dref) { 389 TODO(getLoc(), "gen DataRef"); 390 } 391 ExtValue genval(const Fortran::evaluate::DataRef &dref) { 392 TODO(getLoc(), "genval DataRef"); 393 } 394 395 ExtValue gen(const Fortran::evaluate::Component &cmpt) { 396 TODO(getLoc(), "gen Component"); 397 } 398 ExtValue genval(const Fortran::evaluate::Component &cmpt) { 399 TODO(getLoc(), "genval Component"); 400 } 401 402 ExtValue genval(const Fortran::semantics::Bound &bound) { 403 TODO(getLoc(), "genval Bound"); 404 } 405 406 ExtValue gen(const Fortran::evaluate::ArrayRef &aref) { 407 TODO(getLoc(), "gen ArrayRef"); 408 } 409 ExtValue genval(const Fortran::evaluate::ArrayRef &aref) { 410 TODO(getLoc(), "genval ArrayRef"); 411 } 412 413 ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) { 414 TODO(getLoc(), "gen CoarrayRef"); 415 } 416 ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) { 417 TODO(getLoc(), "genval CoarrayRef"); 418 } 419 420 template <typename A> 421 ExtValue gen(const Fortran::evaluate::Designator<A> &des) { 422 return std::visit([&](const auto &x) { return gen(x); }, des.u); 423 } 424 template <typename A> 425 ExtValue genval(const Fortran::evaluate::Designator<A> &des) { 426 return std::visit([&](const auto &x) { return genval(x); }, des.u); 427 } 428 429 template <typename A> 430 ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) { 431 TODO(getLoc(), "gen FunctionRef<A>"); 432 } 433 434 template <typename A> 435 ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) { 436 TODO(getLoc(), "genval FunctionRef<A>"); 437 } 438 439 ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) { 440 TODO(getLoc(), "genval ProcedureRef"); 441 } 442 443 template <typename A> 444 ExtValue genval(const Fortran::evaluate::Expr<A> &x) { 445 if (isScalar(x)) 446 return std::visit([&](const auto &e) { return genval(e); }, x.u); 447 TODO(getLoc(), "genval Expr<A> arrays"); 448 } 449 450 /// Helper to detect Transformational function reference. 451 template <typename T> 452 bool isTransformationalRef(const T &) { 453 return false; 454 } 455 template <typename T> 456 bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) { 457 return !funcRef.IsElemental() && funcRef.Rank(); 458 } 459 template <typename T> 460 bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) { 461 return std::visit([&](const auto &e) { return isTransformationalRef(e); }, 462 expr.u); 463 } 464 465 template <typename A> 466 ExtValue gen(const Fortran::evaluate::Expr<A> &x) { 467 // Whole array symbols or components, and results of transformational 468 // functions already have a storage and the scalar expression lowering path 469 // is used to not create a new temporary storage. 470 if (isScalar(x) || 471 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) || 472 isTransformationalRef(x)) 473 return std::visit([&](const auto &e) { return genref(e); }, x.u); 474 TODO(getLoc(), "gen Expr non-scalar"); 475 } 476 477 template <typename A> 478 bool isScalar(const A &x) { 479 return x.Rank() == 0; 480 } 481 482 template <int KIND> 483 ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type< 484 Fortran::common::TypeCategory::Logical, KIND>> &exp) { 485 return std::visit([&](const auto &e) { return genval(e); }, exp.u); 486 } 487 488 using RefSet = 489 std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring, 490 Fortran::evaluate::DataRef, Fortran::evaluate::Component, 491 Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef, 492 Fortran::semantics::SymbolRef>; 493 template <typename A> 494 static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>; 495 496 template <typename A, typename = std::enable_if_t<inRefSet<A>>> 497 ExtValue genref(const A &a) { 498 return gen(a); 499 } 500 template <typename A> 501 ExtValue genref(const A &a) { 502 mlir::Type storageType = converter.genType(toEvExpr(a)); 503 return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType); 504 } 505 506 template <typename A, template <typename> typename T, 507 typename B = std::decay_t<T<A>>, 508 std::enable_if_t< 509 std::is_same_v<B, Fortran::evaluate::Expr<A>> || 510 std::is_same_v<B, Fortran::evaluate::Designator<A>> || 511 std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>, 512 bool> = true> 513 ExtValue genref(const T<A> &x) { 514 return gen(x); 515 } 516 517 private: 518 mlir::Location location; 519 Fortran::lower::AbstractConverter &converter; 520 fir::FirOpBuilder &builder; 521 Fortran::lower::SymMap &symMap; 522 }; 523 } // namespace 524 525 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression( 526 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 527 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) { 528 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n'); 529 return ScalarExprLowering{loc, converter, symMap}.genval(expr); 530 } 531 532 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress( 533 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 534 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) { 535 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n'); 536 return ScalarExprLowering{loc, converter, symMap}.gen(expr); 537 } 538