[flang-commits] [flang] [llvm] Flang improve fidelity of unformatted I/O endianness with FORT_CONVERT_UNIT (PR #223831)
David Parks via flang-commits
flang-commits at lists.llvm.org
Fri Sep 18 11:58:18 PDT 2026
https://github.com/d-parks updated https://github.com/llvm/llvm-project/pull/223831
>From 0a16a181b58c055e351ca4b5ed53c8b9822cdbff Mon Sep 17 00:00:00 2001
From: David Parks <djp at david-parks.com>
Date: Tue, 15 Sep 2026 14:35:55 -0600
Subject: [PATCH 1/3] Flang improve fidelity of unformatted I/O endianness with
FORT_CONVERT_UNIT
---
.../include/flang-rt/runtime/environment.h | 13 ++
flang-rt/lib/runtime/array.h | 11 +-
flang-rt/lib/runtime/environment.cpp | 161 +++++++++++++++++-
flang-rt/lib/runtime/external-unit.cpp | 18 ++
flang-rt/test/Runtime/fort_convert.f90 | 151 ++++++++++++++++
flang/docs/RuntimeEnvironment.md | 79 ++++++++-
6 files changed, 427 insertions(+), 6 deletions(-)
create mode 100644 flang-rt/test/Runtime/fort_convert.f90
diff --git a/flang-rt/include/flang-rt/runtime/environment.h b/flang-rt/include/flang-rt/runtime/environment.h
index 8ae441c12b3f8..bba75cc06f5b5 100644
--- a/flang-rt/include/flang-rt/runtime/environment.h
+++ b/flang-rt/include/flang-rt/runtime/environment.h
@@ -9,6 +9,7 @@
#ifndef FLANG_RT_RUNTIME_ENVIRONMENT_H_
#define FLANG_RT_RUNTIME_ENVIRONMENT_H_
+#include "../lib/runtime/array.h"
#include "flang/Common/optional.h"
#include "flang/Decimal/decimal.h"
#include "flang/Runtime/entry-names.h"
@@ -37,6 +38,14 @@ RT_API_ATTRS common::optional<Convert> GetConvertFromString(
struct ExecutionEnvironment {
+ // List of unit(s) from environment variable FORT_CONVERT_UNIT with specific
+ // conversion rules.
+ struct ConvertUnit {
+ Convert conversion;
+ std::int32_t startUnit;
+ std::int32_t endUnit;
+ };
+
typedef void (*ConfigEnvCallbackPtr)(
int, const char *[], const char *[], const EnvironmentDefaultList *);
@@ -58,6 +67,9 @@ struct ExecutionEnvironment {
std::int32_t UnsetEnv(
const char *name, std::size_t name_length, const Terminator &terminator);
+ bool ParseFortConvertUnit(const char *);
+ Convert UnitRtConvert(int);
+
int argc{0};
const char **argv{nullptr};
char **envp{nullptr};
@@ -66,6 +78,7 @@ struct ExecutionEnvironment {
enum decimal::FortranRounding defaultOutputRoundingMode{
decimal::FortranRounding::RoundNearest}; // RP(==PN)
Convert conversion{Convert::Unknown}; // FORT_CONVERT
+ DynamicArray<ConvertUnit> convertUnits; // FORT_CONVERT_UNIT
bool noStopMessage{false}; // NO_STOP_MESSAGE=1 inhibits "Fortran STOP"
// FLANG_TIMEF_IN_MILLISECONDS=1 sets TIMEF resolution to milliseconds.
// Default resolution is seconds.
diff --git a/flang-rt/lib/runtime/array.h b/flang-rt/lib/runtime/array.h
index 4b9bf4541ad7b..ab0d0e2eb3d26 100644
--- a/flang-rt/lib/runtime/array.h
+++ b/flang-rt/lib/runtime/array.h
@@ -15,11 +15,18 @@
namespace Fortran::runtime {
// A simple dynamic array that only supports appending to avoid std::vector.
template <typename T> struct DynamicArray {
- ~DynamicArray() {
+ ~DynamicArray() { clear(); }
+
+ // clear() - remove all elements from array and free allocated memory.
+ void clear() {
+ if (capacity_ == 0) {
+ return;
+ }
for (std::size_t i = 0; i < size_; ++i) {
data_[i].~T();
}
FreeMemory(data_);
+ size_ = capacity_ = 0;
}
void emplace_back(T &&value) {
@@ -47,6 +54,8 @@ template <typename T> struct DynamicArray {
T *begin() const { return data_; }
T *end() const { return data_ + size_; }
+ std::size_t size() const { return size_; }
+ bool empty() const { return 0 == size_; }
private:
T *data_ = nullptr;
diff --git a/flang-rt/lib/runtime/environment.cpp b/flang-rt/lib/runtime/environment.cpp
index ea98eb4634293..0663399a55f75 100644
--- a/flang-rt/lib/runtime/environment.cpp
+++ b/flang-rt/lib/runtime/environment.cpp
@@ -1,3 +1,4 @@
+#include <string_view>
//===-- lib/runtime/environment.cpp -----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -6,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#include "flang-rt/runtime/environment.h"
#include "environment-default-list.h"
+#include "flang-rt/runtime/environment.h"
#include "flang-rt/runtime/memory.h"
#include "flang-rt/runtime/tools.h"
#include <cstdio>
@@ -97,6 +98,157 @@ common::optional<Convert> GetConvertFromString(const char *x, std::size_t n) {
}
RT_OFFLOAD_API_GROUP_END
+bool ExecutionEnvironment::ParseFortConvertUnit(const char *cenvStr) {
+ bool success{true};
+ char *envStr{strdup(cenvStr)};
+
+ if (nullptr == envStr) {
+ Terminator{__FILE__, __LINE__}.Crash(
+ "FORT_CONVERT_UNIT: could not allocate memory");
+ }
+
+ char *exceptionSvptr{nullptr};
+ char *exceptionStr;
+ char *unitListStr;
+ bool firstException{true};
+ Convert gblConversion{conversion};
+
+ // Flang's FORT_CONVERT_UNIT syntax follows NVIDIA's FORT_CONVERT_UNIT and
+ // GNU's gfortran GFORTRAN_CONVERT_UNIT.
+ //
+ // FORT_CONVERT_UNIT: mode | mode ';' exception | exception ;
+ // mode: 'native' | 'swap' | 'big_endian' | 'little_endian' ;
+ // exception: mode ':' unit_list | unit_list ;
+ // unit_list: unit_spec | unit_list ',' unit_spec ;
+ // unit_spec: INTEGER | INTEGER '-' INTEGER ;
+
+ while (success) {
+#if _WIN32
+ exceptionStr =
+ strtok_s(exceptionSvptr ? nullptr : envStr, ";", &exceptionSvptr);
+#else
+ exceptionStr =
+ strtok_r(exceptionSvptr ? nullptr : envStr, ";", &exceptionSvptr);
+#endif
+ if (nullptr == exceptionStr) {
+ break;
+ }
+
+ // unitList is not yet correct, might be nullptr or pointing to ':'.
+ unitListStr = strchr(exceptionStr, ':');
+
+ if (firstException && (nullptr == unitListStr) &&
+ !isdigit(exceptionStr[0])) {
+ // if first pass extracting an exception, and exceptionStr does not
+ // contain a ':' this becomes the global setting.
+ // Akin to specifying FORT_CONVERT=<mode>.
+ if (auto convert{
+ GetConvertFromString(exceptionStr, std::strlen(exceptionStr))}) {
+ gblConversion = *convert;
+ } else {
+ success = false;
+ break;
+ }
+ firstException = false;
+ continue;
+ }
+
+ // mode ';' exception | exception
+ char *unitListSvptr{nullptr};
+ // For the case where <mode> is unspecified
+ Convert conversion{Convert::BigEndian};
+
+ // If unitListStr != nullptr extract mode from modeStr[:unitListStr-1].
+ if (nullptr != unitListStr) {
+ *unitListStr++ = '\0'; // Advance unitListStr
+ if (auto convert{
+ GetConvertFromString(exceptionStr, std::strlen(exceptionStr))}) {
+ conversion = *convert;
+ } else {
+ success = false;
+ break;
+ }
+ } else {
+ unitListStr = exceptionStr;
+ }
+
+ unitListSvptr = nullptr;
+
+ // Loop over unit list extracting individual or ranges of units, separated
+ // by commas.
+
+ while (success) {
+ char *units;
+ int lb, ub;
+ char remStr[2];
+ int nread;
+ int nexpected;
+
+ lb = ub = -1;
+#if _WIN32
+ units =
+ strtok_s(unitListSvptr ? nullptr : unitListStr, ";", &unitListSvptr);
+#else
+ units =
+ strtok_r(unitListSvptr ? nullptr : unitListStr, ",", &unitListSvptr);
+#endif
+ if (nullptr == units) {
+ break;
+ }
+
+ // single unit or range of units.
+ // If hyphen is detected in units, assume range
+ if (strchr(units, '-')) {
+ nexpected = 2;
+ nread = sscanf(units, "%u-%u%1s", &lb, &ub, remStr);
+ } else {
+ nexpected = 1;
+ nread = sscanf(units, "%u%1s", &lb, remStr);
+ ub = lb;
+ }
+ if (nread != nexpected || (lb < 0) || (ub < 0) || (lb > ub)) {
+ success = false;
+ break;
+ }
+
+ ConvertUnit cu;
+ cu.conversion = conversion;
+ cu.startUnit = lb;
+ cu.endUnit = ub;
+ convertUnits.emplace_back(std::move(cu));
+ }
+ }
+
+ free(envStr); // from strdup()
+
+ if (success) {
+ conversion = gblConversion;
+ } else {
+ // Failure(s)
+ convertUnits.clear();
+ }
+ return success;
+}
+
+// ExecutionEnvironment::UnitRtConvert
+// Scan linear array ExecutionEnvironment::convertUnits for unitNumber, and if
+// found, return user specified (runtime) I/O conversion for unformatted
+// files.
+
+Convert ExecutionEnvironment::UnitRtConvert(int unitNumber) {
+ Convert convertReturn{Convert::Unknown};
+ // convertUnits is a small array, but still iterate backwards.
+ // rbegin() and rend() are not available.
+ for (auto it = convertUnits.end(); it != convertUnits.begin();) {
+ --it;
+ if (unitNumber >= it->startUnit && unitNumber <= it->endUnit) {
+ convertReturn = it->conversion;
+ break;
+ }
+ }
+ return convertReturn;
+}
+
void ExecutionEnvironment::Configure(int ac, const char *av[],
const char *env[], const EnvironmentDefaultList *envDefaults) {
argc = ac;
@@ -146,6 +298,13 @@ void ExecutionEnvironment::Configure(int ac, const char *av[],
}
}
+ if (auto *x{std::getenv("FORT_CONVERT_UNIT")}) {
+ if (!ParseFortConvertUnit(x)) {
+ std::fprintf(stderr,
+ "Fortran runtime: FORT_CONVERT_UNIT=%s is invalid; ignored\n", x);
+ }
+ }
+
if (auto *x{std::getenv("FORT_TRUNCATE_STREAM")}) {
char *end;
auto n{std::strtol(x, &end, 10)};
diff --git a/flang-rt/lib/runtime/external-unit.cpp b/flang-rt/lib/runtime/external-unit.cpp
index b4eaf2e85fc9a..67a9b9c0f397d 100644
--- a/flang-rt/lib/runtime/external-unit.cpp
+++ b/flang-rt/lib/runtime/external-unit.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "unit-map.h"
+#include "flang-rt/runtime/environment.h"
#include "flang-rt/runtime/io-error.h"
#include "flang-rt/runtime/lock.h"
#include "flang-rt/runtime/tools.h"
@@ -112,9 +113,26 @@ bool ExternalFileUnit::OpenUnit(common::optional<OpenStatus> status,
common::optional<Action> action, Position position,
OwningPtr<char> &&newPath, std::size_t newPathLength, Convert convert,
IoErrorHandler &handler) {
+ Convert explicitRtConvert{Convert::Unknown};
+
+ // Increasing order of conversion specifiers (endianness)
+ // 1. CONVERT=<mode> specifier on OPEN statement.
+ // 2. Environment variable FORT_CONVERT iff CONVERT specifier is not present.
+ // 3. Even if CONVERT specifier is present, check to environment variable
+ // FORT_CONVERT_UNIT to see if unit number has an explicit setting.
+
if (convert == Convert::Unknown) {
convert = executionEnvironment.conversion;
}
+
+ explicitRtConvert = executionEnvironment.UnitRtConvert(unitNumber());
+ if (Convert::Unknown != explicitRtConvert) {
+ // Runtime has overridden previous defaults using environment variable
+ // FORT_CONVERT_UNIT.
+ convert = explicitRtConvert;
+ }
+
+ executionEnvironment.UnitRtConvert(unitNumber());
swapEndianness_ = convert == Convert::Swap ||
(convert == Convert::LittleEndian && !isHostLittleEndian) ||
(convert == Convert::BigEndian && isHostLittleEndian);
diff --git a/flang-rt/test/Runtime/fort_convert.f90 b/flang-rt/test/Runtime/fort_convert.f90
new file mode 100644
index 0000000000000..19fc173ec9bf0
--- /dev/null
+++ b/flang-rt/test/Runtime/fort_convert.f90
@@ -0,0 +1,151 @@
+! UNSUPPORTED: offload-cuda
+! UNSUPPORTED: system-windows
+
+! Verify endian conversion for unformatted stream I/O with and
+! without runtime environment variable FORT_CONVERT_UNIT specified.
+!
+! Default units:
+! OPEN(10, FORM="unformatted")
+! OPEN(11, FORM="unformatted", CONVERT="native")
+! OPEN(12, FORM="unformatted", CONVERT="big_endian")
+! OPEN(13, FORM="unformatted", CONVERT="little_endian")
+! OPEN(14, FORM="unformatted", CONVERT="swap")
+!
+! First test, FORT_CONVERT_UNIT not present.
+! Second test: FORT_CONVERT_UNIT="swap:10-11;little_endian:12;big_endian:13;native:14"
+
+! RUN: %flang %isysroot -L"%libdir" %s -o %t
+! RUN: %t | FileCheck %s
+! RUN: env FORT_CONVERT_UNIT="swap:10-11;little_endian:12;big_endian:13;native:14" %t | FileCheck %s
+
+! CHECK: PASS
+module testmod
+ implicit none
+ character(len=1), dimension(4) :: c4
+ integer(kind=4) :: i4 = 1
+ integer(kind=4), dimension(2) :: data10 = [10, int(z'01cafe23')]
+ integer(kind=4), dimension(2) :: data11 = [11, int(z'45beef67')]
+ integer(kind=4), dimension(2) :: data12 = [12, int(z'98faceab')]
+ integer(kind=4), dimension(2) :: data13 = [13, int(z'cdfeed01')]
+ integer(kind=4), dimension(2) :: data14 = [14, int(z'02deaf03')]
+
+ contains
+ logical function isLittleEndian()
+ c4 = transfer(i4, c4)
+ isLittleEndian = ichar(c4(1)) == 1
+ end function isLittleEndian
+
+ logical function isBigEndian()
+ isBigEndian = .not. isLittleEndian()
+ end function isBigEndian
+
+ logical function filecheck(unit, refdata, isNative) result(res)
+ integer(kind=4) :: unit
+ integer(kind=4), dimension(2) :: refdata
+ logical :: isNative
+
+ integer :: i
+ integer :: ios
+ integer(kind=4), dimension(4) :: filearr
+ integer(kind=4), dimension(4) :: workarr
+ character(len=1), dimension(4) :: charwork
+
+ res = .true.
+
+ read(unit, iostat=ios) filearr
+ if (ios /= 0) then
+ print*, 'ios=', ios
+ stop 1
+ end if
+
+ workarr(1) = int(z'00000008')
+ workarr(2:3) = refdata
+ workarr(4) = int(z'00000008')
+
+ ! If isNative == .true. file data should match in memory layout of refdata.
+ ! If isNative == .false., refdata (kind=4) has to have endianness switched.
+ if (.not. isNative) then
+ ! swap endianness of input reference data
+ do i = 1, 4
+ charwork = transfer(workarr(i), charwork)
+ charwork = charwork(size(charwork):1:-1)
+ workarr(i) = transfer(charwork, workarr(i))
+ end do
+ end if
+
+ res = .not. any(filearr /= workarr)
+
+ if (.not. res) then
+ write(*,'(4("0x", z8.8:x))') filearr
+ write(*,'(4("0x", z8.8:x))') workarr
+ endif
+
+ end function filecheck
+end module testmod
+program main
+ use testmod
+ implicit none
+ integer :: ios
+ integer :: i
+ logical :: FORT_CONVERT_UNIT_present
+
+ call get_environment_variable("FORT_CONVERT_UNIT", length=ios)
+ FORT_CONVERT_UNIT_present = ios /= 0
+
+ ! Some runtime debug statements.
+ ! print*, 'FORT_CONVERT_UNIT_present=', FORT_CONVERT_UNIT_present
+ ! if (isLittleEndian()) print *, 'Little Endian'
+
+ open(10, iostat=ios, form="unformatted", access="sequential", status="unknown")
+ if (ios /= 0) stop 10
+ open(11, iostat=ios, form="unformatted", access="sequential", status="unknown", convert="native")
+ if (ios /= 0) stop 11
+ open(12, iostat=ios, form="unformatted", access="sequential", status="unknown", convert="big_endian")
+ if (ios /= 0) stop 12
+ open(13, iostat=ios, form="unformatted", access="sequential", status="unknown", convert="little_endian")
+ if (ios /= 0) stop 13
+ open(14, iostat=ios, form="unformatted", access="sequential", status="unknown", convert="swap")
+ if (ios /= 0) stop 14
+
+ write(10, iostat=ios) data10
+ if (ios /= 0) stop 20
+ write(11, iostat=ios) data11
+ if (ios /= 0) stop 21
+ write(12, iostat=ios) data12
+ if (ios /= 0) stop 22
+ write(13, iostat=ios) data13
+ if (ios /= 0) stop 23
+ write(14, iostat=ios) data14
+ if (ios /= 0) stop 24
+
+ do i = 10, 14
+ close(i)
+ end do
+
+ open(20, iostat=ios, form="unformatted", access="stream", name="fort.10", status="old", convert="native")
+ if (ios /= 0) stop 30
+ open(21, iostat=ios, form="unformatted", access="stream", name="fort.11", status="old", convert="native")
+ if (ios /= 0) stop 31
+ open(22, iostat=ios, form="unformatted", access="stream", name="fort.12", status="old", convert="native")
+ if (ios /= 0) stop 32
+ open(23, iostat=ios, form="unformatted", access="stream", name="fort.13", status="old", convert="native")
+ if (ios /= 0) stop 33
+ open(24, iostat=ios, form="unformatted", access="stream", name="fort.14", status="old", convert="native")
+ if (ios /= 0) stop 34
+
+ if (FORT_CONVERT_UNIT_present) then
+ if (.not. filecheck(20, data10, .false.)) stop 40
+ if (.not. filecheck(21, data11, .false.)) stop 41
+ if (.not. filecheck(22, data12, isLittleEndian())) stop 42
+ if (.not. filecheck(23, data13, isBigEndian())) stop 43
+ if (.not. filecheck(24, data14, .true.)) stop 44
+ else
+ if (.not. filecheck(20, data10, .true.)) stop 45
+ if (.not. filecheck(21, data11, .true.)) stop 46
+ if (.not. filecheck(22, data12, isBigEndian())) stop 47
+ if (.not. filecheck(23, data13, isLittleEndian())) stop 48
+ if (.not. filecheck(24, data14, .false.)) stop 49
+ endif
+
+ print *,'PASS'
+end program main
diff --git a/flang/docs/RuntimeEnvironment.md b/flang/docs/RuntimeEnvironment.md
index 0414ea1e0d59c..6f6edac13ac3f 100644
--- a/flang/docs/RuntimeEnvironment.md
+++ b/flang/docs/RuntimeEnvironment.md
@@ -27,12 +27,83 @@ encoding on input and use UTF-8 encoding on formatted external output.
## `FORT_CONVERT`
-Determines data conversions applied to unformatted I/O.
+Determines the data conversions applied to all unformatted I/O units that do
+not have an explicit `CONVERT=` specifier in their `OPEN` statements.
+`FORT_CONVERT` is an alias for the `FORT_CONVERT_UNIT` environment
+variable without any `exception`s.
+`FORT_CONVERT=mode`
+* `mode: 'NATIVE' | 'LITTLE_ENDIAN' | 'BIG_ENDIAN' | 'SWAP';`
* `NATIVE`: no conversions (default)
-* `LITTLE_ENDIAN`: assume input is little-endian; emit little-endian output
-* `BIG_ENDIAN`: assume input is big-endian; emit big-endian output
-* `SWAP`: reverse endianness (always convert)
+* `LITTLE_ENDIAN`: assumes that input is little-endian; emit little-endian
+output
+* `BIG_ENDIAN`: assumes that input is big-endian; emit big-endian output
+* `SWAP`: reverses the endianness (always convert)
+
+## `FORT_CONVERT_UNIT`
+
+Determines the data conversion applied to specific unformatted I/O units.
+
+```
+FORT_CONVERT_UNIT= mode | mode ';' exception | exception ;
+mode: 'NATIVE' | 'LITTLE_ENDIAN' | 'BIG_ENDIAN' | 'SWAP';
+exception: mode ':' unit_list | unit_list ;
+unit_list: unit_spec | unit_list ',' unit_spec ;
+unit_spec: integer | integer '-' integer ;
+integer: [0-9]+ ;
+```
+
+The endianness of unformatted files is determined in the following order:
+1. The host processor's native endianness.
+2. The setting of the `-fconvert=<mode>` flang compiler command-line option.
+3. The global setting from the `FORT_CONVERT=mode` or `FORT_CONVERT_UNIT=mode`
+environment variables. Contradictory mode settings between `FORT_CONVERT` and
+`FORT_CONVERT_UNIT` result in `FORT_CONVERT_UNIT` taking priority.
+4. The explicit setting of the `CONVERT=` specifier in the `OPEN` statement for
+a particular unit.
+5. The exception setting for an individual unit or range of units from the
+`FORT_CONVERT_UNIT` environment variable.
+
+### Examples
+
+* If the `FORT_CONVERT`, `FORT_CONVERT_UNIT`, and `CONVERT=` specifier in the
+ `OPEN` statement are all missing, no data conversion is performed for
+ unformatted I/O; the host processor's native encoding is used.
+* If the environment variable `FORT_CONVERT=BIG_ENDIAN` is set and no
+ `CONVERT=` specifier is present in the `OPEN` statement, input is assumed to
+ be big-endian, and output is emitted in big-endian format.
+* On a little-endian host, if the environment variable `FORT_CONVERT=SWAP` is
+ set and the `CONVERT=LITTLE_ENDIAN` specifier is present in the `OPEN`
+ statement, the `OPEN` statement takes precedence: input is assumed to be
+ little-endian, and output is emitted in little-endian format.
+* If unit 10 is opened on a little-endian host with the environment variable
+ `FORT_CONVERT=SWAP`, `FORT_CONVERT_UNIT=BIG_ENDIAN:10`, and an `OPEN`
+ statement for unit 10 with the `CONVERT=LITTLE_ENDIAN` specifier, the
+ `BIG_ENDIAN:10` exception from the `FORT_CONVERT_UNIT` environment variable
+ takes precedence: input is assumed to be big-endian, and output is emitted
+ in big-endian format.
+
+### Notes
+
+1. `<mode>` values specified with the runtime environment variables
+ `FORT_CONVERT` or `FORT_CONVERT_UNIT` are case-insensitive.
+2. `<mode>` values specified with the flang command-line option
+ `-fconvert=<mode>` are case-sensitive and include:
+`mode: 'native' | 'little-endian' | 'big-endian' | 'swap';`
+3. `unit_spec` supports ranges separated by a hyphen. Ranges must denote
+positive unit numbers, and the starting unit (LHS) must be less than or equal
+to the ending unit (RHS).
+4. Unit numbers and ranges can be specified multiple times with different
+`modes`, with the last (rightmost) `exception` taking priority. For example:
+`FORT_CONVERT_UNIT="LITTLE_ENDIAN:10,11,15-20;BIG_ENDIAN:19"`
+The conversion for unit 19 will be `BIG_ENDIAN`.
+5. If an `exception` is not prefixed with `mode:`, `mode` is assumed to be
+`BIG_ENDIAN`. For example:
+`FORT_CONVERT_UNIT="20-25;LITTLE_ENDIAN:26-27"`
+Regardless of the host processor's endianness, units 20 through 25 will be
+treated as big-endian for both input and output, while units 26 and 27 will be
+treated as little-endian for both input and output.
+
## `FORT_CHECK_POINTER_DEALLOCATION`
>From 7586389fd849d2696843f3e8ca6cae6ec99a8f40 Mon Sep 17 00:00:00 2001
From: David Parks <djp at david-parks.com>
Date: Fri, 18 Sep 2026 12:37:09 -0600
Subject: [PATCH 2/3] Fixes to FORT_CONVERT_UNIT from feedback from reviewers.
---
.../{lib => include/flang-rt}/runtime/array.h | 0
flang-rt/include/flang-rt/runtime/environment.h | 2 +-
flang-rt/lib/runtime/environment.cpp | 15 +++++++--------
flang-rt/lib/runtime/external-unit.cpp | 1 -
flang-rt/lib/runtime/io-api-server.cpp | 2 +-
5 files changed, 9 insertions(+), 11 deletions(-)
rename flang-rt/{lib => include/flang-rt}/runtime/array.h (100%)
diff --git a/flang-rt/lib/runtime/array.h b/flang-rt/include/flang-rt/runtime/array.h
similarity index 100%
rename from flang-rt/lib/runtime/array.h
rename to flang-rt/include/flang-rt/runtime/array.h
diff --git a/flang-rt/include/flang-rt/runtime/environment.h b/flang-rt/include/flang-rt/runtime/environment.h
index bba75cc06f5b5..54cad69d153dd 100644
--- a/flang-rt/include/flang-rt/runtime/environment.h
+++ b/flang-rt/include/flang-rt/runtime/environment.h
@@ -9,10 +9,10 @@
#ifndef FLANG_RT_RUNTIME_ENVIRONMENT_H_
#define FLANG_RT_RUNTIME_ENVIRONMENT_H_
-#include "../lib/runtime/array.h"
#include "flang/Common/optional.h"
#include "flang/Decimal/decimal.h"
#include "flang/Runtime/entry-names.h"
+#include "array.h"
struct EnvironmentDefaultList;
diff --git a/flang-rt/lib/runtime/environment.cpp b/flang-rt/lib/runtime/environment.cpp
index 0663399a55f75..7924e43379127 100644
--- a/flang-rt/lib/runtime/environment.cpp
+++ b/flang-rt/lib/runtime/environment.cpp
@@ -1,4 +1,3 @@
-#include <string_view>
//===-- lib/runtime/environment.cpp -----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -7,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#include "environment-default-list.h"
#include "flang-rt/runtime/environment.h"
+#include "environment-default-list.h"
#include "flang-rt/runtime/memory.h"
#include "flang-rt/runtime/tools.h"
#include <cstdio>
@@ -135,7 +134,7 @@ bool ExecutionEnvironment::ParseFortConvertUnit(const char *cenvStr) {
}
// unitList is not yet correct, might be nullptr or pointing to ':'.
- unitListStr = strchr(exceptionStr, ':');
+ unitListStr = std::strchr(exceptionStr, ':');
if (firstException && (nullptr == unitListStr) &&
!isdigit(exceptionStr[0])) {
@@ -187,7 +186,7 @@ bool ExecutionEnvironment::ParseFortConvertUnit(const char *cenvStr) {
lb = ub = -1;
#if _WIN32
units =
- strtok_s(unitListSvptr ? nullptr : unitListStr, ";", &unitListSvptr);
+ strtok_s(unitListSvptr ? nullptr : unitListStr, ",", &unitListSvptr);
#else
units =
strtok_r(unitListSvptr ? nullptr : unitListStr, ",", &unitListSvptr);
@@ -198,12 +197,12 @@ bool ExecutionEnvironment::ParseFortConvertUnit(const char *cenvStr) {
// single unit or range of units.
// If hyphen is detected in units, assume range
- if (strchr(units, '-')) {
+ if (std::strchr(units, '-')) {
nexpected = 2;
- nread = sscanf(units, "%u-%u%1s", &lb, &ub, remStr);
+ nread = std::sscanf(units, "%u-%u%1s", &lb, &ub, remStr);
} else {
nexpected = 1;
- nread = sscanf(units, "%u%1s", &lb, remStr);
+ nread = std::sscanf(units, "%u%1s", &lb, remStr);
ub = lb;
}
if (nread != nexpected || (lb < 0) || (ub < 0) || (lb > ub)) {
@@ -219,7 +218,7 @@ bool ExecutionEnvironment::ParseFortConvertUnit(const char *cenvStr) {
}
}
- free(envStr); // from strdup()
+ std::free(envStr); // from strdup()
if (success) {
conversion = gblConversion;
diff --git a/flang-rt/lib/runtime/external-unit.cpp b/flang-rt/lib/runtime/external-unit.cpp
index 67a9b9c0f397d..ba1fb3c17e41e 100644
--- a/flang-rt/lib/runtime/external-unit.cpp
+++ b/flang-rt/lib/runtime/external-unit.cpp
@@ -132,7 +132,6 @@ bool ExternalFileUnit::OpenUnit(common::optional<OpenStatus> status,
convert = explicitRtConvert;
}
- executionEnvironment.UnitRtConvert(unitNumber());
swapEndianness_ = convert == Convert::Swap ||
(convert == Convert::LittleEndian && !isHostLittleEndian) ||
(convert == Convert::BigEndian && isHostLittleEndian);
diff --git a/flang-rt/lib/runtime/io-api-server.cpp b/flang-rt/lib/runtime/io-api-server.cpp
index 8973a392785a6..404722a1dd34f 100644
--- a/flang-rt/lib/runtime/io-api-server.cpp
+++ b/flang-rt/lib/runtime/io-api-server.cpp
@@ -9,8 +9,8 @@
// Implements the RPC server-side handlling of the I/O statement API needed for
// basic list-directed output (PRINT *) of intrinsic types for the GPU.
-#include "array.h"
#include "io-api-gpu.h"
+#include "flang-rt/runtime/array.h"
#include "flang-rt/runtime/memory.h"
#include "flang-rt/runtime/terminator.h"
#include "flang/Runtime/io-api.h"
>From d58f893cce91e3d7c52ed16e6c6ca2342d95e81e Mon Sep 17 00:00:00 2001
From: David Parks <djp at david-parks.com>
Date: Fri, 18 Sep 2026 12:57:13 -0600
Subject: [PATCH 3/3] FORT_CONVERT_UNIT fix formatting in:
include/flang-rt/runtime/environment.h.
---
flang-rt/include/flang-rt/runtime/environment.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang-rt/include/flang-rt/runtime/environment.h b/flang-rt/include/flang-rt/runtime/environment.h
index 39deaf840bdaa..67d50ced06eb8 100644
--- a/flang-rt/include/flang-rt/runtime/environment.h
+++ b/flang-rt/include/flang-rt/runtime/environment.h
@@ -9,10 +9,10 @@
#ifndef FLANG_RT_RUNTIME_ENVIRONMENT_H_
#define FLANG_RT_RUNTIME_ENVIRONMENT_H_
+#include "array.h"
#include "flang/Common/optional.h"
#include "flang/Decimal/decimal.h"
#include "flang/Runtime/entry-names.h"
-#include "array.h"
struct EnvironmentDefaultList;
More information about the flang-commits
mailing list