[clang] [clang][SPARC] Treat empty structs as if it's a one-bit type in the CC (PR #90338)
via cfe-commits
cfe-commits at lists.llvm.org
Wed May 1 15:54:00 PDT 2024
https://github.com/koachan updated https://github.com/llvm/llvm-project/pull/90338
>From 5935e661941fd681b2bf6b3d915e97fe0d73fcd8 Mon Sep 17 00:00:00 2001
From: Koakuma <koachan at protonmail.com>
Date: Thu, 25 Apr 2024 22:37:03 +0700
Subject: [PATCH 1/2] [clang][SPARC] Treat empty structs as if it's a one-bit
type in the CC
Make sure that empty structs are treated as if it has a size of
one bit in function parameters and return types so that it occupies
a full argument and/or return register slot.
This fixes crashes and miscompilations when passing and/or returning
empty structs.
---
clang/lib/CodeGen/Targets/Sparc.cpp | 5 ++++-
clang/test/CodeGen/sparcv9-abi.c | 6 ++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index 9025a633f328e2..b82e9a69e19671 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -263,7 +263,10 @@ SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit) const {
CoerceBuilder CB(getVMContext(), getDataLayout());
CB.addStruct(0, StrTy);
- CB.pad(llvm::alignTo(CB.DL.getTypeSizeInBits(StrTy), 64));
+ // All structs, even empty ones, should take up a register argument slot,
+ // so pin the minimum struct size to one bit.
+ CB.pad(llvm::alignTo(
+ std::max(CB.DL.getTypeSizeInBits(StrTy).getKnownMinValue(), 1UL), 64));
// Try to use the original type for coercion.
llvm::Type *CoerceTy = CB.isUsableType(StrTy) ? StrTy : CB.getType();
diff --git a/clang/test/CodeGen/sparcv9-abi.c b/clang/test/CodeGen/sparcv9-abi.c
index 5e74a9a883cefa..360bd9d9019e56 100644
--- a/clang/test/CodeGen/sparcv9-abi.c
+++ b/clang/test/CodeGen/sparcv9-abi.c
@@ -21,6 +21,12 @@ char f_int_4(char x) { return x; }
// CHECK-LABEL: define{{.*}} fp128 @f_ld(fp128 noundef %x)
long double f_ld(long double x) { return x; }
+// Empty struct is lowered as a placeholder word parameter.
+struct empty {};
+
+// CHECK-LABEL: define{{.*}} i64 @f_empty(i64 %x.coerce)
+struct empty f_empty(struct empty x) { return x; }
+
// Small structs are passed in registers.
struct small {
int *a, *b;
>From e93fd507429a1fb3be2618caa20656a94bbaa6a5 Mon Sep 17 00:00:00 2001
From: Koakuma <koachan at protonmail.com>
Date: Thu, 2 May 2024 05:52:02 +0700
Subject: [PATCH 2/2] Add more tests to check for corner cases
---
clang/test/CodeGen/sparcv9-abi.c | 37 +++++++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/clang/test/CodeGen/sparcv9-abi.c b/clang/test/CodeGen/sparcv9-abi.c
index 360bd9d9019e56..616e24e7c519d8 100644
--- a/clang/test/CodeGen/sparcv9-abi.c
+++ b/clang/test/CodeGen/sparcv9-abi.c
@@ -21,12 +21,47 @@ char f_int_4(char x) { return x; }
// CHECK-LABEL: define{{.*}} fp128 @f_ld(fp128 noundef %x)
long double f_ld(long double x) { return x; }
-// Empty struct is lowered as a placeholder word parameter.
+// Zero-sized structs reserves an argument register slot if passed directly.
struct empty {};
+struct emptyarr { struct empty a[10]; };
// CHECK-LABEL: define{{.*}} i64 @f_empty(i64 %x.coerce)
struct empty f_empty(struct empty x) { return x; }
+// CHECK-LABEL: define{{.*}} i64 @f_emptyarr(i64 %x.coerce)
+struct empty f_emptyarr(struct emptyarr x) { return x.a[0]; }
+
+// CHECK-LABEL: define{{.*}} i64 @f_emptyvar(i32 noundef zeroext %count, ...)
+long f_emptyvar(unsigned count, ...) {
+ long ret;
+ va_list args;
+ va_start(args, count);
+
+// CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %args
+// CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 8
+// CHECK-DAG: store ptr %[[NXT]], ptr %args
+ va_arg(args, struct empty);
+
+// CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %args
+// CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 8
+// CHECK-DAG: store ptr %[[NXT]], ptr %args
+// CHECK-DAG: load i64, ptr %[[CUR]]
+ ret = va_arg(args, long);
+ va_end(args);
+ return ret;
+}
+
+// If the zero-sized struct is contained in a non-zero-sized struct,
+// though, it doesn't reserve any registers.
+struct emptymixed { struct empty a; long b; };
+struct emptyflex { unsigned count; struct empty data[10]; };
+
+// CHECK-LABEL: define{{.*}} i64 @f_emptymixed(i64 %x.coerce)
+long f_emptymixed(struct emptymixed x) { return x.b; }
+
+// CHECK-LABEL: define{{.*}} i64 @f_emptyflex(i64 %x.coerce, i64 noundef %y)
+long f_emptyflex(struct emptyflex x, long y) { return y; }
+
// Small structs are passed in registers.
struct small {
int *a, *b;
More information about the cfe-commits
mailing list