[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