[clang] 246ee06 - [RISCV] make `_Complex {integer}` consistent with GCC (#216404)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 15 06:36:11 PDT 2026
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 at 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;
More information about the cfe-commits
mailing list