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 mlir::Value input = genunbox(op.left()); 193 // Like LLVM, integer negation is the binary op "0 - value" 194 mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0); 195 return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input); 196 } 197 198 template <int KIND> 199 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 200 Fortran::common::TypeCategory::Real, KIND>> &op) { 201 return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left())); 202 } 203 template <int KIND> 204 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type< 205 Fortran::common::TypeCategory::Complex, KIND>> &op) { 206 return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left())); 207 } 208 209 template <typename OpTy> 210 mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) { 211 assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right)); 212 mlir::Value lhs = fir::getBase(left); 213 mlir::Value rhs = fir::getBase(right); 214 assert(lhs.getType() == rhs.getType() && "types must be the same"); 215 return builder.create<OpTy>(getLoc(), lhs, rhs); 216 } 217 218 template <typename OpTy, typename A> 219 mlir::Value createBinaryOp(const A &ex) { 220 ExtValue left = genval(ex.left()); 221 return createBinaryOp<OpTy>(left, genval(ex.right())); 222 } 223 224 #undef GENBIN 225 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \ 226 template <int KIND> \ 227 ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \ 228 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \ 229 return createBinaryOp<GenBinFirOp>(x); \ 230 } 231 232 GENBIN(Add, Integer, mlir::arith::AddIOp) 233 GENBIN(Add, Real, mlir::arith::AddFOp) 234 GENBIN(Add, Complex, fir::AddcOp) 235 GENBIN(Subtract, Integer, mlir::arith::SubIOp) 236 GENBIN(Subtract, Real, mlir::arith::SubFOp) 237 GENBIN(Subtract, Complex, fir::SubcOp) 238 GENBIN(Multiply, Integer, mlir::arith::MulIOp) 239 GENBIN(Multiply, Real, mlir::arith::MulFOp) 240 GENBIN(Multiply, Complex, fir::MulcOp) 241 GENBIN(Divide, Integer, mlir::arith::DivSIOp) 242 GENBIN(Divide, Real, mlir::arith::DivFOp) 243 GENBIN(Divide, Complex, fir::DivcOp) 244 245 template <Fortran::common::TypeCategory TC, int KIND> 246 ExtValue genval( 247 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) { 248 TODO(getLoc(), "genval Power"); 249 } 250 251 template <Fortran::common::TypeCategory TC, int KIND> 252 ExtValue genval( 253 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>> 254 &op) { 255 TODO(getLoc(), "genval RealToInt"); 256 } 257 258 template <int KIND> 259 ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) { 260 TODO(getLoc(), "genval ComplexConstructor"); 261 } 262 263 template <int KIND> 264 ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) { 265 TODO(getLoc(), "genval Concat<KIND>"); 266 } 267 268 /// MIN and MAX operations 269 template <Fortran::common::TypeCategory TC, int KIND> 270 ExtValue 271 genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> 272 &op) { 273 TODO(getLoc(), "genval Extremum<TC, KIND>"); 274 } 275 276 template <int KIND> 277 ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) { 278 TODO(getLoc(), "genval SetLength<KIND>"); 279 } 280 281 template <int KIND> 282 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 283 Fortran::common::TypeCategory::Integer, KIND>> &op) { 284 TODO(getLoc(), "genval integer comparison"); 285 } 286 template <int KIND> 287 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 288 Fortran::common::TypeCategory::Real, KIND>> &op) { 289 TODO(getLoc(), "genval real comparison"); 290 } 291 template <int KIND> 292 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 293 Fortran::common::TypeCategory::Complex, KIND>> &op) { 294 TODO(getLoc(), "genval complex comparison"); 295 } 296 template <int KIND> 297 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type< 298 Fortran::common::TypeCategory::Character, KIND>> &op) { 299 TODO(getLoc(), "genval char comparison"); 300 } 301 302 ExtValue 303 genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) { 304 TODO(getLoc(), "genval comparison"); 305 } 306 307 template <Fortran::common::TypeCategory TC1, int KIND, 308 Fortran::common::TypeCategory TC2> 309 ExtValue 310 genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, 311 TC2> &convert) { 312 mlir::Type ty = converter.genType(TC1, KIND); 313 mlir::Value operand = genunbox(convert.left()); 314 return builder.convertWithSemantics(getLoc(), ty, operand); 315 } 316 317 template <typename A> 318 ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) { 319 TODO(getLoc(), "genval parentheses<A>"); 320 } 321 322 template <int KIND> 323 ExtValue genval(const Fortran::evaluate::Not<KIND> &op) { 324 TODO(getLoc(), "genval Not<KIND>"); 325 } 326 327 template <int KIND> 328 ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) { 329 TODO(getLoc(), "genval LogicalOperation<KIND>"); 330 } 331 332 /// Convert a scalar literal constant to IR. 333 template <Fortran::common::TypeCategory TC, int KIND> 334 ExtValue genScalarLit( 335 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> 336 &value) { 337 if constexpr (TC == Fortran::common::TypeCategory::Integer) { 338 return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64()); 339 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) { 340 return genBoolConstant(value.IsTrue()); 341 } else if constexpr (TC == Fortran::common::TypeCategory::Real) { 342 TODO(getLoc(), "genval real constant"); 343 } else if constexpr (TC == Fortran::common::TypeCategory::Complex) { 344 TODO(getLoc(), "genval complex constant"); 345 } else /*constexpr*/ { 346 llvm_unreachable("unhandled constant"); 347 } 348 } 349 350 /// Convert a ascii scalar literal CHARACTER to IR. (specialization) 351 ExtValue 352 genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type< 353 Fortran::common::TypeCategory::Character, 1>> &value, 354 int64_t len) { 355 assert(value.size() == static_cast<std::uint64_t>(len) && 356 "value.size() doesn't match with len"); 357 return fir::factory::createStringLiteral(builder, getLoc(), value); 358 } 359 360 template <Fortran::common::TypeCategory TC, int KIND> 361 ExtValue 362 genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>> 363 &con) { 364 if (con.Rank() > 0) 365 TODO(getLoc(), "genval array constant"); 366 std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>> 367 opt = con.GetScalarValue(); 368 assert(opt.has_value() && "constant has no value"); 369 if constexpr (TC == Fortran::common::TypeCategory::Character) { 370 if constexpr (KIND == 1) 371 return genAsciiScalarLit(opt.value(), con.LEN()); 372 TODO(getLoc(), "genval for Character with KIND != 1"); 373 } else { 374 return genScalarLit<TC, KIND>(opt.value()); 375 } 376 } 377 378 fir::ExtendedValue genval( 379 const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) { 380 TODO(getLoc(), "genval constant derived"); 381 } 382 383 template <typename A> 384 ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) { 385 TODO(getLoc(), "genval ArrayConstructor<A>"); 386 } 387 388 ExtValue gen(const Fortran::evaluate::ComplexPart &x) { 389 TODO(getLoc(), "gen ComplexPart"); 390 } 391 ExtValue genval(const Fortran::evaluate::ComplexPart &x) { 392 TODO(getLoc(), "genval ComplexPart"); 393 } 394 395 ExtValue gen(const Fortran::evaluate::Substring &s) { 396 TODO(getLoc(), "gen Substring"); 397 } 398 ExtValue genval(const Fortran::evaluate::Substring &ss) { 399 TODO(getLoc(), "genval Substring"); 400 } 401 402 ExtValue genval(const Fortran::evaluate::Subscript &subs) { 403 TODO(getLoc(), "genval Subscript"); 404 } 405 406 ExtValue gen(const Fortran::evaluate::DataRef &dref) { 407 TODO(getLoc(), "gen DataRef"); 408 } 409 ExtValue genval(const Fortran::evaluate::DataRef &dref) { 410 TODO(getLoc(), "genval DataRef"); 411 } 412 413 ExtValue gen(const Fortran::evaluate::Component &cmpt) { 414 TODO(getLoc(), "gen Component"); 415 } 416 ExtValue genval(const Fortran::evaluate::Component &cmpt) { 417 TODO(getLoc(), "genval Component"); 418 } 419 420 ExtValue genval(const Fortran::semantics::Bound &bound) { 421 TODO(getLoc(), "genval Bound"); 422 } 423 424 ExtValue gen(const Fortran::evaluate::ArrayRef &aref) { 425 TODO(getLoc(), "gen ArrayRef"); 426 } 427 ExtValue genval(const Fortran::evaluate::ArrayRef &aref) { 428 TODO(getLoc(), "genval ArrayRef"); 429 } 430 431 ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) { 432 TODO(getLoc(), "gen CoarrayRef"); 433 } 434 ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) { 435 TODO(getLoc(), "genval CoarrayRef"); 436 } 437 438 template <typename A> 439 ExtValue gen(const Fortran::evaluate::Designator<A> &des) { 440 return std::visit([&](const auto &x) { return gen(x); }, des.u); 441 } 442 template <typename A> 443 ExtValue genval(const Fortran::evaluate::Designator<A> &des) { 444 return std::visit([&](const auto &x) { return genval(x); }, des.u); 445 } 446 447 template <typename A> 448 ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) { 449 TODO(getLoc(), "gen FunctionRef<A>"); 450 } 451 452 template <typename A> 453 ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) { 454 TODO(getLoc(), "genval FunctionRef<A>"); 455 } 456 457 ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) { 458 TODO(getLoc(), "genval ProcedureRef"); 459 } 460 461 template <typename A> 462 ExtValue genval(const Fortran::evaluate::Expr<A> &x) { 463 if (isScalar(x)) 464 return std::visit([&](const auto &e) { return genval(e); }, x.u); 465 TODO(getLoc(), "genval Expr<A> arrays"); 466 } 467 468 /// Helper to detect Transformational function reference. 469 template <typename T> 470 bool isTransformationalRef(const T &) { 471 return false; 472 } 473 template <typename T> 474 bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) { 475 return !funcRef.IsElemental() && funcRef.Rank(); 476 } 477 template <typename T> 478 bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) { 479 return std::visit([&](const auto &e) { return isTransformationalRef(e); }, 480 expr.u); 481 } 482 483 template <typename A> 484 ExtValue gen(const Fortran::evaluate::Expr<A> &x) { 485 // Whole array symbols or components, and results of transformational 486 // functions already have a storage and the scalar expression lowering path 487 // is used to not create a new temporary storage. 488 if (isScalar(x) || 489 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) || 490 isTransformationalRef(x)) 491 return std::visit([&](const auto &e) { return genref(e); }, x.u); 492 TODO(getLoc(), "gen Expr non-scalar"); 493 } 494 495 template <typename A> 496 bool isScalar(const A &x) { 497 return x.Rank() == 0; 498 } 499 500 template <int KIND> 501 ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type< 502 Fortran::common::TypeCategory::Logical, KIND>> &exp) { 503 return std::visit([&](const auto &e) { return genval(e); }, exp.u); 504 } 505 506 using RefSet = 507 std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring, 508 Fortran::evaluate::DataRef, Fortran::evaluate::Component, 509 Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef, 510 Fortran::semantics::SymbolRef>; 511 template <typename A> 512 static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>; 513 514 template <typename A, typename = std::enable_if_t<inRefSet<A>>> 515 ExtValue genref(const A &a) { 516 return gen(a); 517 } 518 template <typename A> 519 ExtValue genref(const A &a) { 520 mlir::Type storageType = converter.genType(toEvExpr(a)); 521 return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType); 522 } 523 524 template <typename A, template <typename> typename T, 525 typename B = std::decay_t<T<A>>, 526 std::enable_if_t< 527 std::is_same_v<B, Fortran::evaluate::Expr<A>> || 528 std::is_same_v<B, Fortran::evaluate::Designator<A>> || 529 std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>, 530 bool> = true> 531 ExtValue genref(const T<A> &x) { 532 return gen(x); 533 } 534 535 private: 536 mlir::Location location; 537 Fortran::lower::AbstractConverter &converter; 538 fir::FirOpBuilder &builder; 539 Fortran::lower::SymMap &symMap; 540 }; 541 } // namespace 542 543 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression( 544 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 545 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) { 546 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n'); 547 return ScalarExprLowering{loc, converter, symMap}.genval(expr); 548 } 549 550 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress( 551 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 552 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) { 553 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n'); 554 return ScalarExprLowering{loc, converter, symMap}.gen(expr); 555 } 556