text: //===-- TypeConverter.h -- type conversion ----------------------*- C++ -*-===//
  //
  // 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
  //
  //===----------------------------------------------------------------------===//
  //
  // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
  //
  //===----------------------------------------------------------------------===//
  
  #ifndef FORTRAN_OPTIMIZER_CODEGEN_TYPECONVERTER_H
  #define FORTRAN_OPTIMIZER_CODEGEN_TYPECONVERTER_H
  
  #include "DescriptorModel.h"
  #include "Target.h"
  #include "flang/Lower/Todo.h" // remove when TODO's are done
  #include "flang/Optimizer/Dialect/FIRType.h"
  #include "flang/Optimizer/Support/FIRContext.h"
  #include "flang/Optimizer/Support/KindMapping.h"
  #include "mlir/Conversion/LLVMCommon/TypeConverter.h"
  #include "llvm/Support/Debug.h"
  
  // Position of the different values in a `fir.box`.
  static constexpr unsigned kAddrPosInBox = 0;
  static constexpr unsigned kElemLenPosInBox = 1;
  static constexpr unsigned kVersionPosInBox = 2;
  static constexpr unsigned kRankPosInBox = 3;
  static constexpr unsigned kTypePosInBox = 4;
  static constexpr unsigned kAttributePosInBox = 5;
  static constexpr unsigned kF18AddendumPosInBox = 6;
  static constexpr unsigned kDimsPosInBox = 7;
  static constexpr unsigned kOptTypePtrPosInBox = 8;
  static constexpr unsigned kOptRowTypePosInBox = 9;
  
  namespace fir {
  
  /// FIR type converter
  /// This converts FIR types to LLVM types (for now)
  class LLVMTypeConverter : public mlir::LLVMTypeConverter {
  public:
    LLVMTypeConverter(mlir::ModuleOp module)
        : mlir::LLVMTypeConverter(module.getContext()),
          kindMapping(getKindMapping(module)),
          specifics(CodeGenSpecifics::get(module.getContext(),
                                          getTargetTriple(module),
                                          getKindMapping(module))) {
      LLVM_DEBUG(llvm::dbgs() << "FIR type converter\n");
  
      // Each conversion should return a value of type mlir::Type.
      addConversion([&](BoxType box) { return convertBoxType(box); });
      addConversion([&](BoxCharType boxchar) {
        LLVM_DEBUG(llvm::dbgs() << "type convert: " << boxchar << '\n');
        return convertType(specifics->boxcharMemoryType(boxchar.getEleTy()));
      });
      addConversion(
          [&](fir::CharacterType charTy) { return convertCharType(charTy); });
      addConversion([&](HeapType heap) { return convertPointerLike(heap); });
      addConversion([&](fir::IntegerType intTy) {
        return mlir::IntegerType::get(
            &getContext(), kindMapping.getIntegerBitsize(intTy.getFKind()));
      });
      addConversion([&](fir::LogicalType boolTy) {
        return mlir::IntegerType::get(
            &getContext(), kindMapping.getLogicalBitsize(boolTy.getFKind()));
      });
      addConversion([&](fir::LLVMPointerType pointer) {
        return convertPointerLike(pointer);
      });
      addConversion(
          [&](fir::PointerType pointer) { return convertPointerLike(pointer); });
      addConversion(
          [&](fir::RecordType derived) { return convertRecordType(derived); });
      addConversion([&](fir::FieldType field) {
        // Convert to i32 because of LLVM GEP indexing restriction.
        return mlir::IntegerType::get(field.getContext(), 32);
      });
      addConversion([&](fir::LenType field) {
        // Get size of len paramter from the descriptor.
        return getModel<Fortran::runtime::typeInfo::TypeParameterValue>()(
            &getContext());
      });
      addConversion(
          [&](fir::ComplexType cmplx) { return convertComplexType(cmplx); });
      addConversion(
          [&](fir::RealType real) { return convertRealType(real.getFKind()); });
      addConversion(
          [&](fir::ReferenceType ref) { return convertPointerLike(ref); });
      addConversion([&](fir::SequenceType sequence) {
        return convertSequenceType(sequence);
      });
      addConversion([&](fir::TypeDescType tdesc) {
        return convertTypeDescType(tdesc.getContext());
      });
      addConversion([&](fir::VectorType vecTy) {
        return mlir::VectorType::get(llvm::ArrayRef<int64_t>(vecTy.getLen()),
                                     convertType(vecTy.getEleTy()));
      });
      addConversion([&](mlir::TupleType tuple) {
        LLVM_DEBUG(llvm::dbgs() << "type convert: " << tuple << '\n');
        llvm::SmallVector<mlir::Type> inMembers;
        tuple.getFlattenedTypes(inMembers);
        llvm::SmallVector<mlir::Type> members;
        for (auto mem : inMembers)
          members.push_back(convertType(mem).cast<mlir::Type>());
        return mlir::LLVM::LLVMStructType::getLiteral(&getContext(), members,
                                                      /*isPacked=*/false);
      });
    }
  
    // i32 is used here because LLVM wants i32 constants when indexing into struct
    // types. Indexing into other aggregate types is more flexible.
    mlir::Type offsetType() { return mlir::IntegerType::get(&getContext(), 32); }
  
    // i64 can be used to index into aggregates like arrays
    mlir::Type indexType() { return mlir::IntegerType::get(&getContext(), 64); }
  
    // fir.type<name(p : TY'...){f : TY...}>  -->  llvm<"%name = { ty... }">
    mlir::Type convertRecordType(fir::RecordType derived) {
      auto name = derived.getName();
      auto st = mlir::LLVM::LLVMStructType::getIdentified(&getContext(), name);
      llvm::SmallVector<mlir::Type> members;
      for (auto mem : derived.getTypeList()) {
        members.push_back(convertType(mem.second).cast<mlir::Type>());
      }
      if (mlir::succeeded(st.setBody(members, /*isPacked=*/false)))
        return st;
      return mlir::Type();
    }
  
    // Is an extended descriptor needed given the element type of a fir.box type ?
    // Extended descriptors are required for derived types.
    bool requiresExtendedDesc(mlir::Type boxElementType) {
      auto eleTy = fir::unwrapSequenceType(boxElementType);
      return eleTy.isa<fir::RecordType>();
    }
  
    // Magic value to indicate we do not know the rank of an entity, either
    // because it is assumed rank or because we have not determined it yet.
    static constexpr int unknownRank() { return -1; }
  
    // This corresponds to the descriptor as defined in ISO_Fortran_binding.h and
    // the addendum defined in descriptor.h.
    mlir::Type convertBoxType(BoxType box, int rank = unknownRank()) {
      // (base_addr*, elem_len, version, rank, type, attribute, f18Addendum, [dim]
      llvm::SmallVector<mlir::Type> dataDescFields;
      mlir::Type ele = box.getEleTy();
      // remove fir.heap/fir.ref/fir.ptr
      if (auto removeIndirection = fir::dyn_cast_ptrEleTy(ele))
        ele = removeIndirection;
      auto eleTy = convertType(ele);
      // base_addr*
      if (ele.isa<SequenceType>() && eleTy.isa<mlir::LLVM::LLVMPointerType>())
        dataDescFields.push_back(eleTy);
      else
        dataDescFields.push_back(mlir::LLVM::LLVMPointerType::get(eleTy));
      // elem_len
      dataDescFields.push_back(
          getDescFieldTypeModel<kElemLenPosInBox>()(&getContext()));
      // version
      dataDescFields.push_back(
          getDescFieldTypeModel<kVersionPosInBox>()(&getContext()));
      // rank
      dataDescFields.push_back(
          getDescFieldTypeModel<kRankPosInBox>()(&getContext()));
      // type
      dataDescFields.push_back(
          getDescFieldTypeModel<kTypePosInBox>()(&getContext()));
      // attribute
      dataDescFields.push_back(
          getDescFieldTypeModel<kAttributePosInBox>()(&getContext()));
      // f18Addendum
      dataDescFields.push_back(
          getDescFieldTypeModel<kF18AddendumPosInBox>()(&getContext()));
      // [dims]
      if (rank == unknownRank()) {
        if (auto seqTy = ele.dyn_cast<SequenceType>())
          rank = seqTy.getDimension();
        else
          rank = 0;
      }
      if (rank > 0) {
        auto rowTy = getDescFieldTypeModel<kDimsPosInBox>()(&getContext());
        dataDescFields.push_back(mlir::LLVM::LLVMArrayType::get(rowTy, rank));
      }
      // opt-type-ptr: i8* (see fir.tdesc)
      if (requiresExtendedDesc(ele)) {
        dataDescFields.push_back(
            getExtendedDescFieldTypeModel<kOptTypePtrPosInBox>()(&getContext()));
        auto rowTy =
            getExtendedDescFieldTypeModel<kOptRowTypePosInBox>()(&getContext());
        dataDescFields.push_back(mlir::LLVM::LLVMArrayType::get(rowTy, 1));
        if (auto recTy = fir::unwrapSequenceType(ele).dyn_cast<fir::RecordType>())
          if (recTy.getNumLenParams() > 0) {
            // The descriptor design needs to be clarified regarding the number of
            // length parameters in the addendum. Since it can change for
            // polymorphic allocatables, it seems all length parameters cannot
            // always possibly be placed in the addendum.
            TODO_NOLOC("extended descriptor derived with length parameters");
            unsigned numLenParams = recTy.getNumLenParams();
            dataDescFields.push_back(
                mlir::LLVM::LLVMArrayType::get(rowTy, numLenParams));
          }
      }
      return mlir::LLVM::LLVMPointerType::get(
          mlir::LLVM::LLVMStructType::getLiteral(&getContext(), dataDescFields,
                                                 /*isPacked=*/false));
    }
  
    unsigned characterBitsize(fir::CharacterType charTy) {
      return kindMapping.getCharacterBitsize(charTy.getFKind());
    }
  
    // fir.char<k,?>  -->  llvm<"ix">          where ix is scaled by kind mapping
    // fir.char<k,n>  -->  llvm.array<n x "ix">
    mlir::Type convertCharType(fir::CharacterType charTy) {
      auto iTy = mlir::IntegerType::get(&getContext(), characterBitsize(charTy));
      if (charTy.getLen() == fir::CharacterType::unknownLen())
        return iTy;
      return mlir::LLVM::LLVMArrayType::get(iTy, charTy.getLen());
    }
  
    // Use the target specifics to figure out how to map complex to LLVM IR. The
    // use of complex values in function signatures is handled before conversion
    // to LLVM IR dialect here.
    //
    // fir.complex<T> | std.complex<T>    --> llvm<"{t,t}">
    template <typename C>
    mlir::Type convertComplexType(C cmplx) {
      LLVM_DEBUG(llvm::dbgs() << "type convert: " << cmplx << '\n');
      auto eleTy = cmplx.getElementType();
      return convertType(specifics->complexMemoryType(eleTy));
    }
  
    // convert a front-end kind value to either a std or LLVM IR dialect type
    // fir.real<n>  -->  llvm.anyfloat  where anyfloat is a kind mapping
    mlir::Type convertRealType(fir::KindTy kind) {
      return fromRealTypeID(kindMapping.getRealTypeID(kind), kind);
    }
  
    template <typename A>
    mlir::Type convertPointerLike(A &ty) {
      mlir::Type eleTy = ty.getEleTy();
      // A sequence type is a special case. A sequence of runtime size on its
      // interior dimensions lowers to a memory reference. In that case, we
      // degenerate the array and do not want a the type to become `T**` but
      // merely `T*`.
      if (auto seqTy = eleTy.dyn_cast<fir::SequenceType>()) {
        if (!seqTy.hasConstantShape() ||
            characterWithDynamicLen(seqTy.getEleTy())) {
          if (seqTy.hasConstantInterior())
            return convertType(seqTy);
          eleTy = seqTy.getEleTy();
        }
      }
      // fir.ref<fir.box> is a special case because fir.box type is already
      // a pointer to a Fortran descriptor at the LLVM IR level. This implies
      // that a fir.ref<fir.box>, that is the address of fir.box is actually
      // the same as a fir.box at the LLVM level.
      // The distinction is kept in fir to denote when a descriptor is expected
      // to be mutable (fir.ref<fir.box>) and when it is not (fir.box).
      if (eleTy.isa<fir::BoxType>())
        return convertType(eleTy);
  
      return mlir::LLVM::LLVMPointerType::get(convertType(eleTy));
    }
  
    // fir.array<c ... :any>  -->  llvm<"[...[c x any]]">
    mlir::Type convertSequenceType(SequenceType seq) {
      auto baseTy = convertType(seq.getEleTy());
      if (characterWithDynamicLen(seq.getEleTy()))
        return mlir::LLVM::LLVMPointerType::get(baseTy);
      auto shape = seq.getShape();
      auto constRows = seq.getConstantRows();
      if (constRows) {
        decltype(constRows) i = constRows;
        for (auto e : shape) {
          baseTy = mlir::LLVM::LLVMArrayType::get(baseTy, e);
          if (--i == 0)
            break;
        }
        if (seq.hasConstantShape())
          return baseTy;
      }
      return mlir::LLVM::LLVMPointerType::get(baseTy);
    }
  
    // fir.tdesc<any>  -->  llvm<"i8*">
    // TODO: For now use a void*, however pointer identity is not sufficient for
    // the f18 object v. class distinction (F2003).
    mlir::Type convertTypeDescType(mlir::MLIRContext *ctx) {
      return mlir::LLVM::LLVMPointerType::get(
          mlir::IntegerType::get(&getContext(), 8));
    }
  
    /// Convert llvm::Type::TypeID to mlir::Type
    mlir::Type fromRealTypeID(llvm::Type::TypeID typeID, fir::KindTy kind) {
      switch (typeID) {
      case llvm::Type::TypeID::HalfTyID:
        return mlir::FloatType::getF16(&getContext());
      case llvm::Type::TypeID::BFloatTyID:
        return mlir::FloatType::getBF16(&getContext());
      case llvm::Type::TypeID::FloatTyID:
        return mlir::FloatType::getF32(&getContext());
      case llvm::Type::TypeID::DoubleTyID:
        return mlir::FloatType::getF64(&getContext());
      case llvm::Type::TypeID::X86_FP80TyID:
        return mlir::FloatType::getF80(&getContext());
      case llvm::Type::TypeID::FP128TyID:
        return mlir::FloatType::getF128(&getContext());
      default:
        mlir::emitError(mlir::UnknownLoc::get(&getContext()))
            << "unsupported type: !fir.real<" << kind << ">";
        return {};
      }
    }
  
    KindMapping &getKindMap() { return kindMapping; }
  
  private:
    KindMapping kindMapping;
    std::unique_ptr<CodeGenSpecifics> specifics;
  };
  
  } // namespace fir
  
  #endif // FORTRAN_OPTIMIZER_CODEGEN_TYPECONVERTER_H
  