[clang] [Sparc][clang] `_Complex char` in c-variadic call is right-adjusted (PR #215015)
Folkert de Vries via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 8 12:53:06 PDT 2026
https://github.com/folkertdev created https://github.com/llvm/llvm-project/pull/215015
Aggregates are left-adjusted, but GCC right-adjusts complex values within the 8-byte slot
https://godbolt.org/z/as3PevMGd
```c
#include <stdarg.h>
_Complex char complex_char_sink;
void get_complex_char(va_list *args) {
complex_char_sink = va_arg(*args, _Complex char);
}
struct ManualComplexChar { char re; char im };
struct ManualComplexChar manual_complex_char_sink;
void get_manual_complex_char(va_list *args) {
manual_complex_char_sink = va_arg(*args, struct ManualComplexChar);
}
```
Note how Clang has
```asm
ldub [%o1], %o0
ldub [%o1+1], %o1
```
but GCC has
```asm
ldub [%g1+6], %g3
ldub [%g1+7], %g2
```
For `ManualComplexChar` GCC does pass it left-aligned:
```asm
ldub [%g1], %g2
stb %g2, [%l7]
ldub [%g1+1], %g2
```
I'm not sure about `IsComplexGnuAbi` here. It's easy to add but then you carry it forever.
>From fc05816f82b58fb46ebcd4cd72211f5bbc648f01 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 8 Aug 2026 21:42:16 +0200
Subject: [PATCH] [Sparc][clang] `_Complex char` in c-variadic call is
right-adjusted
---
clang/lib/CodeGen/Targets/Sparc.cpp | 5 ++++-
clang/test/CodeGen/Sparc/sparcv9-vaarg.c | 15 ++++++++-------
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index e23771653e251..25f0009fe7a09 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -394,11 +394,14 @@ RValue SparcV9ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
// Zero-sized types have a width of one byte for parameter passing purposes.
TInfo.Width = std::max(TInfo.Width, CharUnits::fromQuantity(1));
+ // Small _Complex types are right-adjusted, but small aggregates are not.
+ bool ForceRightAdjust = Ty->isAnyComplexType();
+
// Arguments bigger than 2*SlotSize bytes are passed indirectly.
return emitVoidPtrVAArg(CGF, VAListAddr, Ty,
/*IsIndirect=*/TInfo.Width > 2 * SlotSize, TInfo,
SlotSize,
- /*AllowHigherAlign=*/true, Slot);
+ /*AllowHigherAlign=*/true, Slot, ForceRightAdjust);
}
void SparcV9ABIInfo::computeInfo(CGFunctionInfo &FI) const {
diff --git a/clang/test/CodeGen/Sparc/sparcv9-vaarg.c b/clang/test/CodeGen/Sparc/sparcv9-vaarg.c
index a23d9f1c6506f..8c9578e4eacc6 100644
--- a/clang/test/CodeGen/Sparc/sparcv9-vaarg.c
+++ b/clang/test/CodeGen/Sparc/sparcv9-vaarg.c
@@ -149,7 +149,7 @@ __int128 get_int128(va_list *args) {
_Complex char complex_char_sink;
-// FIXME: _Complex char should be passed in the right-most bytes of the slot, using a getelementptr with a value of 6.
+// _Complex char is passed in the right-most bytes of the slot, note the getelementptr with a value of 6.
// CHECK-LABEL: define dso_local void @get_complex_char(
// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
@@ -159,12 +159,13 @@ _Complex char complex_char_sink;
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
-// CHECK-NEXT: [[ARGP_CUR_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[ARGP_CUR]], i32 0, i32 0
-// CHECK-NEXT: [[ARGP_CUR_REAL:%.*]] = load i8, ptr [[ARGP_CUR_REALP]], align 8
-// CHECK-NEXT: [[ARGP_CUR_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[ARGP_CUR]], i32 0, i32 1
-// CHECK-NEXT: [[ARGP_CUR_IMAG:%.*]] = load i8, ptr [[ARGP_CUR_IMAGP]], align 1
-// CHECK-NEXT: store i8 [[ARGP_CUR_REAL]], ptr @complex_char_sink, align 1
-// CHECK-NEXT: store i8 [[ARGP_CUR_IMAG]], ptr getelementptr inbounds nuw (i8, ptr @complex_char_sink, i64 1), align 1
+// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 6
+// CHECK-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[TMP1]], i32 0, i32 0
+// CHECK-NEXT: [[DOTREAL:%.*]] = load i8, ptr [[DOTREALP]], align 2
+// CHECK-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[TMP1]], i32 0, i32 1
+// CHECK-NEXT: [[DOTIMAG:%.*]] = load i8, ptr [[DOTIMAGP]], align 1
+// CHECK-NEXT: store i8 [[DOTREAL]], ptr @complex_char_sink, align 1
+// CHECK-NEXT: store i8 [[DOTIMAG]], ptr getelementptr inbounds nuw (i8, ptr @complex_char_sink, i64 1), align 1
// CHECK-NEXT: ret void
//
void get_complex_char(va_list *args) {
More information about the cfe-commits
mailing list