https://github.com/folkertdev updated https://github.com/llvm/llvm-project/pull/227130
>From 236a98c7a712d2a2ccf066791df177bff2f73e43 Mon Sep 17 00:00:00 2001 From: Folkert de Vries <[email protected]> Date: Mon, 28 Sep 2026 23:19:06 +0200 Subject: [PATCH 1/3] [clang][X86] Don't pass non-float/double floating-point types in regparm registers --- clang/lib/CodeGen/Targets/X86.cpp | 6 ++---- clang/test/CodeGen/regparm-struct.c | 26 +++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp index 21f9929cfa882..56cf23b1e9246 100644 --- a/clang/lib/CodeGen/Targets/X86.cpp +++ b/clang/lib/CodeGen/Targets/X86.cpp @@ -639,11 +639,9 @@ X86_32ABIInfo::Class X86_32ABIInfo::classify(QualType Ty) const { if (!T) T = Ty.getTypePtr(); - if (const BuiltinType *BT = T->getAs<BuiltinType>()) { - BuiltinType::Kind K = BT->getKind(); - if (K == BuiltinType::Float || K == BuiltinType::Double) + if (const BuiltinType *BT = T->getAs<BuiltinType>()) + if (BT->isFloatingPoint()) return Float; - } return Integer; } diff --git a/clang/test/CodeGen/regparm-struct.c b/clang/test/CodeGen/regparm-struct.c index a533f6cedbba2..54c0ded11e273 100644 --- a/clang/test/CodeGen/regparm-struct.c +++ b/clang/test/CodeGen/regparm-struct.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple i386-unknown-linux-gnu -target-feature +sse2 %s -emit-llvm -o - | FileCheck %s __attribute__((regparm(3))) void f1(int a, int b, int c, int d); // CHECK: declare void @f1(i32 inreg noundef, i32 inreg noundef, i32 inreg noundef, i32 noundef) @@ -175,3 +175,27 @@ void g18(void) { struct s13 x = {{41}}; f18(x, 42, 43, 44); } + +__attribute__((regparm(3))) void pass_f16(_Float16 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_f16(half noundef %a, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) + +__attribute__((regparm(3))) void pass_bf16(__bf16 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_bf16(bfloat noundef %a, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) + +__attribute__((regparm(3))) void pass_f80(long double a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_f80(x86_fp80 noundef %a, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) + +__attribute__((regparm(3))) void pass_f128(__float128 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_f128(fp128 noundef %a, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) + +struct s14 { + _Float16 x1; +}; +__attribute__((regparm(3))) void pass_wrapped_f16(struct s14 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_wrapped_f16(ptr noundef byval(%struct.s14) align 4 %a, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) + +struct s15 { + __float128 x1; +}; +__attribute__((regparm(3))) void pass_wrapped_f128(struct s15 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_wrapped_f128(ptr noundef byval(%struct.s15) align 4 {{%[^,]*}}, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) >From f8de6469c414c4f3781b30f9b242705a21b084ac Mon Sep 17 00:00:00 2001 From: Folkert de Vries <[email protected]> Date: Mon, 28 Sep 2026 23:19:13 +0200 Subject: [PATCH 2/3] [clang][X86] Don't pass _Complex values in regparm registers --- clang/lib/CodeGen/Targets/X86.cpp | 93 +++++++++++++++++++++++++---- clang/test/CodeGen/regparm-struct.c | 24 ++++++++ 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp index 56cf23b1e9246..57229e33d25f9 100644 --- a/clang/lib/CodeGen/Targets/X86.cpp +++ b/clang/lib/CodeGen/Targets/X86.cpp @@ -101,10 +101,7 @@ struct CCState { /// X86_32ABIInfo - The X86-32 ABI information. class X86_32ABIInfo : public ABIInfo { - enum Class { - Integer, - Float - }; + enum Class { Integer, Float, AlwaysStack }; static const unsigned MinABIStackAlignInBytes = 4; @@ -634,23 +631,93 @@ ABIArgInfo X86_32ABIInfo::getIndirectResult(QualType Ty, bool ByVal, Realign); } +/// Like isSingleElementStruct, but only looks through one level of records. +/// This mirrors how GCC gives a record the mode of its only field. +static const Type *getSingleFieldType(QualType T, ASTContext &Context) { + const auto *RD = T->getAsRecordDecl(); + if (!RD) + return nullptr; + + if (RD->hasFlexibleArrayMember()) + return nullptr; + + const Type *Found = nullptr; + + // If this is a C++ record, check the bases first. + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { + for (const auto &I : CXXRD->bases()) { + // Ignore empty records. + if (isEmptyRecord(Context, I.getType(), true)) + continue; + + // If we already found an element then this isn't a single-element struct. + if (Found) + return nullptr; + + Found = I.getType().getTypePtr(); + } + } + + // Check for single element. + for (const auto *FD : RD->fields()) { + QualType FT = FD->getType(); + + // Ignore empty fields. + if (isEmptyField(Context, FD, true)) + continue; + + // If we already found an element then this isn't a single-element + // struct. + if (Found) + return nullptr; + + // Treat single element arrays as the element. + while (const ConstantArrayType *AT = Context.getAsConstantArrayType(FT)) { + if (AT->getZExtSize() != 1) + break; + FT = AT->getElementType(); + } + + Found = FT.getTypePtr(); + } + + // We don't consider a struct a single-element struct if it has + // padding beyond the element type. + if (Found && Context.getTypeSize(Found) != Context.getTypeSize(T)) + return nullptr; + + return Found; +} + X86_32ABIInfo::Class X86_32ABIInfo::classify(QualType Ty) const { - const Type *T = isSingleElementStruct(Ty, getContext()); - if (!T) - T = Ty.getTypePtr(); + while (Ty->getAsRecordDecl()) { + const Type *FT = getSingleFieldType(Ty, getContext()); - if (const BuiltinType *BT = T->getAs<BuiltinType>()) + // Pick integer for zero-sized types. They will be ignored down the line. + if (!FT) + return Integer; + + Ty = QualType(FT, 0); + } + + // All complex values are passed via the stack. + if (Ty->isAnyComplexType()) + return AlwaysStack; + + // Types like bfloat16 or __float128 are all passed like floats. + if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) if (BT->isFloatingPoint()) return Float; + return Integer; } bool X86_32ABIInfo::updateFreeRegs(QualType Ty, CCState &State) const { - if (!IsSoftFloatABI) { - Class C = classify(Ty); - if (C == Float) - return false; - } + Class C = classify(Ty); + if (C == AlwaysStack && !IsMCUABI) + return false; + if (C == Float && !IsSoftFloatABI) + return false; unsigned Size = getContext().getTypeSize(Ty); unsigned SizeInRegs = (Size + 31) / 32; diff --git a/clang/test/CodeGen/regparm-struct.c b/clang/test/CodeGen/regparm-struct.c index 54c0ded11e273..6346cec9653fe 100644 --- a/clang/test/CodeGen/regparm-struct.c +++ b/clang/test/CodeGen/regparm-struct.c @@ -199,3 +199,27 @@ struct s15 { }; __attribute__((regparm(3))) void pass_wrapped_f128(struct s15 a, int b, int c, int d) {} // CHECK-LABEL: define{{.*}} void @pass_wrapped_f128(ptr noundef byval(%struct.s15) align 4 {{%[^,]*}}, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) + +__attribute__((regparm(3))) void pass_complex_float(_Complex float a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_complex_float(ptr noundef byval({ float, float }) align 4 %a, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) + +__attribute__((regparm(3))) void pass_complex_int(int a, _Complex int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_complex_int(i32 inreg noundef %a, ptr noundef byval({ i32, i32 }) align 4 %b, i32 inreg noundef %c, i32 inreg noundef %d) + +struct s16 { + _Complex float x1; +}; +__attribute__((regparm(3))) void pass_wrapped_complex_float(struct s16 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_wrapped_complex_float(float %a.0, float %a.1, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) + +struct s17 { + _Complex int x1[1]; +}; +__attribute__((regparm(3))) void pass_wrapped_complex_int(struct s17 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_wrapped_complex_int(ptr noundef byval(%struct.s17) align 4 %a, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) + +struct s18 { + float x1[1]; +}; +__attribute__((regparm(3))) void pass_struct_singleton_array_float(struct s18 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_struct_singleton_array_float(ptr noundef byval(%struct.s18) align 4 %a, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) >From a57091159645beb0b5e2f2d49c160bb02b5051d0 Mon Sep 17 00:00:00 2001 From: Folkert de Vries <[email protected]> Date: Mon, 28 Sep 2026 23:19:20 +0200 Subject: [PATCH 3/3] [clang][X86] Don't look through unions when classifying regparm arguments --- clang/lib/CodeGen/Targets/X86.cpp | 6 +++++- clang/test/CodeGen/regparm-struct.c | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp index 57229e33d25f9..984da407f4403 100644 --- a/clang/lib/CodeGen/Targets/X86.cpp +++ b/clang/lib/CodeGen/Targets/X86.cpp @@ -690,7 +690,11 @@ static const Type *getSingleFieldType(QualType T, ASTContext &Context) { } X86_32ABIInfo::Class X86_32ABIInfo::classify(QualType Ty) const { - while (Ty->getAsRecordDecl()) { + while (const RecordDecl *RD = Ty->getAsRecordDecl()) { + // Unions are always passed as integers. + if (RD->isUnion()) + return Integer; + const Type *FT = getSingleFieldType(Ty, getContext()); // Pick integer for zero-sized types. They will be ignored down the line. diff --git a/clang/test/CodeGen/regparm-struct.c b/clang/test/CodeGen/regparm-struct.c index 6346cec9653fe..497c3a5ff0acd 100644 --- a/clang/test/CodeGen/regparm-struct.c +++ b/clang/test/CodeGen/regparm-struct.c @@ -223,3 +223,28 @@ struct s18 { }; __attribute__((regparm(3))) void pass_struct_singleton_array_float(struct s18 a, int b, int c, int d) {} // CHECK-LABEL: define{{.*}} void @pass_struct_singleton_array_float(ptr noundef byval(%struct.s18) align 4 %a, i32 inreg noundef %b, i32 inreg noundef %c, i32 inreg noundef %d) + +// Unions are passed like integers. +union u1 { + float x1; +}; +__attribute__((regparm(3))) void pass_union_float(union u1 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_union_float(i32 inreg %a.coerce, i32 inreg noundef %b, i32 inreg noundef %c, i32 noundef %d) + +union u2 { + struct s5 x1; +}; +__attribute__((regparm(3))) void pass_union_struct_float(union u2 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_union_struct_float(i32 inreg %a.coerce, i32 inreg noundef %b, i32 inreg noundef %c, i32 noundef %d) + +union u3 { + _Complex float x1; +}; +__attribute__((regparm(3))) void pass_union_complex_float(union u3 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_union_complex_float(i32 inreg %a.coerce0, i32 inreg %a.coerce1, i32 inreg noundef %b, i32 noundef %c, i32 noundef %d) + +struct s19 { + union u1 x1; +}; +__attribute__((regparm(3))) void pass_nested_union_float(struct s19 a, int b, int c, int d) {} +// CHECK-LABEL: define{{.*}} void @pass_nested_union_float(i32 inreg %a.coerce, i32 inreg noundef %b, i32 inreg noundef %c, i32 noundef %d) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
