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 <[email protected]>
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) {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to