[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
Sun Sep 20 12:30:44 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