blob: 09a774e3d72dc79fb5bcd4b6b667df40aba27486 [file] [log] [blame]
//===-- RTBuilder.h ---------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines some C++17 template classes that are used to convert the
/// signatures of plain old C functions into a model that can be used to
/// generate MLIR calls to those functions. This can be used to autogenerate
/// tables at compiler compile-time to call runtime support code.
///
//===----------------------------------------------------------------------===//
#ifndef FORTRAN_OPTIMIZER_BUILDER_RUNTIME_RTBUILDER_H
#define FORTRAN_OPTIMIZER_BUILDER_RUNTIME_RTBUILDER_H
#include "flang/Common/Fortran.h"
#include "flang/Common/uint128.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/MLIRContext.h"
#include "llvm/ADT/SmallVector.h"
#include <functional>
// Incomplete type indicating C99 complex ABI in interfaces. Beware, _Complex
// and std::complex are layout compatible, but not compatible in all ABI call
// interfaces (e.g. X86 32 bits). _Complex is not standard C++, so do not use
// it here.
struct c_float_complex_t;
struct c_double_complex_t;
namespace Fortran::runtime {
class Descriptor;
}
namespace fir::runtime {
using TypeBuilderFunc = mlir::Type (*)(mlir::MLIRContext *);
using FuncTypeBuilderFunc = mlir::FunctionType (*)(mlir::MLIRContext *);
//===----------------------------------------------------------------------===//
// Type builder models
//===----------------------------------------------------------------------===//
// TODO: all usages of sizeof in this file assume build == host == target.
// This will need to be re-visited for cross compilation.
/// Return a function that returns the type signature model for the type `T`
/// when provided an MLIRContext*. This allows one to translate C(++) function
/// signatures from runtime header files to MLIR signatures into a static table
/// at compile-time.
///
/// For example, when `T` is `int`, return a function that returns the MLIR
/// standard type `i32` when `sizeof(int)` is 4.
template <typename T>
static constexpr TypeBuilderFunc getModel();
template <>
constexpr TypeBuilderFunc getModel<short int>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, 8 * sizeof(short int));
};
}
template <>
constexpr TypeBuilderFunc getModel<int>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, 8 * sizeof(int));
};
}
template <>
constexpr TypeBuilderFunc getModel<int &>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
TypeBuilderFunc f{getModel<int>()};
return fir::ReferenceType::get(f(context));
};
}
template <>
constexpr TypeBuilderFunc getModel<char *>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return fir::ReferenceType::get(mlir::IntegerType::get(context, 8));
};
}
template <>
constexpr TypeBuilderFunc getModel<const char *>() {
return getModel<char *>();
}
template <>
constexpr TypeBuilderFunc getModel<const char16_t *>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return fir::ReferenceType::get(mlir::IntegerType::get(context, 16));
};
}
template <>
constexpr TypeBuilderFunc getModel<const char32_t *>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return fir::ReferenceType::get(mlir::IntegerType::get(context, 32));
};
}
template <>
constexpr TypeBuilderFunc getModel<signed char>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, 8 * sizeof(signed char));
};
}
template <>
constexpr TypeBuilderFunc getModel<void *>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return fir::LLVMPointerType::get(context,
mlir::IntegerType::get(context, 8));
};
}
template <>
constexpr TypeBuilderFunc getModel<void **>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return fir::ReferenceType::get(
fir::LLVMPointerType::get(context, mlir::IntegerType::get(context, 8)));
};
}
template <>
constexpr TypeBuilderFunc getModel<long>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, 8 * sizeof(long));
};
}
template <>
constexpr TypeBuilderFunc getModel<long &>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
TypeBuilderFunc f{getModel<long>()};
return fir::ReferenceType::get(f(context));
};
}
template <>
constexpr TypeBuilderFunc getModel<long *>() {
return getModel<long &>();
}
template <>
constexpr TypeBuilderFunc getModel<long long>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, 8 * sizeof(long long));
};
}
template <>
constexpr TypeBuilderFunc getModel<Fortran::common::int128_t>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context,
8 * sizeof(Fortran::common::int128_t));
};
}
template <>
constexpr TypeBuilderFunc getModel<long long &>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
TypeBuilderFunc f{getModel<long long>()};
return fir::ReferenceType::get(f(context));
};
}
template <>
constexpr TypeBuilderFunc getModel<long long *>() {
return getModel<long long &>();
}
template <>
constexpr TypeBuilderFunc getModel<unsigned long>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, 8 * sizeof(unsigned long));
};
}
template <>
constexpr TypeBuilderFunc getModel<unsigned long long>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, 8 * sizeof(unsigned long long));
};
}
template <>
constexpr TypeBuilderFunc getModel<double>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::FloatType::getF64(context);
};
}
template <>
constexpr TypeBuilderFunc getModel<double &>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
TypeBuilderFunc f{getModel<double>()};
return fir::ReferenceType::get(f(context));
};
}
template <>
constexpr TypeBuilderFunc getModel<double *>() {
return getModel<double &>();
}
template <>
constexpr TypeBuilderFunc getModel<float>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::FloatType::getF32(context);
};
}
template <>
constexpr TypeBuilderFunc getModel<float &>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
TypeBuilderFunc f{getModel<float>()};
return fir::ReferenceType::get(f(context));
};
}
template <>
constexpr TypeBuilderFunc getModel<float *>() {
return getModel<float &>();
}
template <>
constexpr TypeBuilderFunc getModel<bool>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context, 1);
};
}
template <>
constexpr TypeBuilderFunc getModel<bool &>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
TypeBuilderFunc f{getModel<bool>()};
return fir::ReferenceType::get(f(context));
};
}
template <>
constexpr TypeBuilderFunc getModel<std::complex<float> &>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
auto ty = mlir::ComplexType::get(mlir::FloatType::getF32(context));
return fir::ReferenceType::get(ty);
};
}
template <>
constexpr TypeBuilderFunc getModel<std::complex<double> &>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
auto ty = mlir::ComplexType::get(mlir::FloatType::getF64(context));
return fir::ReferenceType::get(ty);
};
}
template <>
constexpr TypeBuilderFunc getModel<c_float_complex_t>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return fir::ComplexType::get(context, sizeof(float));
};
}
template <>
constexpr TypeBuilderFunc getModel<c_double_complex_t>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return fir::ComplexType::get(context, sizeof(double));
};
}
template <>
constexpr TypeBuilderFunc getModel<const Fortran::runtime::Descriptor &>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return fir::BoxType::get(mlir::NoneType::get(context));
};
}
template <>
constexpr TypeBuilderFunc getModel<Fortran::runtime::Descriptor &>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return fir::ReferenceType::get(
fir::BoxType::get(mlir::NoneType::get(context)));
};
}
template <>
constexpr TypeBuilderFunc getModel<const Fortran::runtime::Descriptor *>() {
return getModel<const Fortran::runtime::Descriptor &>();
}
template <>
constexpr TypeBuilderFunc getModel<Fortran::runtime::Descriptor *>() {
return getModel<Fortran::runtime::Descriptor &>();
}
template <>
constexpr TypeBuilderFunc getModel<Fortran::common::TypeCategory>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::IntegerType::get(context,
sizeof(Fortran::common::TypeCategory) * 8);
};
}
template <>
constexpr TypeBuilderFunc getModel<void>() {
return [](mlir::MLIRContext *context) -> mlir::Type {
return mlir::NoneType::get(context);
};
}
template <typename...>
struct RuntimeTableKey;
template <typename RT, typename... ATs>
struct RuntimeTableKey<RT(ATs...)> {
static constexpr FuncTypeBuilderFunc getTypeModel() {
return [](mlir::MLIRContext *ctxt) {
TypeBuilderFunc ret = getModel<RT>();
std::array<TypeBuilderFunc, sizeof...(ATs)> args = {getModel<ATs>()...};
mlir::Type retTy = ret(ctxt);
llvm::SmallVector<mlir::Type, sizeof...(ATs)> argTys;
for (auto f : args)
argTys.push_back(f(ctxt));
return mlir::FunctionType::get(ctxt, argTys, {retTy});
};
}
};
//===----------------------------------------------------------------------===//
// Runtime table building (constexpr folded)
//===----------------------------------------------------------------------===//
template <char... Cs>
using RuntimeIdentifier = std::integer_sequence<char, Cs...>;
namespace details {
template <typename T, T... As, T... Bs>
static constexpr std::integer_sequence<T, As..., Bs...>
concat(std::integer_sequence<T, As...>, std::integer_sequence<T, Bs...>) {
return {};
}
template <typename T, T... As, T... Bs, typename... Cs>
static constexpr auto concat(std::integer_sequence<T, As...>,
std::integer_sequence<T, Bs...>, Cs...) {
return concat(std::integer_sequence<T, As..., Bs...>{}, Cs{}...);
}
template <typename T>
static constexpr std::integer_sequence<T> concat(std::integer_sequence<T>) {
return {};
}
template <typename T, T a>
static constexpr auto filterZero(std::integer_sequence<T, a>) {
if constexpr (a != 0) {
return std::integer_sequence<T, a>{};
} else {
return std::integer_sequence<T>{};
}
}
template <typename T, T... b>
static constexpr auto filter(std::integer_sequence<T, b...>) {
if constexpr (sizeof...(b) > 0) {
return details::concat(filterZero(std::integer_sequence<T, b>{})...);
} else {
return std::integer_sequence<T>{};
}
}
} // namespace details
template <typename...>
struct RuntimeTableEntry;
template <typename KT, char... Cs>
struct RuntimeTableEntry<RuntimeTableKey<KT>, RuntimeIdentifier<Cs...>> {
static constexpr FuncTypeBuilderFunc getTypeModel() {
return RuntimeTableKey<KT>::getTypeModel();
}
static constexpr const char name[sizeof...(Cs) + 1] = {Cs..., '\0'};
};
/// These macros are used to create the RuntimeTableEntry for runtime function.
///
/// For example the runtime function `SumReal4` will be expanded as shown below
/// (simplified version)
///
/// ```
/// fir::runtime::RuntimeTableEntry<fir::runtime::RuntimeTableKey<
/// decltype(_FortranASumReal4)>, "_FortranASumReal4"))>
/// ```
/// These entries are then used to to generate the MLIR FunctionType that
/// correspond to the runtime function declaration in C++.
#undef FirE
#define FirE(L, I) (I < sizeof(L) / sizeof(*L) ? L[I] : 0)
#define FirQuoteKey(X) #X
#define FirMacroExpandKey(X) \
FirE(X, 0), FirE(X, 1), FirE(X, 2), FirE(X, 3), FirE(X, 4), FirE(X, 5), \
FirE(X, 6), FirE(X, 7), FirE(X, 8), FirE(X, 9), FirE(X, 10), \
FirE(X, 11), FirE(X, 12), FirE(X, 13), FirE(X, 14), FirE(X, 15), \
FirE(X, 16), FirE(X, 17), FirE(X, 18), FirE(X, 19), FirE(X, 20), \
FirE(X, 21), FirE(X, 22), FirE(X, 23), FirE(X, 24), FirE(X, 25), \
FirE(X, 26), FirE(X, 27), FirE(X, 28), FirE(X, 29), FirE(X, 30), \
FirE(X, 31), FirE(X, 32), FirE(X, 33), FirE(X, 34), FirE(X, 35), \
FirE(X, 36), FirE(X, 37), FirE(X, 38), FirE(X, 39), FirE(X, 40), \
FirE(X, 41), FirE(X, 42), FirE(X, 43), FirE(X, 44), FirE(X, 45), \
FirE(X, 46), FirE(X, 47), FirE(X, 48), FirE(X, 49)
#define FirExpandKey(X) FirMacroExpandKey(FirQuoteKey(X))
#define FirFullSeq(X) std::integer_sequence<char, FirExpandKey(X)>
#define FirAsSequence(X) \
decltype(fir::runtime::details::filter(FirFullSeq(X){}))
#define FirmkKey(X) \
fir::runtime::RuntimeTableEntry<fir::runtime::RuntimeTableKey<decltype(X)>, \
FirAsSequence(X)>
#define mkRTKey(X) FirmkKey(RTNAME(X))
/// Get (or generate) the MLIR FuncOp for a given runtime function. Its template
/// argument is intended to be of the form: <mkRTKey(runtime function name)>.
template <typename RuntimeEntry>
static mlir::FuncOp getRuntimeFunc(mlir::Location loc,
fir::FirOpBuilder &builder) {
using namespace Fortran::runtime;
auto name = RuntimeEntry::name;
auto func = builder.getNamedFunction(name);
if (func)
return func;
auto funTy = RuntimeEntry::getTypeModel()(builder.getContext());
func = builder.createFunction(loc, name, funTy);
func->setAttr("fir.runtime", builder.getUnitAttr());
return func;
}
namespace helper {
template <int N, typename A>
void createArguments(llvm::SmallVectorImpl<mlir::Value> &result,
fir::FirOpBuilder &builder, mlir::Location loc,
mlir::FunctionType fTy, A arg) {
result.emplace_back(builder.createConvert(loc, fTy.getInput(N), arg));
}
template <int N, typename A, typename... As>
void createArguments(llvm::SmallVectorImpl<mlir::Value> &result,
fir::FirOpBuilder &builder, mlir::Location loc,
mlir::FunctionType fTy, A arg, As... args) {
result.emplace_back(builder.createConvert(loc, fTy.getInput(N), arg));
createArguments<N + 1>(result, builder, loc, fTy, args...);
}
} // namespace helper
/// Create a SmallVector of arguments for a runtime call.
template <typename... As>
llvm::SmallVector<mlir::Value>
createArguments(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::FunctionType fTy, As... args) {
llvm::SmallVector<mlir::Value> result;
helper::createArguments<0>(result, builder, loc, fTy, args...);
return result;
}
} // namespace fir::runtime
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_RTBUILDER_H