[flang-commits] [flang] [llvm] [flang-rt] Remove dependency on flang and implement iso file in C++ (PR #197687)
Joseph Huber via flang-commits
flang-commits at lists.llvm.org
Sat May 16 15:31:34 PDT 2026
https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/197687
>From 7e9c0237293857df26847425284cd54108bc091b Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Thu, 14 May 2026 08:56:32 -0500
Subject: [PATCH] [flang-rt] Remove dependency on flang and implement iso file
in C++
Summary:
Right now we have a very heavy dependency on `flang` for a single module
file. The flang build will handle creating the actual module file, we
just need to export the symbols. This PR ports the source file from the
flang .f90 to equivalent C++. This allows us to build `flang-rt`
standalone without bringing in the entire flang infrastructure, which is
by far the heaviest build in the entire LLVM project.
I verified the symbols and values against a previous build, so they are
identical.
---
flang-rt/lib/runtime/CMakeLists.txt | 2 +-
flang-rt/lib/runtime/iso_fortran_env_impl.cpp | 303 ++++++++++++++++++
flang/include/flang/Common/type-kinds.h | 41 ++-
flang/module/iso_fortran_env_impl.f90 | 5 +-
4 files changed, 340 insertions(+), 11 deletions(-)
create mode 100644 flang-rt/lib/runtime/iso_fortran_env_impl.cpp
diff --git a/flang-rt/lib/runtime/CMakeLists.txt b/flang-rt/lib/runtime/CMakeLists.txt
index cc5d88e985329..b297b5c556b18 100644
--- a/flang-rt/lib/runtime/CMakeLists.txt
+++ b/flang-rt/lib/runtime/CMakeLists.txt
@@ -75,7 +75,7 @@ set(supported_sources
# List of source not used for GPU offloading.
set(host_sources
- ${FLANG_SOURCE_DIR}/module/iso_fortran_env_impl.f90
+ iso_fortran_env_impl.cpp
command.cpp
complex-powi.cpp
complex-reduction.c
diff --git a/flang-rt/lib/runtime/iso_fortran_env_impl.cpp b/flang-rt/lib/runtime/iso_fortran_env_impl.cpp
new file mode 100644
index 0000000000000..21ab9184dab68
--- /dev/null
+++ b/flang-rt/lib/runtime/iso_fortran_env_impl.cpp
@@ -0,0 +1,303 @@
+//===-- lib/runtime/iso_fortran_env_impl.cpp --------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Provides linkable symbols for the iso_fortran_env_impl module constants. It
+// replaces the Fortran source (flang/module/iso_fortran_env_impl.f90) to remove
+// the runtime library's build dependency on the Fortran compiler.
+//
+// The values here must stay in sync with the Fortran module source which
+// generates the .mod file during the flang compiler build.
+//
+// Symbol naming follows Flang's module variable mangling:
+// _QM<module_name>EC<constant_name>
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Common/float128.h"
+#include "flang/Common/real.h"
+#include "flang/Common/type-kinds.h"
+#include <cstdint>
+
+// Real kind 10 (x87 extended) is only available on x86-64.
+#if defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
+#define FLANG_RT_HAS_REAL_10 1
+#else
+#define FLANG_RT_HAS_REAL_10 0
+#endif
+
+// Real kind 16 (IEEE quad) requires a float128 runtime math library.
+// Must match the logic in flang/include/flang/Tools/TargetSetup.h.
+#if defined(FLANG_RUNTIME_F128_MATH_LIB) || HAS_LDBL128
+#define FLANG_RT_HAS_REAL_16 1
+#else
+#define FLANG_RT_HAS_REAL_16 0
+#endif
+
+// Fortran merge(tsource, fsource, mask) to match the source module style.
+static constexpr std::int32_t Merge(
+ std::int32_t tsource, std::int32_t fsource, bool mask) {
+ return mask ? tsource : fsource;
+}
+
+// Fortran selected_int_kind(R) returns the smallest integer kind with range >=
+// R digits. Flang integer kinds and their decimal ranges are fixed.
+static constexpr std::int32_t SelectedIntKind(int range) {
+ struct {
+ int kind, range;
+ } constexpr intKinds[]{{1, 2}, {2, 4}, {4, 9}, {8, 18}, {16, 38}};
+ for (auto [k, r] : intKinds) {
+ if (r >= range)
+ return k;
+ }
+ return -1;
+}
+
+// Fortran selected_real_kind(P, R) returns the smallest real kind with decimal
+// precision >= P and decimal range >= R. Returns negative per Fortran
+// 2023 16.9.170 when no kind qualifies.
+static constexpr std::int32_t SelectedRealKind(int p, int r) {
+ constexpr int realKinds[]{
+ 2,
+ 3,
+ 4,
+ 8,
+#if FLANG_RT_HAS_REAL_10
+ 10,
+#endif
+#if FLANG_RT_HAS_REAL_16
+ 16,
+#endif
+ };
+ for (int k : realKinds) {
+ Fortran::common::RealCharacteristics rc{
+ Fortran::common::PrecisionOfRealKind(k)};
+ if (rc.decimalPrecision >= p && rc.decimalRange >= r)
+ return k;
+ }
+ bool pOK{false}, rOK{false};
+ for (int k : realKinds) {
+ Fortran::common::RealCharacteristics rc{
+ Fortran::common::PrecisionOfRealKind(k)};
+ if (rc.decimalPrecision >= p)
+ pOK = true;
+ if (rc.decimalRange >= r)
+ rOK = true;
+ }
+ if (pOK)
+ return rOK ? -4 : -2;
+ return rOK ? -1 : -3;
+}
+
+// Fortran digits() for integer kind K returns 8*K - 1 (excludes sign bit).
+static constexpr int IntDigits(int kind) { return 8 * kind - 1; }
+
+// Fortran digits() for unsigned kind K returns 8*K (no sign bit).
+static constexpr int UintDigits(int kind) { return 8 * kind; }
+
+// Fortran digits() for real kind K returns the binary precision.
+static constexpr int RealDigits(int kind) {
+ return Fortran::common::PrecisionOfRealKind(kind);
+}
+
+// The smallest valid real kind, used as a safe fallback when a selected
+// real kind is unavailable (so digits() can be called without error).
+static constexpr std::int32_t safeRealFallback{SelectedRealKind(0, 0)};
+
+// Integer kinds, selected -> safe -> validated.
+static constexpr std::int32_t selectedInt8{SelectedIntKind(2)};
+static constexpr std::int32_t selectedInt16{SelectedIntKind(4)};
+static constexpr std::int32_t selectedInt32{SelectedIntKind(9)};
+static constexpr std::int32_t selectedInt64{SelectedIntKind(18)};
+static constexpr std::int32_t selectedInt128{SelectedIntKind(38)};
+
+static constexpr std::int32_t safeInt8{
+ Merge(selectedInt8, SelectedIntKind(0), selectedInt8 >= 0)};
+static constexpr std::int32_t safeInt16{
+ Merge(selectedInt16, SelectedIntKind(0), selectedInt16 >= 0)};
+static constexpr std::int32_t safeInt32{
+ Merge(selectedInt32, SelectedIntKind(0), selectedInt32 >= 0)};
+static constexpr std::int32_t safeInt64{
+ Merge(selectedInt64, SelectedIntKind(0), selectedInt64 >= 0)};
+static constexpr std::int32_t safeInt128{
+ Merge(selectedInt128, SelectedIntKind(0), selectedInt128 >= 0)};
+
+static constexpr std::int32_t int8{Merge(
+ selectedInt8, Merge(-2, -1, selectedInt8 >= 0), IntDigits(safeInt8) == 7)};
+static constexpr std::int32_t int16{Merge(selectedInt16,
+ Merge(-2, -1, selectedInt16 >= 0), IntDigits(safeInt16) == 15)};
+static constexpr std::int32_t int32{Merge(selectedInt32,
+ Merge(-2, -1, selectedInt32 >= 0), IntDigits(safeInt32) == 31)};
+static constexpr std::int32_t int64{Merge(selectedInt64,
+ Merge(-2, -1, selectedInt64 >= 0), IntDigits(safeInt64) == 63)};
+static constexpr std::int32_t int128{Merge(selectedInt128,
+ Merge(-2, -1, selectedInt128 >= 0), IntDigits(safeInt128) == 127)};
+
+// Unsigned kinds, same selection as integer, validated with unsigned digits.
+static constexpr std::int32_t selectedUInt8{selectedInt8};
+static constexpr std::int32_t selectedUInt16{selectedInt16};
+static constexpr std::int32_t selectedUInt32{selectedInt32};
+static constexpr std::int32_t selectedUInt64{selectedInt64};
+static constexpr std::int32_t selectedUInt128{selectedInt128};
+
+static constexpr std::int32_t safeUInt8{safeInt8};
+static constexpr std::int32_t safeUInt16{safeInt16};
+static constexpr std::int32_t safeUInt32{safeInt32};
+static constexpr std::int32_t safeUInt64{safeInt64};
+static constexpr std::int32_t safeUInt128{safeInt128};
+
+static constexpr std::int32_t uint8{Merge(selectedUInt8,
+ Merge(-2, -1, selectedUInt8 >= 0), UintDigits(safeUInt8) == 8)};
+static constexpr std::int32_t uint16{Merge(selectedUInt16,
+ Merge(-2, -1, selectedUInt16 >= 0), UintDigits(safeUInt16) == 16)};
+static constexpr std::int32_t uint32{Merge(selectedUInt32,
+ Merge(-2, -1, selectedUInt32 >= 0), UintDigits(safeUInt32) == 32)};
+static constexpr std::int32_t uint64{Merge(selectedUInt64,
+ Merge(-2, -1, selectedUInt64 >= 0), UintDigits(safeUInt64) == 64)};
+static constexpr std::int32_t uint128{Merge(selectedUInt128,
+ Merge(-2, -1, selectedUInt128 >= 0), UintDigits(safeUInt128) == 128)};
+
+// Logical kinds mirror integer kinds.
+static constexpr std::int32_t logical8{int8};
+static constexpr std::int32_t logical16{int16};
+static constexpr std::int32_t logical32{int32};
+static constexpr std::int32_t logical64{int64};
+
+// Real kinds, selected -> safe -> validated.
+static constexpr std::int32_t selectedReal16{SelectedRealKind(3, 4)};
+static constexpr std::int32_t selectedBfloat16{SelectedRealKind(2, 37)};
+static constexpr std::int32_t selectedReal32{SelectedRealKind(6, 37)};
+static constexpr std::int32_t selectedReal64{SelectedRealKind(15, 307)};
+static constexpr std::int32_t selectedReal80{SelectedRealKind(18, 4931)};
+static constexpr std::int32_t selectedReal64x2{SelectedRealKind(31, 307)};
+static constexpr std::int32_t selectedReal128{SelectedRealKind(33, 4931)};
+
+static constexpr std::int32_t safeReal16{
+ Merge(selectedReal16, safeRealFallback, selectedReal16 >= 0)};
+static constexpr std::int32_t safeBfloat16{
+ Merge(selectedBfloat16, safeRealFallback, selectedBfloat16 >= 0)};
+static constexpr std::int32_t safeReal32{
+ Merge(selectedReal32, safeRealFallback, selectedReal32 >= 0)};
+static constexpr std::int32_t safeReal64{
+ Merge(selectedReal64, safeRealFallback, selectedReal64 >= 0)};
+static constexpr std::int32_t safeReal80{
+ Merge(selectedReal80, safeRealFallback, selectedReal80 >= 0)};
+static constexpr std::int32_t safeReal64x2{
+ Merge(selectedReal64x2, safeRealFallback, selectedReal64x2 >= 0)};
+static constexpr std::int32_t safeReal128{
+ Merge(selectedReal128, safeRealFallback, selectedReal128 >= 0)};
+
+static constexpr std::int32_t real16{Merge(selectedReal16,
+ Merge(-2, -1, selectedReal16 >= 0), RealDigits(safeReal16) == 11)};
+static constexpr std::int32_t bfloat16{Merge(selectedBfloat16,
+ Merge(-2, -1, selectedBfloat16 >= 0), RealDigits(safeBfloat16) == 8)};
+static constexpr std::int32_t real32{Merge(selectedReal32,
+ Merge(-2, -1, selectedReal32 >= 0), RealDigits(safeReal32) == 24)};
+static constexpr std::int32_t real64{Merge(selectedReal64,
+ Merge(-2, -1, selectedReal64 >= 0), RealDigits(safeReal64) == 53)};
+static constexpr std::int32_t real80{Merge(selectedReal80,
+ Merge(-2, -1, selectedReal80 >= 0), RealDigits(safeReal80) == 64)};
+static constexpr std::int32_t real64x2{Merge(selectedReal64x2,
+ Merge(-2, -1, selectedReal64x2 >= 0), RealDigits(safeReal64x2) == 106)};
+static constexpr std::int32_t real128{Merge(selectedReal128,
+ Merge(-2, -1, selectedReal128 >= 0), RealDigits(safeReal128) == 113)};
+
+// Exported symbols with Flang module-variable mangling.
+#define FORTRAN_NAMED_CONST(name) _QMiso_fortran_env_implEC##name
+
+extern "C" {
+
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedint8){selectedInt8};
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedint16){selectedInt16};
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedint32){selectedInt32};
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedint64){selectedInt64};
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedint128){selectedInt128};
+
+extern const std::int32_t FORTRAN_NAMED_CONST(safeint8){safeInt8};
+extern const std::int32_t FORTRAN_NAMED_CONST(safeint16){safeInt16};
+extern const std::int32_t FORTRAN_NAMED_CONST(safeint32){safeInt32};
+extern const std::int32_t FORTRAN_NAMED_CONST(safeint64){safeInt64};
+extern const std::int32_t FORTRAN_NAMED_CONST(safeint128){safeInt128};
+
+extern const std::int32_t FORTRAN_NAMED_CONST(int8){int8};
+extern const std::int32_t FORTRAN_NAMED_CONST(int16){int16};
+extern const std::int32_t FORTRAN_NAMED_CONST(int32){int32};
+extern const std::int32_t FORTRAN_NAMED_CONST(int64){int64};
+extern const std::int32_t FORTRAN_NAMED_CONST(int128){int128};
+
+extern const std::int32_t FORTRAN_NAMED_CONST(selecteduint8){selectedUInt8};
+extern const std::int32_t FORTRAN_NAMED_CONST(selecteduint16){selectedUInt16};
+extern const std::int32_t FORTRAN_NAMED_CONST(selecteduint32){selectedUInt32};
+extern const std::int32_t FORTRAN_NAMED_CONST(selecteduint64){selectedUInt64};
+extern const std::int32_t FORTRAN_NAMED_CONST(selecteduint128){selectedUInt128};
+
+extern const std::int32_t FORTRAN_NAMED_CONST(safeuint8){safeUInt8};
+extern const std::int32_t FORTRAN_NAMED_CONST(safeuint16){safeUInt16};
+extern const std::int32_t FORTRAN_NAMED_CONST(safeuint32){safeUInt32};
+extern const std::int32_t FORTRAN_NAMED_CONST(safeuint64){safeUInt64};
+extern const std::int32_t FORTRAN_NAMED_CONST(safeuint128){safeUInt128};
+
+extern const std::int32_t FORTRAN_NAMED_CONST(uint8){uint8};
+extern const std::int32_t FORTRAN_NAMED_CONST(uint16){uint16};
+extern const std::int32_t FORTRAN_NAMED_CONST(uint32){uint32};
+extern const std::int32_t FORTRAN_NAMED_CONST(uint64){uint64};
+extern const std::int32_t FORTRAN_NAMED_CONST(uint128){uint128};
+
+extern const std::int32_t FORTRAN_NAMED_CONST(logical8){logical8};
+extern const std::int32_t FORTRAN_NAMED_CONST(logical16){logical16};
+extern const std::int32_t FORTRAN_NAMED_CONST(logical32){logical32};
+extern const std::int32_t FORTRAN_NAMED_CONST(logical64){logical64};
+
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedreal16){selectedReal16};
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedbfloat16){
+ selectedBfloat16};
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedreal32){selectedReal32};
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedreal64){selectedReal64};
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedreal80){selectedReal80};
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedreal64x2){
+ selectedReal64x2};
+extern const std::int32_t FORTRAN_NAMED_CONST(selectedreal128){selectedReal128};
+
+extern const std::int32_t FORTRAN_NAMED_CONST(safereal16){safeReal16};
+extern const std::int32_t FORTRAN_NAMED_CONST(safebfloat16){safeBfloat16};
+extern const std::int32_t FORTRAN_NAMED_CONST(safereal32){safeReal32};
+extern const std::int32_t FORTRAN_NAMED_CONST(safereal64){safeReal64};
+extern const std::int32_t FORTRAN_NAMED_CONST(safereal80){safeReal80};
+extern const std::int32_t FORTRAN_NAMED_CONST(safereal64x2){safeReal64x2};
+extern const std::int32_t FORTRAN_NAMED_CONST(safereal128){safeReal128};
+
+extern const std::int32_t FORTRAN_NAMED_CONST(real16){real16};
+extern const std::int32_t FORTRAN_NAMED_CONST(bfloat16){bfloat16};
+extern const std::int32_t FORTRAN_NAMED_CONST(real32){real32};
+extern const std::int32_t FORTRAN_NAMED_CONST(real64){real64};
+extern const std::int32_t FORTRAN_NAMED_CONST(real80){real80};
+extern const std::int32_t FORTRAN_NAMED_CONST(real64x2){real64x2};
+extern const std::int32_t FORTRAN_NAMED_CONST(real128){real128};
+
+extern const std::int32_t FORTRAN_NAMED_CONST(
+ __builtin_integer_kinds)[] = FORTRAN_INTEGER_KINDS;
+
+extern const std::int32_t FORTRAN_NAMED_CONST(
+ __builtin_logical_kinds)[] = FORTRAN_LOGICAL_KINDS;
+
+// Target-filtered subset of FORTRAN_REAL_KINDS from type-kinds.h. We cannot
+// simply reference it due to variable runtime support, so we statically assert.
+static_assert(sizeof((int[])FORTRAN_REAL_KINDS) / sizeof(int) == 6);
+extern const std::int32_t FORTRAN_NAMED_CONST(__builtin_real_kinds)[]{
+ 2,
+ 3,
+ 4,
+ 8,
+#if FLANG_RT_HAS_REAL_10
+ 10,
+#endif
+#if FLANG_RT_HAS_REAL_16
+ 16,
+#endif
+};
+
+} // extern "C"
diff --git a/flang/include/flang/Common/type-kinds.h b/flang/include/flang/Common/type-kinds.h
index 4e5c4f69fcc67..dd100b9dcd17e 100644
--- a/flang/include/flang/Common/type-kinds.h
+++ b/flang/include/flang/Common/type-kinds.h
@@ -13,10 +13,26 @@
#include "real.h"
#include <cinttypes>
+// Canonical lists of supported Fortran kinds for each intrinsic type.
+#define FORTRAN_INTEGER_KINDS {1, 2, 4, 8, 16}
+#define FORTRAN_UNSIGNED_KINDS FORTRAN_INTEGER_KINDS
+#define FORTRAN_REAL_KINDS {2, 3, 4, 8, 10, 16}
+#define FORTRAN_LOGICAL_KINDS {1, 2, 4, 8}
+#define FORTRAN_CHARACTER_KINDS {1, 2, 4}
+
namespace Fortran::common {
static constexpr int maxKind{16};
+template <typename T, std::size_t N>
+static constexpr bool IsKindInList(const T (&kinds)[N], std::int64_t kind) {
+ for (std::size_t i{0}; i < N; ++i) {
+ if (kinds[i] == kind)
+ return true;
+ }
+ return false;
+}
+
// A predicate that is true when a kind value is a kind that could possibly
// be supported for an intrinsic type category on some target instruction
// set architecture.
@@ -24,16 +40,23 @@ static constexpr bool IsValidKindOfIntrinsicType(
TypeCategory category, std::int64_t kind) {
switch (category) {
case TypeCategory::Integer:
- case TypeCategory::Unsigned:
- return kind == 1 || kind == 2 || kind == 4 || kind == 8 || kind == 16;
+ case TypeCategory::Unsigned: {
+ constexpr int kinds[] = FORTRAN_INTEGER_KINDS;
+ return IsKindInList(kinds, kind);
+ }
case TypeCategory::Real:
- case TypeCategory::Complex:
- return kind == 2 || kind == 3 || kind == 4 || kind == 8 || kind == 10 ||
- kind == 16;
- case TypeCategory::Character:
- return kind == 1 || kind == 2 || kind == 4;
- case TypeCategory::Logical:
- return kind == 1 || kind == 2 || kind == 4 || kind == 8;
+ case TypeCategory::Complex: {
+ constexpr int kinds[] = FORTRAN_REAL_KINDS;
+ return IsKindInList(kinds, kind);
+ }
+ case TypeCategory::Character: {
+ constexpr int kinds[] = FORTRAN_CHARACTER_KINDS;
+ return IsKindInList(kinds, kind);
+ }
+ case TypeCategory::Logical: {
+ constexpr int kinds[] = FORTRAN_LOGICAL_KINDS;
+ return IsKindInList(kinds, kind);
+ }
default:
return false;
}
diff --git a/flang/module/iso_fortran_env_impl.f90 b/flang/module/iso_fortran_env_impl.f90
index 5408e7de37001..d0726e621bbd1 100644
--- a/flang/module/iso_fortran_env_impl.f90
+++ b/flang/module/iso_fortran_env_impl.f90
@@ -1,4 +1,4 @@
-!===-- module/iso_fortran_env_impl.f90 --=--------------------------------===!
+!===-- module/iso_fortran_env_impl.f90 --=----------------------------------===!
!
! Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
! See https://llvm.org/LICENSE.txt for license information.
@@ -9,6 +9,9 @@
! This MODULE implements part of the ISO_FORTRAN_ENV module file, which
! partially requires linkable symbols for some entities defined
! (e.g., real_kinds).
+!
+! The runtime symbols are in flang-rt/lib/runtime/iso_fortran_env_impl.cpp and
+! must be kept up-to-date.
module iso_fortran_env_impl
implicit none
More information about the flang-commits
mailing list