Author: Folkert de Vries
Date: 2026-08-13T11:54:33+02:00
New Revision: d14a4f30bd01da6ea225e4fd47deb4d447976cfb

URL: 
https://github.com/llvm/llvm-project/commit/d14a4f30bd01da6ea225e4fd47deb4d447976cfb
DIFF: 
https://github.com/llvm/llvm-project/commit/d14a4f30bd01da6ea225e4fd47deb4d447976cfb.diff

LOG: [Sparc][clang] `_Complex char` in c-variadic call is right-adjusted 
(#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.

Added: 
    

Modified: 
    clang/lib/CodeGen/Targets/Sparc.cpp
    clang/test/CodeGen/Sparc/sparcv9-vaarg.c

Removed: 
    


################################################################################
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