[clang] [Sparc][clang] Match GCC's SPARC V8 vector argument passing (PR #222254)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 9 00:15:27 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: Daniel Cederman (doac)
<details>
<summary>Changes</summary>
Pass floating-point vectors and vectors larger than 64 bits indirectly.
https://godbolt.org/z/WKfd1eTxe
Assisted-by: Codex
---
Full diff: https://github.com/llvm/llvm-project/pull/222254.diff
2 Files Affected:
- (modified) clang/lib/CodeGen/Targets/Sparc.cpp (+7)
- (modified) clang/test/CodeGen/Sparc/sparc-vaarg.c (+149)
``````````diff
diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index f5a17aa51184a..a8ba31c10af71 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -83,6 +83,13 @@ ABIArgInfo SparcV8ABIInfo::classifyArgumentType(QualType Ty) const {
if (const auto *CT = Ty->getAs<ComplexType>())
return classifyComplexType(CT, /*IsRet=*/false);
+ // Pass floating-point vectors and vectors larger than 64 bits by reference.
+ if (const auto *VT = Ty->getAs<VectorType>()) {
+ uint64_t SizeInBits = getContext().getTypeSize(Ty);
+ if (VT->getElementType()->isRealFloatingType() || SizeInBits > 64)
+ return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
+ }
+
const auto *BT = Ty->getAs<BuiltinType>();
if (BT && BT->getKind() == BuiltinType::LongDouble)
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
diff --git a/clang/test/CodeGen/Sparc/sparc-vaarg.c b/clang/test/CodeGen/Sparc/sparc-vaarg.c
index f322229e20c43..7be4c1665c3c4 100644
--- a/clang/test/CodeGen/Sparc/sparc-vaarg.c
+++ b/clang/test/CodeGen/Sparc/sparc-vaarg.c
@@ -219,3 +219,152 @@ _Complex long double complex_long_double_sink;
void get_complex_long_double(va_list *args) {
complex_long_double_sink = va_arg(*args, _Complex long double);
}
+
+typedef float float2 __attribute__((vector_size(8)));
+float2 float2_sink;
+
+// Floating-point vectors are passed indirectly, note how ARGP_CUR is advanced
+// by 4.
+// CHECK-LABEL: define dso_local void @get_float2(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[ARGS_ADDR:%.*]] = alloca ptr, align 4
+// CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4
+// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4
+// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
+// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4
+// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
+// CHECK-NEXT: [[TMP2:%.*]] = load <2 x float>, ptr [[TMP1]], align 8
+// CHECK-NEXT: store <2 x float> [[TMP2]], ptr @float2_sink, align 8
+// CHECK-NEXT: ret void
+//
+void get_float2(va_list *args) {
+ float2_sink = va_arg(*args, float2);
+}
+
+typedef int int4 __attribute__((vector_size(16)));
+int4 int4_sink;
+
+// Integer vectors larger than 8 bytes are also passed indirectly.
+// CHECK-LABEL: define dso_local void @get_int4(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[ARGS_ADDR:%.*]] = alloca ptr, align 4
+// CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4
+// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4
+// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
+// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4
+// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
+// CHECK-NEXT: [[TMP2:%.*]] = load <4 x i32>, ptr [[TMP1]], align 16
+// CHECK-NEXT: store <4 x i32> [[TMP2]], ptr @int4_sink, align 16
+// CHECK-NEXT: ret void
+//
+void get_int4(va_list *args) {
+ int4_sink = va_arg(*args, int4);
+}
+
+extern void variadic_vector_sink(int, ...);
+
+// Verify that call lowering uses the same indirect convention as va_arg.
+// CHECK-LABEL: define dso_local void @pass_float2(
+// CHECK-SAME: ptr noundef byval(<2 x float>) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[VALUE_ADDR:%.*]] = alloca <2 x float>, align 8
+// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca <2 x float>, align 8
+// CHECK-NEXT: [[VALUE:%.*]] = load <2 x float>, ptr [[TMP0]], align 8
+// CHECK-NEXT: store <2 x float> [[VALUE]], ptr [[VALUE_ADDR]], align 8
+// CHECK-NEXT: [[TMP1:%.*]] = load <2 x float>, ptr [[VALUE_ADDR]], align 8
+// CHECK-NEXT: store <2 x float> [[TMP1]], ptr [[BYVAL_TEMP]], align 8
+// CHECK-NEXT: call void (i32, ...) @variadic_vector_sink(i32 noundef 0, ptr noundef byval(<2 x float>) align 8 [[BYVAL_TEMP]])
+// CHECK-NEXT: ret void
+//
+void pass_float2(float2 value) {
+ variadic_vector_sink(0, value);
+}
+
+// CHECK-LABEL: define dso_local void @pass_int4(
+// CHECK-SAME: ptr noundef byval(<4 x i32>) align 16 [[TMP0:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[VALUE_ADDR:%.*]] = alloca <4 x i32>, align 16
+// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca <4 x i32>, align 16
+// CHECK-NEXT: [[VALUE:%.*]] = load <4 x i32>, ptr [[TMP0]], align 16
+// CHECK-NEXT: store <4 x i32> [[VALUE]], ptr [[VALUE_ADDR]], align 16
+// CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, ptr [[VALUE_ADDR]], align 16
+// CHECK-NEXT: store <4 x i32> [[TMP1]], ptr [[BYVAL_TEMP]], align 16
+// CHECK-NEXT: call void (i32, ...) @variadic_vector_sink(i32 noundef 0, ptr noundef byval(<4 x i32>) align 16 [[BYVAL_TEMP]])
+// CHECK-NEXT: ret void
+//
+void pass_int4(int4 value) {
+ variadic_vector_sink(0, value);
+}
+
+typedef int int2 __attribute__((vector_size(8)));
+int2 int2_sink;
+
+// Integer vectors up to 8 bytes remain direct.
+// CHECK-LABEL: define dso_local void @get_int2(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[ARGS_ADDR:%.*]] = alloca ptr, align 4
+// CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4
+// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4
+// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 8
+// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4
+// CHECK-NEXT: [[TMP1:%.*]] = load <2 x i32>, ptr [[ARGP_CUR]], align 4
+// CHECK-NEXT: store <2 x i32> [[TMP1]], ptr @int2_sink, align 8
+// CHECK-NEXT: ret void
+//
+void get_int2(va_list *args) {
+ int2_sink = va_arg(*args, int2);
+}
+
+// CHECK-LABEL: define dso_local void @pass_int2(
+// CHECK-SAME: <2 x i32> noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[VALUE_ADDR:%.*]] = alloca <2 x i32>, align 8
+// CHECK-NEXT: store <2 x i32> [[VALUE]], ptr [[VALUE_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load <2 x i32>, ptr [[VALUE_ADDR]], align 8
+// CHECK-NEXT: call void (i32, ...) @variadic_vector_sink(i32 noundef 0, <2 x i32> noundef [[TMP0]])
+// CHECK-NEXT: ret void
+//
+void pass_int2(int2 value) {
+ variadic_vector_sink(0, value);
+}
+
+typedef signed char char2 __attribute__((vector_size(2)));
+char2 char2_sink;
+
+// Sub-slot integer vectors remain direct.
+// CHECK-LABEL: define dso_local void @get_char2(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[ARGS_ADDR:%.*]] = alloca ptr, align 4
+// CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4
+// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4
+// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
+// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4
+// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 2
+// CHECK-NEXT: [[TMP2:%.*]] = load <2 x i8>, ptr [[TMP1]], align 2
+// CHECK-NEXT: store <2 x i8> [[TMP2]], ptr @char2_sink, align 2
+// CHECK-NEXT: ret void
+//
+void get_char2(va_list *args) {
+ char2_sink = va_arg(*args, char2);
+}
+
+// CHECK-LABEL: define dso_local void @pass_char2(
+// CHECK-SAME: <2 x i8> noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[VALUE_ADDR:%.*]] = alloca <2 x i8>, align 2
+// CHECK-NEXT: store <2 x i8> [[VALUE]], ptr [[VALUE_ADDR]], align 2
+// CHECK-NEXT: [[TMP0:%.*]] = load <2 x i8>, ptr [[VALUE_ADDR]], align 2
+// CHECK-NEXT: call void (i32, ...) @variadic_vector_sink(i32 noundef 0, <2 x i8> noundef [[TMP0]])
+// CHECK-NEXT: ret void
+//
+void pass_char2(char2 value) {
+ variadic_vector_sink(0, value);
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/222254
More information about the cfe-commits
mailing list