//===-- ConvertType.cpp ---------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "flang/Lower/ConvertType.h" #include "flang/Lower/AbstractConverter.h" #include "flang/Lower/PFTBuilder.h" #include "flang/Lower/Todo.h" #include "flang/Lower/Utils.h" #include "flang/Optimizer/Dialect/FIRType.h" #include "flang/Semantics/tools.h" #include "flang/Semantics/type.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" #define DEBUG_TYPE "flang-lower-type" //===--------------------------------------------------------------------===// // Intrinsic type translation helpers //===--------------------------------------------------------------------===// template int getIntegerBits() { return Fortran::evaluate::Type::Scalar::bits; } static mlir::Type genIntegerType(mlir::MLIRContext *context, int kind) { if (Fortran::evaluate::IsValidKindOfIntrinsicType( Fortran::common::TypeCategory::Integer, kind)) { switch (kind) { case 1: return mlir::IntegerType::get(context, getIntegerBits<1>()); case 2: return mlir::IntegerType::get(context, getIntegerBits<2>()); case 4: return mlir::IntegerType::get(context, getIntegerBits<4>()); case 8: return mlir::IntegerType::get(context, getIntegerBits<8>()); case 16: return mlir::IntegerType::get(context, getIntegerBits<16>()); } } llvm_unreachable("INTEGER kind not translated"); } static mlir::Type genLogicalType(mlir::MLIRContext *context, int KIND) { if (Fortran::evaluate::IsValidKindOfIntrinsicType( Fortran::common::TypeCategory::Logical, KIND)) return fir::LogicalType::get(context, KIND); return {}; } static mlir::Type genFIRType(mlir::MLIRContext *context, Fortran::common::TypeCategory tc, int kind) { switch (tc) { case Fortran::common::TypeCategory::Real: TODO_NOLOC("genFIRType Real"); case Fortran::common::TypeCategory::Integer: return genIntegerType(context, kind); case Fortran::common::TypeCategory::Complex: TODO_NOLOC("genFIRType Complex"); case Fortran::common::TypeCategory::Logical: return genLogicalType(context, kind); case Fortran::common::TypeCategory::Character: TODO_NOLOC("genFIRType Character"); default: break; } llvm_unreachable("unhandled type category"); } template bool isConstant(const Fortran::evaluate::Expr &e) { return Fortran::evaluate::IsConstantExpr(Fortran::lower::SomeExpr{e}); } template int64_t toConstant(const Fortran::evaluate::Expr &e) { auto opt = Fortran::evaluate::ToInt64(e); assert(opt.has_value() && "expression didn't resolve to a constant"); return opt.value(); } // one argument template, must be specialized template mlir::Type genFIRType(mlir::MLIRContext *, int) { return {}; } // two argument template template mlir::Type genFIRType(mlir::MLIRContext *context) { if constexpr (TC == Fortran::common::TypeCategory::Integer) { auto bits{Fortran::evaluate::Type::Scalar::bits}; return mlir::IntegerType::get(context, bits); } else if constexpr (TC == Fortran::common::TypeCategory::Logical || TC == Fortran::common::TypeCategory::Character || TC == Fortran::common::TypeCategory::Complex) { return genFIRType(context, KIND); } else { return {}; } } template <> mlir::Type genFIRType(mlir::MLIRContext *context) { return mlir::FloatType::getF16(context); } template <> mlir::Type genFIRType(mlir::MLIRContext *context) { return mlir::FloatType::getBF16(context); } template <> mlir::Type genFIRType(mlir::MLIRContext *context) { return mlir::FloatType::getF32(context); } template <> mlir::Type genFIRType(mlir::MLIRContext *context) { return mlir::FloatType::getF64(context); } template <> mlir::Type genFIRType( mlir::MLIRContext *context) { return fir::RealType::get(context, 10); } template <> mlir::Type genFIRType( mlir::MLIRContext *context) { return fir::RealType::get(context, 16); } template <> mlir::Type genFIRType(mlir::MLIRContext *context, int kind) { if (Fortran::evaluate::IsValidKindOfIntrinsicType( Fortran::common::TypeCategory::Real, kind)) { switch (kind) { case 2: return genFIRType(context); case 3: return genFIRType(context); case 4: return genFIRType(context); case 8: return genFIRType(context); case 10: return genFIRType(context); case 16: return genFIRType(context); } } llvm_unreachable("REAL type translation not implemented"); } template <> mlir::Type genFIRType(mlir::MLIRContext *context, int KIND) { if (Fortran::evaluate::IsValidKindOfIntrinsicType( Fortran::common::TypeCategory::Character, KIND)) return fir::CharacterType::get(context, KIND, 1); return {}; } template <> mlir::Type genFIRType(mlir::MLIRContext *context, int KIND) { if (Fortran::evaluate::IsValidKindOfIntrinsicType( Fortran::common::TypeCategory::Complex, KIND)) return fir::ComplexType::get(context, KIND); return {}; } namespace { /// Discover the type of an Fortran::evaluate::Expr and convert it to an /// mlir::Type. The type returned may be an MLIR standard or FIR type. class TypeBuilder { public: TypeBuilder(Fortran::lower::AbstractConverter &converter) : converter{converter}, context{&converter.getMLIRContext()} {} template std::optional toInt64(A &&expr) { return Fortran::evaluate::ToInt64(Fortran::evaluate::Fold( converter.getFoldingContext(), std::move(expr))); } mlir::Type genSymbolType(const Fortran::semantics::Symbol &symbol, bool isAlloc = false, bool isPtr = false) { mlir::Location loc = converter.genLocation(symbol.name()); mlir::Type ty; // If the symbol is not the same as the ultimate one (i.e, it is host or use // associated), all the symbol properties are the ones of the ultimate // symbol but the volatile and asynchronous attributes that may differ. To // avoid issues with helper functions that would not follow association // links, the fir type is built based on the ultimate symbol. This relies // on the fact volatile and asynchronous are not reflected in fir types. const Fortran::semantics::Symbol &ultimate = symbol.GetUltimate(); if (const Fortran::semantics::DeclTypeSpec *type = ultimate.GetType()) { if (const Fortran::semantics::IntrinsicTypeSpec *tySpec = type->AsIntrinsic()) { int kind = toInt64(Fortran::common::Clone(tySpec->kind())).value(); ty = genFIRType(context, tySpec->category(), kind); } else if (type->IsPolymorphic()) { TODO(loc, "genSymbolType polymorphic types"); } else if (type->AsDerived()) { TODO(loc, "genSymbolType derived type"); } else { fir::emitFatalError(loc, "symbol's type must have a type spec"); } } else { fir::emitFatalError(loc, "symbol must have a type"); } if (Fortran::semantics::IsPointer(symbol)) return fir::BoxType::get(fir::PointerType::get(ty)); if (Fortran::semantics::IsAllocatable(symbol)) return fir::BoxType::get(fir::HeapType::get(ty)); // isPtr and isAlloc are variable that were promoted to be on the // heap or to be pointers, but they do not have Fortran allocatable // or pointer semantics, so do not use box for them. if (isPtr) return fir::PointerType::get(ty); if (isAlloc) return fir::HeapType::get(ty); return ty; } //===--------------------------------------------------------------------===// // Generate type entry points //===--------------------------------------------------------------------===// template