[flang] Add a utility to safely interact with host types
authorJean Perier <jperier@hsw1.pgi.net>
Fri, 1 Feb 2019 16:28:21 +0000 (08:28 -0800)
committerGitHub <noreply@github.com>
Wed, 27 Mar 2019 17:16:07 +0000 (10:16 -0700)
Original-commit: flang-compiler/f18@32cd789dc2036e888a23e146ff5b251827a1c0c4
Tree-same-pre-rewrite: false

flang/lib/evaluate/host.h [new file with mode: 0644]

diff --git a/flang/lib/evaluate/host.h b/flang/lib/evaluate/host.h
new file mode 100644 (file)
index 0000000..46f833a
--- /dev/null
@@ -0,0 +1,196 @@
+// Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef FORTRAN_EVALUATE_HOST_H_
+#define FORTRAN_EVALUATE_HOST_H_
+
+// Define a compile-time mapping between Fortran intrinsic types and host
+// hardware types if possible. The purpose is to avoid having to do any kind of
+// assumption on whether a "float" matches the Scalar<Type<TypeCategory::Real,
+// 4>> outside of this header. The main tools are HostTypeExists<T> and
+// HostType<T>. HostTypeExists<T>() will return true if and only if a host
+// hardware type maps to Fortran intrinsic type T. Then HostType<T> can be used
+// to safely refer to this hardware type.
+
+#include "type.h"
+#include <complex>
+#include <cstdint>
+#include <limits>
+#include <string>
+#include <type_traits>
+
+namespace Fortran::evaluate {
+namespace Host {
+
+struct UnsupportedType {};
+
+template<typename FTN_T> struct HostTypeHelper {
+  using Type = UnsupportedType;
+};
+template<typename FTN_T> using HostType = typename HostTypeHelper<FTN_T>::Type;
+
+template<typename... T> constexpr inline bool HostTypeExists() {
+  return (... && (!std::is_same_v<HostType<T>, UnsupportedType>));
+}
+
+template<typename FTN_T>
+inline constexpr Scalar<FTN_T> CastHostToFortran(const HostType<FTN_T> &x) {
+  if constexpr (FTN_T::category == TypeCategory::Complex &&
+      sizeof(Scalar<FTN_T>) != sizeof(HostType<FTN_T>)) {
+    // X87 is usually padded to 12 or 16bytes. Need to cast piecewise for
+    // complex
+    return Scalar<FTN_T>{CastHostToFortran<typename FTN_T::Part>(std::real(x)),
+        CastHostToFortran<typename FTN_T::Part>(std::imag(x))};
+  } else {
+    return *reinterpret_cast<const Scalar<FTN_T> *>(&x);
+  }
+}
+
+template<typename FTN_T>
+inline constexpr HostType<FTN_T> CastFortranToHost(const Scalar<FTN_T> &x) {
+  if constexpr (FTN_T::category == TypeCategory::Complex &&
+      sizeof(Scalar<FTN_T>) != sizeof(HostType<FTN_T>)) {
+    // X87 is usually padded to 12 or 16bytes. Need to cast piecewise for
+    // complex
+    return HostType<FTN_T>{CastFortranToHost<typename FTN_T::Part>(x.REAL()),
+        CastFortranToHost<typename FTN_T::Part>(x.AIMAG())};
+  } else {
+    return *reinterpret_cast<const HostType<FTN_T> *>(&x);
+  }
+}
+
+template<typename T> struct BiggerOrSameHostTypeHelper {
+  using Type =
+      std::conditional_t<HostTypeExists<T>(), HostType<T>, UnsupportedType>;
+  using FortranType = T;
+};
+
+template<typename FTN_T>
+using BiggerOrSameHostType = typename BiggerOrSameHostTypeHelper<FTN_T>::Type;
+template<typename FTN_T>
+using BiggerOrSameFortanTypeSupportedOnHost =
+    typename BiggerOrSameHostTypeHelper<FTN_T>::FortranType;
+
+template<typename... T> constexpr inline bool BiggerOrSameHostTypeExists() {
+  return (... && (!std::is_same_v<BiggerOrSameHostType<T>, UnsupportedType>));
+}
+
+// Defining the actual mapping
+template<> struct HostTypeHelper<Type<TypeCategory::Integer, 1>> {
+  using Type = std::int8_t;
+};
+
+template<> struct HostTypeHelper<Type<TypeCategory::Integer, 2>> {
+  using Type = std::int16_t;
+};
+
+template<> struct HostTypeHelper<Type<TypeCategory::Integer, 4>> {
+  using Type = std::int32_t;
+};
+
+template<> struct HostTypeHelper<Type<TypeCategory::Integer, 8>> {
+  using Type = std::int64_t;
+};
+
+// no int 128bit
+
+// no float 16bits
+
+template<> struct HostTypeHelper<Type<TypeCategory::Real, 4>> {
+  // IEE 754 64bits
+  using Type = std::conditional_t<sizeof(float) == 4 &&
+          std::numeric_limits<float>::is_iec559,
+      float, UnsupportedType>;
+};
+
+template<> struct HostTypeHelper<Type<TypeCategory::Real, 8>> {
+  // IEE 754 64bits
+  using Type = std::conditional_t<sizeof(double) == 8 &&
+          std::numeric_limits<double>::is_iec559,
+      double, UnsupportedType>;
+};
+
+template<> struct HostTypeHelper<Type<TypeCategory::Real, 10>> {
+  // X87 80bits
+  using Type = std::conditional_t<sizeof(long double) >= 10 &&
+          std::numeric_limits<long double>::digits == 64 &&
+          std::numeric_limits<long double>::max_exponent == 16384,
+      long double, UnsupportedType>;
+};
+
+template<> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
+  // IEE 754 128bits
+  using Type = std::conditional_t<sizeof(long double) == 16 &&
+          std::numeric_limits<long double>::digits == 113 &&
+          std::numeric_limits<long double>::max_exponent == 16384,
+      long double, UnsupportedType>;
+};
+
+template<int KIND> struct HostTypeHelper<Type<TypeCategory::Complex, KIND>> {
+  using RealT = Fortran::evaluate::Type<TypeCategory::Real, KIND>;
+  using Type = std::conditional_t<HostTypeExists<RealT>(),
+      std::complex<HostType<RealT>>, UnsupportedType>;
+};
+
+template<int KIND> struct HostTypeHelper<Type<TypeCategory::Character, KIND>> {
+  using Type = std::conditional_t<KIND <= 8, std::uint8_t, UnsupportedType>;
+};
+
+template<> struct HostTypeHelper<Type<TypeCategory::Character, 1>> {
+  using Type = std::string;
+};
+
+template<> struct HostTypeHelper<Type<TypeCategory::Character, 2>> {
+  using Type = std::u16string;
+};
+
+// Utility to find "bigger" types that exist on host. By bigger, it is meant
+// that the bigger type can represent all the values of the smaller types without
+// information loss.
+template<TypeCategory cat, int KIND> struct NextBiggerReal {
+  using Type = void;
+};
+template<TypeCategory cat> struct NextBiggerReal<cat, 2> {
+  using Type = Fortran::evaluate::Type<cat, 3>;
+};
+template<TypeCategory cat> struct NextBiggerReal<cat, 3> {
+  using Type = Fortran::evaluate::Type<cat, 4>;
+};
+template<TypeCategory cat> struct NextBiggerReal<cat, 4> {
+  using Type = Fortran::evaluate::Type<cat, 8>;
+};
+
+template<int KIND>
+struct BiggerOrSameHostTypeHelper<Type<TypeCategory::Real, KIND>> {
+  using T = Fortran::evaluate::Type<TypeCategory::Real, KIND>;
+  using NextT = typename NextBiggerReal<TypeCategory::Real, KIND>::Type;
+  using Type = std::conditional_t<HostTypeExists<T>(), HostType<T>,
+      typename BiggerOrSameHostTypeHelper<NextT>::Type>;
+  using FortranType = std::conditional_t<HostTypeExists<T>(), T,
+      typename BiggerOrSameHostTypeHelper<NextT>::FortranType>;
+};
+
+template<int KIND>
+struct BiggerOrSameHostTypeHelper<Type<TypeCategory::Complex, KIND>> {
+  using T = Fortran::evaluate::Type<TypeCategory::Complex, KIND>;
+  using NextT = typename NextBiggerReal<TypeCategory::Complex, KIND>::Type;
+  using Type = std::conditional_t<HostTypeExists<T>(), HostType<T>,
+      typename BiggerOrSameHostTypeHelper<NextT>::Type>;
+  using FortranType = std::conditional_t<HostTypeExists<T>(), T,
+      typename BiggerOrSameHostTypeHelper<NextT>::FortranType>;
+};
+}
+}
+
+#endif  // FORTRAN_EVALUATE_HOST_H_