[llvm] [flang-rt] Fix REAL(10)/COMPLEX(10) component sizes in runtime type info (PR #192049)
Sairudra More via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 14 10:11:07 PDT 2026
https://github.com/Saieiei updated https://github.com/llvm/llvm-project/pull/192049
>From dc680c4e9d5422a97cf3e8f905854f0d2ce98d6b Mon Sep 17 00:00:00 2001
From: Sairudra More <moresair at pe31.hpc.amslabs.hpecorp.net>
Date: Tue, 14 Apr 2026 07:46:11 -0500
Subject: [PATCH 1/2] [flang] Fix REAL(10)/COMPLEX(10) component sizes in
runtime type info
For REAL(10) and COMPLEX(10) components, Component::GetElementByteSize()
was using the Fortran kind value as the byte size. On x86-64 that
underestimates the actual storage size (10 vs 16 bytes for REAL(10),
20 vs 32 for COMPLEX(10)), so strided componentwise assignment of
SEQUENCE types can corrupt adjacent data and crash.
Route REAL and COMPLEX component sizes through Descriptor::BytesFor(),
which matches the runtime existing storage-size handling.
Add a regression test covering strided assignment of a SEQUENCE type
with REAL(10)/COMPLEX(10) components.
---
flang-rt/lib/runtime/type-info.cpp | 5 ++--
flang-rt/unittests/Runtime/Descriptor.cpp | 28 +++++++++++++++++++++++
2 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/flang-rt/lib/runtime/type-info.cpp b/flang-rt/lib/runtime/type-info.cpp
index cb8e894bd3922..c5acfad3a98e3 100644
--- a/flang-rt/lib/runtime/type-info.cpp
+++ b/flang-rt/lib/runtime/type-info.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "flang-rt/runtime/type-info.h"
+#include "flang-rt/runtime/descriptor.h"
#include "flang-rt/runtime/terminator.h"
#include "flang-rt/runtime/tools.h"
#include <cstdio>
@@ -37,11 +38,11 @@ RT_API_ATTRS std::size_t Component::GetElementByteSize(
switch (category()) {
case TypeCategory::Integer:
case TypeCategory::Unsigned:
- case TypeCategory::Real:
case TypeCategory::Logical:
return kind_;
+ case TypeCategory::Real:
case TypeCategory::Complex:
- return 2 * kind_;
+ return Descriptor::BytesFor(category(), kind_);
case TypeCategory::Character:
if (auto value{characterLen_.GetValue(&instance)}) {
return kind_ * *value;
diff --git a/flang-rt/unittests/Runtime/Descriptor.cpp b/flang-rt/unittests/Runtime/Descriptor.cpp
index 7737b91d8a5b4..fc7f266dd17c6 100644
--- a/flang-rt/unittests/Runtime/Descriptor.cpp
+++ b/flang-rt/unittests/Runtime/Descriptor.cpp
@@ -280,3 +280,31 @@ TEST(Descriptor, Dump) {
fclose(tmpf);
}
#endif // defined(__linux__) && !defined(__ANDROID__)
+
+// Verify that Descriptor::BytesFor returns the correct storage size
+// for Real and Complex types, especially for kind=10 where the x87
+// 80-bit extended precision value is stored in a 16-byte container.
+TEST(Descriptor, BytesForRealAndComplex) {
+ using TC = TypeCategory;
+ // Real kinds: storage size should account for container padding
+ EXPECT_EQ(Descriptor::BytesFor(TC::Real, 2), 2u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Real, 3), 2u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Real, 4), 4u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Real, 8), 8u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Real, 10),
+ 16u); // x87: 80-bit in 128-bit container
+ EXPECT_EQ(Descriptor::BytesFor(TC::Real, 16), 16u);
+ // Complex kinds: should be twice the Real storage size
+ EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 2), 4u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 3), 4u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 4), 8u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 8), 16u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 10), 32u); // x87: 2 * 16 bytes
+ EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 16), 32u);
+ // Integer and Logical kinds: storage size equals the kind value
+ EXPECT_EQ(Descriptor::BytesFor(TC::Integer, 1), 1u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Integer, 4), 4u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Integer, 8), 8u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Logical, 1), 1u);
+ EXPECT_EQ(Descriptor::BytesFor(TC::Logical, 4), 4u);
+}
>From 55feb869d5f2e6e3ea4f5c663b0a2a9132cd65e0 Mon Sep 17 00:00:00 2001
From: Sairudra More <moresair at pe31.hpc.amslabs.hpecorp.net>
Date: Tue, 14 Apr 2026 12:00:15 -0500
Subject: [PATCH 2/2] [flang-rt] Guard kind-10 checks on x86 and test
GetElementByteSize
---
flang-rt/unittests/Runtime/Descriptor.cpp | 99 +++++++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/flang-rt/unittests/Runtime/Descriptor.cpp b/flang-rt/unittests/Runtime/Descriptor.cpp
index fc7f266dd17c6..ff3cffab1ece0 100644
--- a/flang-rt/unittests/Runtime/Descriptor.cpp
+++ b/flang-rt/unittests/Runtime/Descriptor.cpp
@@ -9,6 +9,8 @@
#include "flang-rt/runtime/descriptor.h"
#include "tools.h"
#include "gtest/gtest.h"
+#include "flang-rt/runtime/type-info.h"
+#include <cstring>
#include <regex>
using namespace Fortran::runtime;
@@ -291,15 +293,21 @@ TEST(Descriptor, BytesForRealAndComplex) {
EXPECT_EQ(Descriptor::BytesFor(TC::Real, 3), 2u);
EXPECT_EQ(Descriptor::BytesFor(TC::Real, 4), 4u);
EXPECT_EQ(Descriptor::BytesFor(TC::Real, 8), 8u);
+#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
+ defined(_M_IX86)
EXPECT_EQ(Descriptor::BytesFor(TC::Real, 10),
16u); // x87: 80-bit in 128-bit container
+#endif
EXPECT_EQ(Descriptor::BytesFor(TC::Real, 16), 16u);
// Complex kinds: should be twice the Real storage size
EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 2), 4u);
EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 3), 4u);
EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 4), 8u);
EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 8), 16u);
+#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
+ defined(_M_IX86)
EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 10), 32u); // x87: 2 * 16 bytes
+#endif
EXPECT_EQ(Descriptor::BytesFor(TC::Complex, 16), 32u);
// Integer and Logical kinds: storage size equals the kind value
EXPECT_EQ(Descriptor::BytesFor(TC::Integer, 1), 1u);
@@ -308,3 +316,94 @@ TEST(Descriptor, BytesForRealAndComplex) {
EXPECT_EQ(Descriptor::BytesFor(TC::Logical, 1), 1u);
EXPECT_EQ(Descriptor::BytesFor(TC::Logical, 4), 4u);
}
+
+// Test that Component::GetElementByteSize() returns the correct storage size.
+// This is the function modified by the fix; it delegates to
+// Descriptor::BytesFor() for Real and Complex types.
+TEST(Descriptor, ComponentGetElementByteSize) {
+ using TC = TypeCategory;
+ using typeInfo::Component;
+ using typeInfo::Value;
+
+ // Component has all-private members and no setters. Mirror its layout
+ // with public fields so we can populate category_ and kind_ for testing.
+ struct TestableComponent {
+ StaticDescriptor<0> name_;
+ Component::Genre genre_;
+ std::uint8_t category_;
+ std::uint8_t kind_;
+ std::uint8_t rank_;
+ std::uint8_t memorySpace_;
+ std::uint8_t padding_[3];
+ std::uint64_t offset_;
+ Value characterLen_;
+ StaticDescriptor<0, true> derivedType_;
+ StaticDescriptor<1, true> lenValue_;
+ StaticDescriptor<2, true> bounds_;
+ const char *initialization_;
+ };
+ static_assert(sizeof(TestableComponent) == sizeof(Component),
+ "TestableComponent layout must match Component");
+
+ // Dummy descriptor needed as the instance argument (unused for
+ // Real/Complex/Integer paths).
+ StaticDescriptor<0> staticDesc;
+ Descriptor &instance{staticDesc.descriptor()};
+ instance.Establish(TypeCode{TC::Integer, 4}, 4, nullptr, 0);
+
+ auto makeComponent = [](TC cat, std::uint8_t kind) {
+ TestableComponent tc{};
+ tc.genre_ = Component::Genre::Data;
+ tc.category_ = static_cast<std::uint8_t>(cat);
+ tc.kind_ = kind;
+ return tc;
+ };
+
+ // Integer: GetElementByteSize should return kind_ directly.
+ {
+ auto tc = makeComponent(TC::Integer, 4);
+ const auto &comp = reinterpret_cast<const Component &>(tc);
+ EXPECT_EQ(comp.GetElementByteSize(instance), 4u);
+ }
+ {
+ auto tc = makeComponent(TC::Integer, 8);
+ const auto &comp = reinterpret_cast<const Component &>(tc);
+ EXPECT_EQ(comp.GetElementByteSize(instance), 8u);
+ }
+ // Real: GetElementByteSize should use Descriptor::BytesFor.
+ {
+ auto tc = makeComponent(TC::Real, 4);
+ const auto &comp = reinterpret_cast<const Component &>(tc);
+ EXPECT_EQ(comp.GetElementByteSize(instance), 4u);
+ }
+ {
+ auto tc = makeComponent(TC::Real, 8);
+ const auto &comp = reinterpret_cast<const Component &>(tc);
+ EXPECT_EQ(comp.GetElementByteSize(instance), 8u);
+ }
+#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
+ defined(_M_IX86)
+ {
+ auto tc = makeComponent(TC::Real, 10);
+ const auto &comp = reinterpret_cast<const Component &>(tc);
+ EXPECT_EQ(comp.GetElementByteSize(instance), 16u);
+ }
+ // Complex kind=10: should be 2 * 16 = 32 bytes.
+ {
+ auto tc = makeComponent(TC::Complex, 10);
+ const auto &comp = reinterpret_cast<const Component &>(tc);
+ EXPECT_EQ(comp.GetElementByteSize(instance), 32u);
+ }
+#endif
+ // Complex: GetElementByteSize should use Descriptor::BytesFor.
+ {
+ auto tc = makeComponent(TC::Complex, 4);
+ const auto &comp = reinterpret_cast<const Component &>(tc);
+ EXPECT_EQ(comp.GetElementByteSize(instance), 8u);
+ }
+ {
+ auto tc = makeComponent(TC::Complex, 8);
+ const auto &comp = reinterpret_cast<const Component &>(tc);
+ EXPECT_EQ(comp.GetElementByteSize(instance), 16u);
+ }
+}
More information about the llvm-commits
mailing list