[flang-commits] [flang] [flang] Build the initial image of equivalence groups in target byte order (PR #228833)

MMS IT GmbH via flang-commits flang-commits at lists.llvm.org
Sat Oct 3 23:09:17 PDT 2026


https://github.com/mms-it-ch created https://github.com/llvm/llvm-project/pull/228833

`InitialImage` serialized numeric values in host byte order and read them back the same way (three `// TODO endianness`). When the storage of an initialized equivalence group is typed differently from the initialized member, e.g. CHARACTER data over an INTEGER array, the global was emitted in host byte order: compiling on x86-64 for a big-endian target produced `"7654"` instead of `"4567"`. Details in #228832.

This
- sets `TargetCharacteristics::isBigEndian` from the target triple (it kept the host value; this also affects the existing use for substrings of CHARACTER literals with KIND > 1),
- adds an optional swap unit to `StoreSerialValues`/`LoadSerialValues` and uses it in `InitialImage::Add` and `AsConstant` when the byte orders of target and host differ: per value, per part for COMPLEX, per character for CHARACTER with KIND > 1.

Nothing changes when target and host have the same byte order. The new test uses `powerpc64-unknown-linux-gnu` as a big-endian target.

Tests: the same change, written against the code before `StoreRawBytes`/`FromRawBytes` (memcpy), is in my branch based on 85ac560262 with an s390x/z/OS target: the test passes there (with `-triple s390x-ibm-zos`), the gfortran test `arrayio_7.f90` now passes on z/OS 3.1, and the NIST FORTRAN 77 suite still gives the host output. This version for `main` I could only check with `clang++ -fsyntax-only` (including `data-to-inits.cpp`, which instantiates `Add` for all types); I don't have a PowerPC build, so the new test runs in CI.

Fixes #228832.

Assisted-by: Claude Code (Anthropic)

🤖 Generated with [Claude Code](https://claude.com/claude-code)


>From dc8f2a26830db3c6a012ed9ce32609934405e64c Mon Sep 17 00:00:00 2001
From: mms-it-ch <info at mms-it.ch>
Date: Sun, 4 Oct 2026 08:08:11 +0200
Subject: [PATCH] [flang] Build the initial image of equivalence groups in
 target byte order

InitialImage serialized numeric values in host byte order and read them
back the same way (marked "TODO endianness"). When the storage of an
initialized equivalence group is typed differently from the initialized
member (e.g. CHARACTER data over an INTEGER array), the global was emitted
in host byte order: compiling on a little-endian host for a big-endian
target produced "7654" instead of "4567".

Set TargetCharacteristics::isBigEndian from the target triple (it kept the
host value), and let StoreSerialValues/LoadSerialValues reverse the bytes of
each value (per part for COMPLEX, per character for CHARACTER with KIND > 1)
when the target and host byte orders differ.

Assisted-by: Claude Code (Anthropic)
---
 flang/include/flang/Evaluate/initial-image.h  | 88 ++++++++++++++++---
 flang/include/flang/Tools/TargetSetup.h       |  2 +
 flang/lib/Evaluate/initial-image.cpp          | 19 ++--
 .../Lower/equivalence-init-big-endian.f90     | 23 +++++
 4 files changed, 116 insertions(+), 16 deletions(-)
 create mode 100755 flang/test/Lower/equivalence-init-big-endian.f90

diff --git a/flang/include/flang/Evaluate/initial-image.h b/flang/include/flang/Evaluate/initial-image.h
index 4fa7b9307014b..dad1091605b4f 100644
--- a/flang/include/flang/Evaluate/initial-image.h
+++ b/flang/include/flang/Evaluate/initial-image.h
@@ -15,26 +15,63 @@
 
 #include "expression.h"
 #include "flang/Evaluate/char.h"
+#include "llvm/ADT/SmallVector.h"
+#include <algorithm>
+#include <cstring>
 #include <map>
 #include <optional>
 #include <vector>
 
 namespace Fortran::evaluate {
 
+/// Reverses the bytes of each \p unit sized piece of \p p[0..bytes).
+inline void ReverseByteUnits(char *p, std::size_t bytes, std::size_t unit) {
+  if (unit > 1) {
+    for (std::size_t j{0}; j + unit <= bytes; j += unit) {
+      std::reverse(p + j, p + j + unit);
+    }
+  }
+}
+
+/// Serializes \p values to \p dst. If \p swapUnit is greater than one, the
+/// bytes of each \p swapUnit sized piece are reversed (host to target byte
+/// order).
 template <typename SCALAR>
 inline void StoreSerialValues(char *dst, llvm::ArrayRef<SCALAR> values,
-    size_t elementSize, bool *changed = nullptr) {
+    size_t elementSize, bool *changed = nullptr, size_t swapUnit = 0) {
   for (auto [i, v] : llvm::enumerate(values)) {
-    v.StoreRawBytes(dst + i * elementSize, elementSize, changed);
+    char *to{dst + i * elementSize};
+    if (swapUnit > 1) {
+      llvm::SmallVector<char, 32> buffer(elementSize);
+      v.StoreRawBytes(buffer.data(), elementSize);
+      ReverseByteUnits(buffer.data(), elementSize, swapUnit);
+      if (changed) {
+        if (std::memcmp(to, buffer.data(), elementSize) == 0) {
+          continue;
+        }
+        *changed = true;
+      }
+      std::memcpy(to, buffer.data(), elementSize);
+    } else {
+      v.StoreRawBytes(to, elementSize, changed);
+    }
   }
 }
 
+/// De-serializes \p values from \p src; \p swapUnit as for
+/// StoreSerialValues (target to host byte order).
 template <typename SCALAR>
-inline void LoadSerialValues(
-    const char *src, llvm::MutableArrayRef<SCALAR> values, size_t stride) {
+inline void LoadSerialValues(const char *src,
+    llvm::MutableArrayRef<SCALAR> values, size_t stride, size_t swapUnit = 0) {
   for (auto it : llvm::enumerate(values)) {
-    it.value() =
-        SCALAR::FromRawBytes(src + stride * it.index(), SCALAR::bytesStored());
+    const char *from{src + stride * it.index()};
+    if (swapUnit > 1) {
+      llvm::SmallVector<char, 32> buffer(from, from + SCALAR::bytesStored());
+      ReverseByteUnits(buffer.data(), buffer.size(), swapUnit);
+      it.value() = SCALAR::FromRawBytes(buffer.data(), SCALAR::bytesStored());
+    } else {
+      it.value() = SCALAR::FromRawBytes(from, SCALAR::bytesStored());
+    }
   }
 }
 
@@ -55,6 +92,23 @@ class InitialImage {
 
   std::size_t size() const { return data_.size(); }
 
+  /// The image holds the bytes of the values in the byte order of the target.
+  /// Returns the size of the pieces whose bytes have to be reversed when the
+  /// byte orders of the target and the host differ, or 0.
+  template <typename T>
+  static std::size_t ByteSwapUnit(
+      const FoldingContext &context, std::size_t elementBytes) {
+    if (context.targetCharacteristics().isBigEndian() != isHostLittleEndian) {
+      return 0; // same byte order
+    } else if constexpr (T::category == TypeCategory::Character) {
+      return T::kind;
+    } else if constexpr (T::category == TypeCategory::Complex) {
+      return elementBytes / 2; // real and imaginary parts
+    } else {
+      return elementBytes;
+    }
+  }
+
   template <typename A>
   Result Add(ConstantSubscript, std::size_t, const A &, FoldingContext &) {
     return NotAConstant;
@@ -73,10 +127,10 @@ class InitialImage {
       } else if (bytes == 0) {
         return OkNoChange;
       } else {
-        // TODO endianness
         bool changed{false};
         StoreSerialValues<Scalar<T>>(&data_.at(offset),
-            llvm::ArrayRef<Scalar<T>>(x.values()), *elementBytes, &changed);
+            llvm::ArrayRef<Scalar<T>>(x.values()), *elementBytes, &changed,
+            ByteSwapUnit<T>(context, *elementBytes));
         return changed ? Ok : OkNoChange;
       }
     }
@@ -84,7 +138,7 @@ class InitialImage {
   template <int KIND>
   Result Add(ConstantSubscript offset, std::size_t bytes,
       const Constant<Type<TypeCategory::Character, KIND>> &x,
-      FoldingContext &) {
+      FoldingContext &context) {
     if (offset < 0 || offset + bytes > data_.size()) {
       return OutOfRange;
     } else {
@@ -106,10 +160,22 @@ class InitialImage {
           if (scalarBytes != elementBytes) {
             result = LengthMismatch;
           }
-          // TODO endianness
           auto *to{&data_.at(offset)};
           bool changed{false};
-          scalar.StoreRawBytes(to, elementBytes, &changed);
+          if (std::size_t unit{
+                  ByteSwapUnit<Type<TypeCategory::Character, KIND>>(
+                      context, elementBytes)};
+              unit > 1) {
+            llvm::SmallVector<char, 64> buffer(elementBytes);
+            scalar.StoreRawBytes(buffer.data(), elementBytes);
+            ReverseByteUnits(buffer.data(), elementBytes, unit);
+            if (std::memcmp(to, buffer.data(), elementBytes) != 0) {
+              std::memcpy(to, buffer.data(), elementBytes);
+              changed = true;
+            }
+          } else {
+            scalar.StoreRawBytes(to, elementBytes, &changed);
+          }
           if (changed && result == OkNoChange) {
             result = Ok;
           }
diff --git a/flang/include/flang/Tools/TargetSetup.h b/flang/include/flang/Tools/TargetSetup.h
index 47f886141b002..995163904dcb0 100644
--- a/flang/include/flang/Tools/TargetSetup.h
+++ b/flang/include/flang/Tools/TargetSetup.h
@@ -24,6 +24,8 @@ namespace Fortran::tools {
 
   const llvm::Triple &targetTriple{targetMachine.getTargetTriple()};
 
+  targetCharacteristics.set_isBigEndian(!targetTriple.isLittleEndian());
+
   if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) {
     targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/3);
     targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/4);
diff --git a/flang/lib/Evaluate/initial-image.cpp b/flang/lib/Evaluate/initial-image.cpp
index 5da8018b3b3b5..d207736dc76f4 100644
--- a/flang/lib/Evaluate/initial-image.cpp
+++ b/flang/lib/Evaluate/initial-image.cpp
@@ -162,9 +162,18 @@ class AsConstantHelper {
       auto length{static_cast<ConstantSubscript>(stride) / T::kind};
       llvm::SmallVector<char, 256> buffer;
       const char *data{GetTailPaddedData(offset_, elements * stride, buffer)};
+      std::size_t unit{InitialImage::ByteSwapUnit<T>(context_, stride)};
       for (std::size_t j{0}; j < elements; ++j) {
-        typedValue[j] = value::Character<T::kind>::FromRawBytes(
-            data + j * stride, length * T::kind);
+        if (unit > 1) {
+          llvm::SmallVector<char, 64> chars(
+              data + j * stride, data + j * stride + length * T::kind);
+          ReverseByteUnits(chars.data(), chars.size(), unit);
+          typedValue[j] = value::Character<T::kind>::FromRawBytes(
+              chars.data(), length * T::kind);
+        } else {
+          typedValue[j] = value::Character<T::kind>::FromRawBytes(
+              data + j * stride, length * T::kind);
+        }
       }
       return AsGenericExpr(
           Const{length, std::move(typedValue), std::move(extents_)});
@@ -176,9 +185,9 @@ class AsConstantHelper {
               ? 0
               : (elements - 1) * stride + evaluate::Scalar<T>::bytesStored(),
           buffer)};
-      // TODO endianness
-      LoadSerialValues(
-          data, llvm::MutableArrayRef<evaluate::Scalar<T>>(typedValue), stride);
+      LoadSerialValues(data,
+          llvm::MutableArrayRef<evaluate::Scalar<T>>(typedValue), stride,
+          InitialImage::ByteSwapUnit<T>(context_, stride));
       return AsGenericExpr(Const{std::move(typedValue), std::move(extents_)});
     }
   }
diff --git a/flang/test/Lower/equivalence-init-big-endian.f90 b/flang/test/Lower/equivalence-init-big-endian.f90
new file mode 100755
index 0000000000000..3b7387b290d08
--- /dev/null
+++ b/flang/test/Lower/equivalence-init-big-endian.f90
@@ -0,0 +1,23 @@
+! The initial image of an equivalence group holds the bytes in the byte order
+! of the target, also when compiling for a big-endian target on a
+! little-endian host (and vice versa).
+! REQUIRES: powerpc-registered-target
+! RUN: %flang_fc1 -triple powerpc64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+subroutine chars_over_integers()
+  character*4, dimension(2) :: c = (/"0123", "4567"/)
+  integer :: k(2)
+  equivalence (k, c)
+  print *, c(2), k(1)
+end subroutine
+! "0123" = 0x30313233, "4567" = 0x34353637 in big-endian memory
+! CHECK: @_QFchars_over_integersEc = internal global [2 x i32] [i32 808530483, i32 875902519]
+
+subroutine integer_over_chars()
+  integer :: k = 1
+  character*4 :: c
+  equivalence (k, c)
+  print *, c, k
+end subroutine
+! The integer is the storage type here: no second byte swap on the way back.
+! CHECK: @_QFinteger_over_charsEc = internal global [1 x i32] [i32 1]



More information about the flang-commits mailing list