[llvm] flang-rt: improve endian conversion performance (PR #218302)
Yuji Watanabe via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 18:51:02 PDT 2026
https://github.com/3776-u2 created https://github.com/llvm/llvm-project/pull/218302
Improve endian conversion performance in the flang runtime.
This patch updates the implementation of endian conversion in
flang-rt/lib/runtime/unit.cpp and adds a driver test for endian
conversion handling.
The modified helper functions were not unit-tested directly because
they have internal linkage.
Addresses #208193.
>From 251d55a2ba55daba66d53b32785e3a2192924b2c Mon Sep 17 00:00:00 2001
From: Watanabe Yuji <y.watanabe at jp.fujitsu.com>
Date: Thu, 20 Aug 2026 19:35:04 +0900
Subject: [PATCH] flang-rt: improve endian conversion performance
---
flang-rt/lib/runtime/unit.cpp | 55 +++++++++++++++
flang-rt/test/Driver/endian.f90 | 114 ++++++++++++++++++++++++++++++++
2 files changed, 169 insertions(+)
create mode 100644 flang-rt/test/Driver/endian.f90
diff --git a/flang-rt/lib/runtime/unit.cpp b/flang-rt/lib/runtime/unit.cpp
index c577ae7673127..280dea0a44cd8 100644
--- a/flang-rt/lib/runtime/unit.cpp
+++ b/flang-rt/lib/runtime/unit.cpp
@@ -31,6 +31,61 @@ RT_OFFLOAD_API_GROUP_BEGIN
static inline RT_API_ATTRS void SwapEndianness(
char *data, std::size_t bytes, std::size_t elementBytes) {
+ switch (elementBytes) {
+ case 2:
+ for (std::size_t j{0}; j + 2 <= bytes; j += 2) {
+ std::uint16_t x;
+ __builtin_memcpy(&x, data + j, sizeof(x));
+#if defined(_MSC_VER)
+ x = _byteswap_ushort(x);
+#else
+ x = __builtin_bswap16(x);
+#endif
+ __builtin_memcpy(data + j, &x, sizeof(x));
+ }
+ return;
+ case 4:
+ for (std::size_t j{0}; j + 4 <= bytes; j += 4) {
+ std::uint32_t x;
+ __builtin_memcpy(&x, data + j, sizeof(x));
+#if defined(_MSC_VER)
+ x = _byteswap_ulong(x);
+#else
+ x = __builtin_bswap32(x);
+#endif
+ __builtin_memcpy(data + j, &x, sizeof(x));
+ }
+ return;
+ case 8:
+ for (std::size_t j{0}; j + 8 <= bytes; j += 8) {
+ std::uint64_t x;
+ __builtin_memcpy(&x, data + j, sizeof(x));
+#if defined(_MSC_VER)
+ x = _byteswap_uint64(x);
+#else
+ x = __builtin_bswap64(x);
+#endif
+ __builtin_memcpy(data + j, &x, sizeof(x));
+ }
+ return;
+ case 16:
+ for (std::size_t j{0}; j + 16 <= bytes; j += 16) {
+ std::uint64_t hi, lo;
+ __builtin_memcpy(&lo, data + j, sizeof(lo));
+ __builtin_memcpy(&hi, data + j + 8, sizeof(hi));
+#if defined(_MSC_VER)
+ lo = _byteswap_uint64(lo);
+ hi = _byteswap_uint64(hi);
+#else
+ lo = __builtin_bswap64(lo);
+ hi = __builtin_bswap64(hi);
+#endif
+ __builtin_memcpy(data + j + 8, &lo, sizeof(lo));
+ __builtin_memcpy(data + j, &hi, sizeof(hi));
+ }
+ return;
+ default:;
+ }
if (elementBytes > 1) {
auto half{elementBytes >> 1};
for (std::size_t j{0}; j + elementBytes <= bytes; j += elementBytes) {
diff --git a/flang-rt/test/Driver/endian.f90 b/flang-rt/test/Driver/endian.f90
new file mode 100644
index 0000000000000..b64be8ddf3172
--- /dev/null
+++ b/flang-rt/test/Driver/endian.f90
@@ -0,0 +1,114 @@
+! UNSUPPORTED: offload-cuda
+
+! Verify endian conversion for unformatted stream I/O with
+! CONVERT='BIG_ENDIAN'. The test writes values in big-endian
+! format, reads raw bytes back, applies SwapEndianness(), and
+! checks that the original values are restored.
+
+! RUN: %flang %isysroot -L"%libdir" %s -o %t
+! RUN: env LD_LIBRARY_PATH="$LD_LIBRARY_PATH:%libdir" %t | FileCheck %s
+
+! CHECK: PASS
+program main
+ implicit none
+ call test2()
+ call test4()
+ call test8()
+ call test16()
+ print *,'PASS'
+
+end program main
+subroutine SwapEndianness(data, bytes, elementBytes)
+ implicit none
+ integer(1) data(0:*), tmp
+ integer(8) bytes, elementBytes, half
+ integer(8) j,k
+ half = elementBytes/2
+ do j = 0, bytes - elementBytes, elementBytes
+ do k = 0, half-1
+ tmp = data(j + k)
+ data(j + k) = data(j + elementBytes - 1 - k)
+ data(j + elementBytes - 1 - k) = tmp
+ end do
+ end do
+end subroutine
+subroutine test2()
+ implicit none
+ integer(2) i2(2)
+ integer(1) i8(4)
+ i2 = (/1,2/)
+
+ open(10,file='test.dat',form='unformatted',access='stream',status='unknown', convert='big_endian')
+ write(10) i2
+ close(10)
+
+ open(10,file='test.dat',form='unformatted',access='stream',status='unknown')
+ read(10) i8
+ close(10)
+
+ call SwapEndianness(i8, 4_8, 2_8)
+
+ if (.not. all (i2 == transfer(i8, i2))) then
+ stop
+ endif
+end subroutine
+subroutine test4()
+ implicit none
+ real(4) f32(2)
+ integer(1) i8(8)
+ f32 = (/1,2/)
+
+ open(10,file='test.dat',form='unformatted',access='stream',status='unknown', convert='big_endian')
+ write(10) f32
+ close(10)
+
+ open(10,file='test.dat',form='unformatted',access='stream',status='unknown')
+ read(10) i8
+ close(10)
+
+ call SwapEndianness(i8, 8_8, 4_8)
+
+ if (.not. all (f32 == transfer(i8, f32))) then
+ stop
+ endif
+end subroutine
+subroutine test8()
+ implicit none
+ real(8) f64(2)
+ integer(1) i8(16)
+ f64 = (/1,2/)
+
+ open(10,file='test.dat',form='unformatted',access='stream',status='unknown', convert='big_endian')
+ write(10) f64
+ close(10)
+
+ open(10,file='test.dat',form='unformatted',access='stream',status='unknown')
+ read(10) i8
+ close(10)
+
+ call SwapEndianness(i8, 16_8, 8_8)
+
+ if (.not. all (f64 == transfer(i8, f64))) then
+ stop
+ endif
+end subroutine
+subroutine test16()
+ implicit none
+ real(16) f128(2)
+ integer(1) i8(32)
+ f128 = (/1,2/)
+
+ open(10,file='test.dat',form='unformatted',access='stream',status='unknown', convert='big_endian')
+ write(10) f128
+ close(10)
+
+ open(10,file='test.dat',form='unformatted',access='stream',status='unknown')
+ read(10) i8
+ close(10)
+
+ call SwapEndianness(i8, 32_8, 16_8)
+
+ if (.not. all (f128 == transfer(i8, f128))) then
+ stop
+ endif
+end subroutine
More information about the llvm-commits
mailing list