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
