[clang] [llvm] [Sparc][clang] Use 64-bit long double on bare-metal targets (PR #222304)
Daniel Cederman via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 16 05:26:07 PDT 2026
https://github.com/doac updated https://github.com/llvm/llvm-project/pull/222304
>From 6d87620fab43b0ab7cf6cc3b46b111316c027010 Mon Sep 17 00:00:00 2001
From: Daniel Cederman <cederman at gaisler.com>
Date: Wed, 1 Jul 2026 07:42:06 +0200
Subject: [PATCH 1/2] [Sparc][clang] Use 64-bit long double on bare-metal
targets
GCC uses 64-bit long double for bare-metal SPARC V8 targets,
including RTEMS. Keep the default 128-bit for other SPARC
targets.
Use the scalar calling convention for 64-bit long double.
Assisted-by: Codex
---
clang/lib/Basic/Targets/Sparc.cpp | 3 +-
clang/lib/Basic/Targets/Sparc.h | 18 +++++++---
clang/lib/CodeGen/Targets/Sparc.cpp | 6 ++--
.../CodeGen/Sparc/sparc-long-double-abi.c | 17 ++++++++++
clang/test/CodeGen/Sparc/sparc-vaarg.c | 2 +-
clang/test/CodeGen/Sparc/sparcv8-abi.c | 2 +-
clang/test/CodeGen/Sparc/variadic-aggregate.c | 2 +-
clang/test/Preprocessor/init.c | 33 ++++++++++++-------
.../Preprocessor/predefined-arch-macros.c | 9 +++++
9 files changed, 70 insertions(+), 22 deletions(-)
create mode 100644 clang/test/CodeGen/Sparc/sparc-long-double-abi.c
diff --git a/clang/lib/Basic/Targets/Sparc.cpp b/clang/lib/Basic/Targets/Sparc.cpp
index fe1aad6804aa6..46261d4556998 100644
--- a/clang/lib/Basic/Targets/Sparc.cpp
+++ b/clang/lib/Basic/Targets/Sparc.cpp
@@ -165,7 +165,8 @@ void SparcV8TargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4");
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8");
}
- Builder.defineMacro("__LONG_DOUBLE_128__");
+ if (LongDoubleWidth == 128)
+ Builder.defineMacro("__LONG_DOUBLE_128__");
}
void SparcV9TargetInfo::getTargetDefines(const LangOptions &Opts,
diff --git a/clang/lib/Basic/Targets/Sparc.h b/clang/lib/Basic/Targets/Sparc.h
index 95b69ff2be4bb..1d0cebaa18496 100644
--- a/clang/lib/Basic/Targets/Sparc.h
+++ b/clang/lib/Basic/Targets/Sparc.h
@@ -167,11 +167,19 @@ class LLVM_LIBRARY_VISIBILITY SparcV8TargetInfo : public SparcTargetInfo {
break;
}
- // The SPARCv8 System V ABI has long double 128-bits in size, but 64-bit
- // aligned.
- LongDoubleWidth = 128;
- LongDoubleAlign = 64;
- LongDoubleFormat = &llvm::APFloat::IEEEquad();
+ // Bare-metal and RTEMS targets default to 64-bit long double, matching
+ // GCC. Other targets use 128-bit per the SPARCv8 System V ABI, with
+ // 64-bit alignment.
+ if (getTriple().getOS() == llvm::Triple::UnknownOS ||
+ getTriple().getOS() == llvm::Triple::RTEMS) {
+ LongDoubleWidth = 64;
+ LongDoubleAlign = 64;
+ LongDoubleFormat = &llvm::APFloat::IEEEdouble();
+ } else {
+ LongDoubleWidth = 128;
+ LongDoubleAlign = 64;
+ LongDoubleFormat = &llvm::APFloat::IEEEquad();
+ }
// Up to 32 bits (V8) or 64 bits (V9) are lock-free atomic, but we're
// willing to do atomic ops on up to 64 bits.
diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index f5a17aa51184a..fd0c18e290655 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -72,7 +72,8 @@ ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
return classifyComplexType(CT, /*IsRet=*/true);
if (const auto *BT = Ty->getAs<BuiltinType>();
- BT && BT->getKind() == BuiltinType::LongDouble)
+ BT && BT->getKind() == BuiltinType::LongDouble &&
+ getContext().getTypeSize(Ty) > 64)
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
/*ByVal=*/false);
@@ -84,7 +85,8 @@ ABIArgInfo SparcV8ABIInfo::classifyArgumentType(QualType Ty) const {
return classifyComplexType(CT, /*IsRet=*/false);
const auto *BT = Ty->getAs<BuiltinType>();
- if (BT && BT->getKind() == BuiltinType::LongDouble)
+ if (BT && BT->getKind() == BuiltinType::LongDouble &&
+ getContext().getTypeSize(Ty) > 64)
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
return DefaultABIInfo::classifyArgumentType(Ty);
diff --git a/clang/test/CodeGen/Sparc/sparc-long-double-abi.c b/clang/test/CodeGen/Sparc/sparc-long-double-abi.c
new file mode 100644
index 0000000000000..e022643e87adc
--- /dev/null
+++ b/clang/test/CodeGen/Sparc/sparc-long-double-abi.c
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -triple sparc-unknown-none -O1 -emit-llvm -o - %s | FileCheck %s
+
+void sink(long double);
+void vararg(int, ...);
+long double source(void);
+
+long double test(long double x) {
+ sink(x);
+ vararg(0, x);
+ return source();
+}
+
+// CHECK-LABEL: define{{.*}} double @test(double noundef %x)
+// CHECK: {{.*}}call void @sink(double noundef %x)
+// CHECK: {{.*}}call void (i32, ...) @vararg(i32 noundef 0, double noundef %x)
+// CHECK: [[RESULT:%.*]] = {{.*}}call double @source()
+// CHECK: ret double [[RESULT]]
diff --git a/clang/test/CodeGen/Sparc/sparc-vaarg.c b/clang/test/CodeGen/Sparc/sparc-vaarg.c
index f322229e20c43..bfe1a0209461e 100644
--- a/clang/test/CodeGen/Sparc/sparc-vaarg.c
+++ b/clang/test/CodeGen/Sparc/sparc-vaarg.c
@@ -1,5 +1,5 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
-// RUN: %clang_cc1 -triple sparc -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple sparc-unknown-linux -emit-llvm -o - %s | FileCheck %s
#include <stdarg.h>
// CHECK-LABEL: define dso_local i32 @get_int(
diff --git a/clang/test/CodeGen/Sparc/sparcv8-abi.c b/clang/test/CodeGen/Sparc/sparcv8-abi.c
index 7beddd20e5e4d..317a5ea1beee5 100644
--- a/clang/test/CodeGen/Sparc/sparcv8-abi.c
+++ b/clang/test/CodeGen/Sparc/sparcv8-abi.c
@@ -1,5 +1,5 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --filter "^define |^entry:" --version 6
-// RUN: %clang_cc1 -triple sparc-unknown-unknown -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple sparc-unknown-linux -emit-llvm %s -o - | FileCheck %s
float __complex__
// CHECK-LABEL: define dso_local { float, float } @p(
diff --git a/clang/test/CodeGen/Sparc/variadic-aggregate.c b/clang/test/CodeGen/Sparc/variadic-aggregate.c
index be571ecb5fadd..d451056ef434a 100644
--- a/clang/test/CodeGen/Sparc/variadic-aggregate.c
+++ b/clang/test/CodeGen/Sparc/variadic-aggregate.c
@@ -1,5 +1,5 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
-// RUN: %clang_cc1 -triple sparc -emit-llvm -o - %s \
+// RUN: %clang_cc1 -triple sparc-unknown-linux -emit-llvm -o - %s \
// RUN: | FileCheck %s --check-prefixes=SPARC
//
// RUN: %clang_cc1 -triple sparcv9-unknown-unknown -emit-llvm -o - %s \
diff --git a/clang/test/Preprocessor/init.c b/clang/test/Preprocessor/init.c
index 21d61de8c0aaf..56c782aeb1397 100644
--- a/clang/test/Preprocessor/init.c
+++ b/clang/test/Preprocessor/init.c
@@ -1122,19 +1122,29 @@
// SPARC:#define __INT_LEAST8_MAX__ 127
// SPARC:#define __INT_LEAST8_TYPE__ signed char
// SPARC:#define __INT_MAX__ 2147483647
-// SPARC:#define __LDBL_DENORM_MIN__ 6.47517511943802511092443895822764655e-4966L
-// SPARC:#define __LDBL_DIG__ 33
-// SPARC:#define __LDBL_EPSILON__ 1.92592994438723585305597794258492732e-34L
+// SPARC-DEFAULT:#define __LDBL_DENORM_MIN__ 4.9406564584124654e-324L
+// SPARC-NETOPENBSD:#define __LDBL_DENORM_MIN__ 6.47517511943802511092443895822764655e-4966L
+// SPARC-DEFAULT:#define __LDBL_DIG__ 15
+// SPARC-NETOPENBSD:#define __LDBL_DIG__ 33
+// SPARC-DEFAULT:#define __LDBL_EPSILON__ 2.2204460492503131e-16L
+// SPARC-NETOPENBSD:#define __LDBL_EPSILON__ 1.92592994438723585305597794258492732e-34L
// SPARC:#define __LDBL_HAS_DENORM__ 1
// SPARC:#define __LDBL_HAS_INFINITY__ 1
// SPARC:#define __LDBL_HAS_QUIET_NAN__ 1
-// SPARC:#define __LDBL_MANT_DIG__ 113
-// SPARC:#define __LDBL_MAX_10_EXP__ 4932
-// SPARC:#define __LDBL_MAX_EXP__ 16384
-// SPARC:#define __LDBL_MAX__ 1.18973149535723176508575932662800702e+4932L
-// SPARC:#define __LDBL_MIN_10_EXP__ (-4931)
-// SPARC:#define __LDBL_MIN_EXP__ (-16381)
-// SPARC:#define __LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L
+// SPARC-DEFAULT:#define __LDBL_MANT_DIG__ 53
+// SPARC-NETOPENBSD:#define __LDBL_MANT_DIG__ 113
+// SPARC-DEFAULT:#define __LDBL_MAX_10_EXP__ 308
+// SPARC-NETOPENBSD:#define __LDBL_MAX_10_EXP__ 4932
+// SPARC-DEFAULT:#define __LDBL_MAX_EXP__ 1024
+// SPARC-NETOPENBSD:#define __LDBL_MAX_EXP__ 16384
+// SPARC-DEFAULT:#define __LDBL_MAX__ 1.7976931348623157e+308L
+// SPARC-NETOPENBSD:#define __LDBL_MAX__ 1.18973149535723176508575932662800702e+4932L
+// SPARC-DEFAULT:#define __LDBL_MIN_10_EXP__ (-307)
+// SPARC-NETOPENBSD:#define __LDBL_MIN_10_EXP__ (-4931)
+// SPARC-DEFAULT:#define __LDBL_MIN_EXP__ (-1021)
+// SPARC-NETOPENBSD:#define __LDBL_MIN_EXP__ (-16381)
+// SPARC-DEFAULT:#define __LDBL_MIN__ 2.2250738585072014e-308L
+// SPARC-NETOPENBSD:#define __LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L
// SPARC:#define __LONG_LONG_MAX__ 9223372036854775807LL
// SPARC:#define __LONG_MAX__ 2147483647L
// SPARC-NOT:#define __LP64__
@@ -1150,7 +1160,8 @@
// SPARC:#define __SIZEOF_DOUBLE__ 8
// SPARC:#define __SIZEOF_FLOAT__ 4
// SPARC:#define __SIZEOF_INT__ 4
-// SPARC:#define __SIZEOF_LONG_DOUBLE__ 16
+// SPARC-DEFAULT:#define __SIZEOF_LONG_DOUBLE__ 8
+// SPARC-NETOPENBSD:#define __SIZEOF_LONG_DOUBLE__ 16
// SPARC:#define __SIZEOF_LONG_LONG__ 8
// SPARC:#define __SIZEOF_LONG__ 4
// SPARC:#define __SIZEOF_POINTER__ 4
diff --git a/clang/test/Preprocessor/predefined-arch-macros.c b/clang/test/Preprocessor/predefined-arch-macros.c
index 01a132d84daba..1eb28de306421 100644
--- a/clang/test/Preprocessor/predefined-arch-macros.c
+++ b/clang/test/Preprocessor/predefined-arch-macros.c
@@ -4792,6 +4792,15 @@
// RUN: | FileCheck -match-full-lines %s -check-prefix=CHECK_SPARC_LDBL
// CHECK_SPARC_LDBL: #define __LONG_DOUBLE_128__ 1
+// RUN: %clang -E -dM %s -o - 2>&1 \
+// RUN: -target sparc-unknown-none \
+// RUN: | FileCheck -match-full-lines %s -check-prefix=CHECK_SPARC_LDBL64
+// RUN: %clang -E -dM %s -o - 2>&1 \
+// RUN: -target sparc-unknown-rtems \
+// RUN: | FileCheck -match-full-lines %s -check-prefix=CHECK_SPARC_LDBL64
+// CHECK_SPARC_LDBL64-NOT: #define __LONG_DOUBLE_128__ 1
+// CHECK_SPARC_LDBL64: #define __SIZEOF_LONG_DOUBLE__ 8
+
// RUN: %clang -mcpu=v9 -E -dM %s -o - 2>&1 \
// RUN: -target sparc-unknown-linux \
// RUN: | FileCheck -match-full-lines %s -check-prefix=CHECK_SPARC-V9
>From 35017a31fac7420c2fadeb7cf2535fbaba2acd68 Mon Sep 17 00:00:00 2001
From: Daniel Cederman <cederman at gaisler.com>
Date: Mon, 14 Sep 2026 10:43:09 +0200
Subject: [PATCH 2/2] [Sparc][clang] Use 64-bit long double on bare-metal
targets v2
Move size decision to Triple::getDefaultLongDoubleFormat().
Added more tests.
---
clang/lib/Basic/Targets/Sparc.h | 8 +++-----
.../CodeGen/Sparc/sparc-long-double-abi.c | 8 ++++++++
.../Preprocessor/predefined-arch-macros.c | 3 +++
llvm/lib/TargetParser/Triple.cpp | 8 ++++++--
llvm/unittests/TargetParser/TripleTest.cpp | 19 ++++++++++++++-----
5 files changed, 34 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Basic/Targets/Sparc.h b/clang/lib/Basic/Targets/Sparc.h
index 1d0cebaa18496..dfb34f3cb45ed 100644
--- a/clang/lib/Basic/Targets/Sparc.h
+++ b/clang/lib/Basic/Targets/Sparc.h
@@ -167,11 +167,9 @@ class LLVM_LIBRARY_VISIBILITY SparcV8TargetInfo : public SparcTargetInfo {
break;
}
- // Bare-metal and RTEMS targets default to 64-bit long double, matching
- // GCC. Other targets use 128-bit per the SPARCv8 System V ABI, with
- // 64-bit alignment.
- if (getTriple().getOS() == llvm::Triple::UnknownOS ||
- getTriple().getOS() == llvm::Triple::RTEMS) {
+ // Base long double format on the triple.
+ if (getTriple().getDefaultLongDoubleFormat() ==
+ llvm::LongDoubleFormat::IEEEdouble) {
LongDoubleWidth = 64;
LongDoubleAlign = 64;
LongDoubleFormat = &llvm::APFloat::IEEEdouble();
diff --git a/clang/test/CodeGen/Sparc/sparc-long-double-abi.c b/clang/test/CodeGen/Sparc/sparc-long-double-abi.c
index e022643e87adc..0cc2d666d1642 100644
--- a/clang/test/CodeGen/Sparc/sparc-long-double-abi.c
+++ b/clang/test/CodeGen/Sparc/sparc-long-double-abi.c
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple sparc-unknown-none -O1 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple sparc-unknown-linux-gnu -O1 -emit-llvm -o - %s | FileCheck %s --check-prefix=LINUX
void sink(long double);
void vararg(int, ...);
@@ -15,3 +16,10 @@ long double test(long double x) {
// CHECK: {{.*}}call void (i32, ...) @vararg(i32 noundef 0, double noundef %x)
// CHECK: [[RESULT:%.*]] = {{.*}}call double @source()
// CHECK: ret double [[RESULT]]
+
+// Linux retains the SPARC V8 System V 128-bit long double ABI.
+// LINUX-LABEL: define{{.*}} void @test(ptr {{.*}}sret(fp128) align 8{{.*}}, ptr {{.*}}byval(fp128) align 8{{.*}})
+// LINUX: {{.*}}call void @sink(ptr {{.*}}byval(fp128) align 8{{.*}})
+// LINUX: {{.*}}call void (i32, ...) @vararg(i32 noundef 0, ptr {{.*}}byval(fp128) align 8{{.*}})
+// LINUX: {{.*}}call void @source(ptr {{.*}}sret(fp128) align 8{{.*}})
+// LINUX: ret void
diff --git a/clang/test/Preprocessor/predefined-arch-macros.c b/clang/test/Preprocessor/predefined-arch-macros.c
index 1eb28de306421..c9295a5f2ab3c 100644
--- a/clang/test/Preprocessor/predefined-arch-macros.c
+++ b/clang/test/Preprocessor/predefined-arch-macros.c
@@ -4790,6 +4790,9 @@
// RUN: %clang -E -dM %s -o - 2>&1 \
// RUN: -target sparc-unknown-linux \
// RUN: | FileCheck -match-full-lines %s -check-prefix=CHECK_SPARC_LDBL
+// RUN: %clang -E -dM %s -o - 2>&1 \
+// RUN: -target sparc-sun-solaris \
+// RUN: | FileCheck -match-full-lines %s -check-prefix=CHECK_SPARC_LDBL
// CHECK_SPARC_LDBL: #define __LONG_DOUBLE_128__ 1
// RUN: %clang -E -dM %s -o - 2>&1 \
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index 7d67961256f92..f44bc98852c12 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -2545,14 +2545,18 @@ LongDoubleFormat Triple::getDefaultLongDoubleFormat() const {
case riscv64:
case riscv32be:
case riscv64be:
- case sparc:
- case sparcel:
case sparcv9:
case systemz:
case ve:
case wasm32:
case wasm64:
return LongDoubleFormat::IEEEquad;
+ case sparc:
+ case sparcel:
+ // GCC uses IEEE double for bare-metal and RTEMS SPARC V8 targets.
+ if (getOS() == UnknownOS || getOS() == RTEMS)
+ return LongDoubleFormat::IEEEdouble;
+ return LongDoubleFormat::IEEEquad;
case ppc:
case ppcle:
case ppc64:
diff --git a/llvm/unittests/TargetParser/TripleTest.cpp b/llvm/unittests/TargetParser/TripleTest.cpp
index e7a6b056702c3..976765c39eaff 100644
--- a/llvm/unittests/TargetParser/TripleTest.cpp
+++ b/llvm/unittests/TargetParser/TripleTest.cpp
@@ -1787,17 +1787,26 @@ TEST(TripleTest, DefaultLongDoubleFormat) {
LongDoubleFormat::IEEEdouble,
Triple("thumbv7-unknown-linux-gnueabi").getDefaultLongDoubleFormat());
- // Targets that use IEEE quad, independent of the environment.
- EXPECT_EQ(LongDoubleFormat::IEEEquad,
- Triple("s390x-unknown-linux-gnu").getDefaultLongDoubleFormat());
- EXPECT_EQ(LongDoubleFormat::IEEEquad,
- Triple("s390x-unknown-linux").getDefaultLongDoubleFormat());
+ // SPARC V8 uses IEEE double for bare-metal and RTEMS targets. Linux,
+ // Solaris, and other targets use IEEE quad. SPARC V9 always uses IEEE quad.
+ EXPECT_EQ(LongDoubleFormat::IEEEdouble,
+ Triple("sparc-unknown-unknown").getDefaultLongDoubleFormat());
+ EXPECT_EQ(LongDoubleFormat::IEEEdouble,
+ Triple("sparc-unknown-rtems").getDefaultLongDoubleFormat());
EXPECT_EQ(LongDoubleFormat::IEEEquad,
Triple("sparc-unknown-linux-gnu").getDefaultLongDoubleFormat());
EXPECT_EQ(LongDoubleFormat::IEEEquad,
Triple("sparcel-unknown-linux-gnu").getDefaultLongDoubleFormat());
+ EXPECT_EQ(LongDoubleFormat::IEEEquad,
+ Triple("sparc-unknown-solaris").getDefaultLongDoubleFormat());
EXPECT_EQ(LongDoubleFormat::IEEEquad,
Triple("sparcv9-unknown-linux-gnu").getDefaultLongDoubleFormat());
+
+ // Targets that use IEEE quad, independent of the environment.
+ EXPECT_EQ(LongDoubleFormat::IEEEquad,
+ Triple("s390x-unknown-linux-gnu").getDefaultLongDoubleFormat());
+ EXPECT_EQ(LongDoubleFormat::IEEEquad,
+ Triple("s390x-unknown-linux").getDefaultLongDoubleFormat());
EXPECT_EQ(LongDoubleFormat::IEEEquad,
Triple("riscv32-unknown-linux-gnu").getDefaultLongDoubleFormat());
EXPECT_EQ(LongDoubleFormat::IEEEquad,
More information about the llvm-commits
mailing list