[flang-commits] [flang] [flang] Build the initial image of equivalence groups in target byte order (PR #228833)
via flang-commits
flang-commits at lists.llvm.org
Sat Oct 3 23:09:59 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-semantics
@llvm/pr-subscribers-flang-fir-hlfir
Author: MMS IT GmbH (mms-it-ch)
<details>
<summary>Changes</summary>
`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)
---
Full diff: https://github.com/llvm/llvm-project/pull/228833.diff
4 Files Affected:
- (modified) flang/include/flang/Evaluate/initial-image.h (+77-11)
- (modified) flang/include/flang/Tools/TargetSetup.h (+2)
- (modified) flang/lib/Evaluate/initial-image.cpp (+14-5)
- (added) flang/test/Lower/equivalence-init-big-endian.f90 (+23)
``````````diff
diff --git a/flang/include/flang/Evaluate/initial-image.h b/flang/include/flang/Evaluate/initial-image.h
index 4fa7b9307014b07..dad1091605b4f2c 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 47f886141b002a2..995163904dcb092 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 5da8018b3b3b598..d207736dc76f441 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 000000000000000..3b7387b290d0874
--- /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]
``````````
</details>
https://github.com/llvm/llvm-project/pull/228833
More information about the flang-commits
mailing list