Author: Folkert de Vries
Date: 2026-08-15T13:36:07Z
New Revision: 246ee06e2e8805255cd3f907d5e2f86422c6cac6

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

LOG: [RISCV] make `_Complex {integer}` consistent with GCC (#216404)

The Loongarch fix from https://github.com/llvm/llvm-project/pull/215222
but for riscv, which hits the same issue.

Clang appears to pass `_Complex short` and `_Complex char` like a struct
with two `short`/`char` fields, taking up 2 registers. GCC instead packs
the values together into a single register.

https://godbolt.org/z/dKhPToqcd

```c
struct S { _Complex unsigned short c; };

void callee(struct S s);

void caller(void) {
    struct S s;
    __real__ s.c = 0xC1C0;
    __imag__ s.c = 0xD1D0;
    callee(s);
}
```

Clang uses both `a0` and `a1`:

```asm
caller:
        lui     a0, 12
        lui     a1, 13
        addi    a0, a0, 448
        addi    a1, a1, 464
        tail    callee
```

GCC bitpacks everything into `a0`:

```asm
caller:
        addi    sp,sp,-16
        sd      ra,8(sp)
        li      a0,-774848512
        addiw   a0,a0,448
        call    callee@plt
        ld      ra,8(sp)
        addi    sp,sp,16
        jr      ra
```

Complex integers are a GNU extension, so clang behavior should match
GCC.

Added: 
    

Modified: 
    clang/lib/CodeGen/Targets/RISCV.cpp
    clang/test/CodeGen/RISCV/riscv32-abi.c
    clang/test/CodeGen/RISCV/riscv64-abi.c

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/Targets/RISCV.cpp 
b/clang/lib/CodeGen/Targets/RISCV.cpp
index ce2352ca76284..4bac5711a2dd6 100644
--- a/clang/lib/CodeGen/Targets/RISCV.cpp
+++ b/clang/lib/CodeGen/Targets/RISCV.cpp
@@ -225,6 +225,12 @@ bool RISCVABIInfo::detectFPCCEligibleStructHelper(QualType 
Ty, CharUnits CurOff,
     if (Field1Ty)
       return false;
     QualType EltTy = CTy->getElementType();
+    // Only floating-point complex types (e.g. _Complex float/double) are
+    // eligible to be passed in floating-point argument registers. Complex
+    // integer types (a GNU extension) should be treated like a normal
+    // aggregate and packed into GPRs instead.
+    if (!EltTy->isRealFloatingType())
+      return false;
     if (getContext().getTypeSize(EltTy) > FLen)
       return false;
     Field1Ty = CGT.ConvertType(EltTy);

diff  --git a/clang/test/CodeGen/RISCV/riscv32-abi.c 
b/clang/test/CodeGen/RISCV/riscv32-abi.c
index e9f7e6c26a0dc..fe4cb21146ee8 100644
--- a/clang/test/CodeGen/RISCV/riscv32-abi.c
+++ b/clang/test/CodeGen/RISCV/riscv32-abi.c
@@ -1888,14 +1888,15 @@ struct float16complex_s f_ret_float16complex_s(void) {
   return (struct float16complex_s){1.0};
 }
 
-// CHECK-LABEL: define{{.*}} i64 @f_ucharcomplex(i64 %x.coerce)
+// Complex integer values or structs containing a single complex
+// integer value should be passed as if it were an int+int struct.
+
 // ILP32-ILP32F-ILP32D-LABEL: define dso_local i16 @f_ucharcomplex
 // ILP32-ILP32F-ILP32D-SAME: (i16 noundef [[X_COERCE:%.*]]) #[[ATTR0]] {
 // ILP32-ILP32F-ILP32D:  entry:
 //
 unsigned char __complex__ f_ucharcomplex(unsigned char __complex__ x) { return 
x; }
 
-// CHECK-LABEL: define{{.*}} i64 @f_ushortcomplex(i64 %x.coerce)
 // ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_ushortcomplex
 // ILP32-ILP32F-ILP32D-SAME: (i32 noundef [[X_COERCE:%.*]]) #[[ATTR0]] {
 // ILP32-ILP32F-ILP32D:  entry:
@@ -1905,14 +1906,9 @@ unsigned short __complex__ f_ushortcomplex(unsigned 
short __complex__ x) { retur
 struct ucharcomplex_s {
   unsigned char __complex__ c;
 };
-// CHECK-LABEL: define{{.*}} i64 @f_ucharcomplex_s(i64 %x.coerce)
-// ILP32-LABEL: define dso_local i16 @f_ucharcomplex_s
-// ILP32-SAME: (i16 [[X_COERCE:%.*]]) #[[ATTR0]] {
-// ILP32:  entry:
-//
-// ILP32F-ILP32D-LABEL: define dso_local { i8, i8 } @f_ucharcomplex_s
-// ILP32F-ILP32D-SAME: (i8 [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] {
-// ILP32F-ILP32D:  entry:
+// ILP32-ILP32F-ILP32D-LABEL: define dso_local i16 @f_ucharcomplex_s
+// ILP32-ILP32F-ILP32D-SAME: (i16 [[X_COERCE:%.*]]) #[[ATTR0]] {
+// ILP32-ILP32F-ILP32D:  entry:
 //
 struct ucharcomplex_s f_ucharcomplex_s(struct ucharcomplex_s x) {
   return x;
@@ -1921,14 +1917,9 @@ struct ucharcomplex_s f_ucharcomplex_s(struct 
ucharcomplex_s x) {
 struct ushortcomplex_s {
   unsigned short __complex__ c;
 };
-// CHECK-LABEL: define{{.*}} i64 @f_ushortcomplex_s(i64 %x.coerce)
-// ILP32-LABEL: define dso_local i32 @f_ushortcomplex_s
-// ILP32-SAME: (i32 [[X_COERCE:%.*]]) #[[ATTR0]] {
-// ILP32:  entry:
-//
-// ILP32F-ILP32D-LABEL: define dso_local { i16, i16 } @f_ushortcomplex_s
-// ILP32F-ILP32D-SAME: (i16 [[TMP0:%.*]], i16 [[TMP1:%.*]]) #[[ATTR0]] {
-// ILP32F-ILP32D:  entry:
+// ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_ushortcomplex_s
+// ILP32-ILP32F-ILP32D-SAME: (i32 [[X_COERCE:%.*]]) #[[ATTR0]] {
+// ILP32-ILP32F-ILP32D:  entry:
 //
 struct ushortcomplex_s f_ushortcomplex_s(struct ushortcomplex_s x) {
   return x;

diff  --git a/clang/test/CodeGen/RISCV/riscv64-abi.c 
b/clang/test/CodeGen/RISCV/riscv64-abi.c
index ac5524720ee31..88a64f41f71b9 100644
--- a/clang/test/CodeGen/RISCV/riscv64-abi.c
+++ b/clang/test/CodeGen/RISCV/riscv64-abi.c
@@ -709,14 +709,15 @@ struct floatcomplex_s f_ret_floatcomplex_s(void) {
   return (struct floatcomplex_s){1.0};
 }
 
-// CHECK-LABEL: define{{.*}} i64 @f_ucharcomplex(i64 %x.coerce)
+// Complex integer values or structs containing a single complex
+// integer value should be passed as if it were an int+int struct.
+
 // LP64-LP64F-LP64D-LABEL: define dso_local i16 @f_ucharcomplex
 // LP64-LP64F-LP64D-SAME: (i16 noundef [[X_COERCE:%.*]]) #[[ATTR0]] {
 // LP64-LP64F-LP64D:  entry:
 //
 unsigned char __complex__ f_ucharcomplex(unsigned char __complex__ x) { return 
x; }
 
-// CHECK-LABEL: define{{.*}} i64 @f_ushortcomplex(i64 %x.coerce)
 // LP64-LP64F-LP64D-LABEL: define dso_local i32 @f_ushortcomplex
 // LP64-LP64F-LP64D-SAME: (i32 noundef [[X_COERCE:%.*]]) #[[ATTR0]] {
 // LP64-LP64F-LP64D:  entry:
@@ -726,14 +727,9 @@ unsigned short __complex__ f_ushortcomplex(unsigned short 
__complex__ x) { retur
 struct ucharcomplex_s {
   unsigned char __complex__ c;
 };
-// CHECK-LABEL: define{{.*}} i64 @f_ucharcomplex_s(i64 %x.coerce)
-// LP64-LABEL: define dso_local i16 @f_ucharcomplex_s
-// LP64-SAME: (i16 [[X_COERCE:%.*]]) #[[ATTR0]] {
-// LP64:  entry:
-//
-// LP64F-LP64D-LABEL: define dso_local { i8, i8 } @f_ucharcomplex_s
-// LP64F-LP64D-SAME: (i8 [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] {
-// LP64F-LP64D:  entry:
+// LP64-LP64F-LP64D-LABEL: define dso_local i16 @f_ucharcomplex_s
+// LP64-LP64F-LP64D-SAME: (i16 [[X_COERCE:%.*]]) #[[ATTR0]] {
+// LP64-LP64F-LP64D:  entry:
 //
 struct ucharcomplex_s f_ucharcomplex_s(struct ucharcomplex_s x) {
   return x;
@@ -742,14 +738,9 @@ struct ucharcomplex_s f_ucharcomplex_s(struct 
ucharcomplex_s x) {
 struct ushortcomplex_s {
   unsigned short __complex__ c;
 };
-// CHECK-LABEL: define{{.*}} i64 @f_ushortcomplex_s(i64 %x.coerce)
-// LP64-LABEL: define dso_local i32 @f_ushortcomplex_s
-// LP64-SAME: (i32 [[X_COERCE:%.*]]) #[[ATTR0]] {
-// LP64:  entry:
-//
-// LP64F-LP64D-LABEL: define dso_local { i16, i16 } @f_ushortcomplex_s
-// LP64F-LP64D-SAME: (i16 [[TMP0:%.*]], i16 [[TMP1:%.*]]) #[[ATTR0]] {
-// LP64F-LP64D:  entry:
+// LP64-LP64F-LP64D-LABEL: define dso_local i32 @f_ushortcomplex_s
+// LP64-LP64F-LP64D-SAME: (i32 [[X_COERCE:%.*]]) #[[ATTR0]] {
+// LP64-LP64F-LP64D:  entry:
 //
 struct ushortcomplex_s f_ushortcomplex_s(struct ushortcomplex_s x) {
   return x;


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

Reply via email to