[clang] ee779de - [SPARC] use `divideCeil` to calculate register offset (#213739)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 5 05:36:46 PDT 2026
Author: Folkert de Vries
Date: 2026-08-05T14:36:41+02:00
New Revision: ee779de847774cc935ec089ebb6185c790aedcf7
URL: https://github.com/llvm/llvm-project/commit/ee779de847774cc935ec089ebb6185c790aedcf7
DIFF: https://github.com/llvm/llvm-project/commit/ee779de847774cc935ec089ebb6185c790aedcf7.diff
LOG: [SPARC] use `divideCeil` to calculate register offset (#213739)
So that later arguments get the correct register alignment
https://godbolt.org/z/oaEf4Thvx
On current clang the aligned struct starts in `o1`, but with GCC it is
aligned and starts in `o2`. In practice I think only `float` could hit
this (not an int, not an aggregate, smaller than 64 bits).
Added:
Modified:
clang/include/clang/CodeGen/CGFunctionInfo.h
clang/lib/CodeGen/Targets/Sparc.cpp
clang/test/CodeGen/Sparc/sparcv9-abi.c
Removed:
################################################################################
diff --git a/clang/include/clang/CodeGen/CGFunctionInfo.h b/clang/include/clang/CodeGen/CGFunctionInfo.h
index d1fc80337d859..d9c449d78eb08 100644
--- a/clang/include/clang/CodeGen/CGFunctionInfo.h
+++ b/clang/include/clang/CodeGen/CGFunctionInfo.h
@@ -161,22 +161,24 @@ class ABIArgInfo {
return AI;
}
- static ABIArgInfo getSignExtend(QualType Ty, llvm::Type *T = nullptr) {
+ static ABIArgInfo getSignExtend(QualType Ty, llvm::Type *T = nullptr,
+ llvm::Type *Padding = nullptr) {
assert(Ty->isIntegralOrEnumerationType() && "Unexpected QualType");
auto AI = ABIArgInfo(Extend);
AI.setCoerceToType(T);
- AI.setPaddingType(nullptr);
+ AI.setPaddingType(Padding);
AI.setDirectOffset(0);
AI.setDirectAlign(0);
AI.setSignExt(true);
return AI;
}
- static ABIArgInfo getZeroExtend(QualType Ty, llvm::Type *T = nullptr) {
+ static ABIArgInfo getZeroExtend(QualType Ty, llvm::Type *T = nullptr,
+ llvm::Type *Padding = nullptr) {
assert(Ty->isIntegralOrEnumerationType() && "Unexpected QualType");
auto AI = ABIArgInfo(Extend);
AI.setCoerceToType(T);
- AI.setPaddingType(nullptr);
+ AI.setPaddingType(Padding);
AI.setDirectOffset(0);
AI.setDirectAlign(0);
AI.setZeroExt(true);
@@ -185,11 +187,12 @@ class ABIArgInfo {
// ABIArgInfo will record the argument as being extended based on the sign
// of its type. Produces a sign or zero extension.
- static ABIArgInfo getExtend(QualType Ty, llvm::Type *T = nullptr) {
+ static ABIArgInfo getExtend(QualType Ty, llvm::Type *T = nullptr,
+ llvm::Type *Padding = nullptr) {
assert(Ty->isIntegralOrEnumerationType() && "Unexpected QualType");
if (Ty->hasSignedIntegerRepresentation())
- return getSignExtend(Ty, T);
- return getZeroExtend(Ty, T);
+ return getSignExtend(Ty, T, Padding);
+ return getZeroExtend(Ty, T, Padding);
}
// Struct in register marked explicitly as not needing extension.
diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index 3fa4e84823d51..20796bd16d943 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -248,9 +248,15 @@ ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
auto &Context = getContext();
auto &VMContext = getVMContext();
- uint64_t Size = Context.getTypeSize(Ty);
+ // FIXME: the GCC-style `aligned` attribute on typedefs is not taken into
+ // account here, because the canonicalized type no longer has that
+ // information. Hence such over-aligned typedefs are not ABI-compatible with
+ // GCC.
+ //
+ // This is
diff erent from the `aligned` attribute on structs or fields, which
+ // is taken into account.
unsigned Alignment = Context.getTypeAlign(Ty);
- bool NeedPadding = (Alignment > 64) && (RegOffset % 2 != 0);
+ uint64_t Size = Context.getTypeSize(Ty);
// Anything too big to fit in registers is passed with an explicit indirect
// pointer / sret pointer.
@@ -261,26 +267,37 @@ ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
/*ByVal=*/false);
}
+ // An argument that is passed in registers but has an alignment higher than 8
+ // bytes must be register-aligned. Insert a dummy i64 argument to fill the
+ // odd-numbered register.
+ //
+ // See SCD 2.4.1, pages 3P-11 and 3P-12.
+ llvm::Type *Padding = (Alignment > 64 && RegOffset % 2 != 0)
+ ? llvm::Type::getInt64Ty(VMContext)
+ : nullptr;
+ unsigned PaddingSlots = Padding ? 1 : 0;
+ unsigned SizeSlots = llvm::divideCeil(Size, 64);
+
// Treat an enum type as its underlying type.
if (const auto *ED = Ty->getAsEnumDecl())
Ty = ED->getIntegerType();
// Integer types smaller than a register are extended.
if (Size < 64 && Ty->isIntegerType()) {
- RegOffset += 1;
- return ABIArgInfo::getExtend(Ty);
+ RegOffset += PaddingSlots + SizeSlots;
+ return ABIArgInfo::getExtend(Ty, /*T=*/nullptr, Padding);
}
if (const auto *EIT = Ty->getAs<BitIntType>())
if (EIT->getNumBits() < 64) {
- RegOffset += 1;
- return ABIArgInfo::getExtend(Ty);
+ RegOffset += PaddingSlots + SizeSlots;
+ return ABIArgInfo::getExtend(Ty, /*T=*/nullptr, Padding);
}
// Other non-aggregates go in registers.
if (!isAggregateTypeForABI(Ty)) {
- RegOffset += Size / 64;
- return ABIArgInfo::getDirect();
+ RegOffset += PaddingSlots + SizeSlots;
+ return ABIArgInfo::getDirect(/*T=*/nullptr, /*Offset=*/0, Padding);
}
// If a C++ object has either a non-trivial copy constructor or a non-trivial
@@ -295,8 +312,8 @@ ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
// Build a coercion type from the LLVM struct type.
llvm::StructType *StrTy = dyn_cast<llvm::StructType>(CGT.ConvertType(Ty));
if (!StrTy) {
- RegOffset += Size / 64;
- return ABIArgInfo::getDirect();
+ RegOffset += PaddingSlots + SizeSlots;
+ return ABIArgInfo::getDirect(/*T=*/nullptr, /*Offset=*/0, Padding);
}
CoerceBuilder CB(VMContext, getDataLayout());
@@ -306,15 +323,7 @@ ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
CB.pad(llvm::alignTo(
std::max(CB.DL.getTypeSizeInBits(StrTy).getKnownMinValue(), uint64_t(1)),
64));
- RegOffset += CB.Size / 64;
-
- // If we're dealing with overaligned structs we may need to add a padding in
- // the front, to preserve the correct register-memory mapping.
- //
- // See SCD 2.4.1, pages 3P-11 and 3P-12.
- llvm::Type *Padding =
- NeedPadding ? llvm::Type::getInt64Ty(VMContext) : nullptr;
- RegOffset += NeedPadding ? 1 : 0;
+ RegOffset += PaddingSlots + CB.Size / 64;
// Try to use the original type for coercion.
llvm::Type *CoerceTy = CB.isUsableType(StrTy) ? StrTy : CB.getType();
diff --git a/clang/test/CodeGen/Sparc/sparcv9-abi.c b/clang/test/CodeGen/Sparc/sparcv9-abi.c
index 94c91e05a9d99..7d0dbc7eba5bf 100644
--- a/clang/test/CodeGen/Sparc/sparcv9-abi.c
+++ b/clang/test/CodeGen/Sparc/sparcv9-abi.c
@@ -54,6 +54,29 @@ long double f_longdouble(long a, struct align16_longdouble b) {
return b.x;
}
+// CHECK-LABEL: define{{.*}} signext i32 @f_float_aligned(float noundef %a, i64 %0, i64 %b.coerce0, i64 %b.coerce1)
+int f_float_aligned(float a, struct align16_int b) {
+ return b.x;
+}
+
+// CHECK-LABEL: define{{.*}} signext i32 @f_float_pair_aligned(float noundef %a, float noundef %b, i64 %c.coerce0, i64 %c.coerce1)
+int f_float_pair_aligned(float a, float b, struct align16_int c) {
+ return c.x;
+}
+
+// CHECK-LABEL: define{{.*}} i64 @f_char_int128_aligned(i8 noundef signext %x, i64 %0, i128 noundef %v, i64 %q.coerce0, i64 %q.coerce1)
+struct aligned16_struct { long a, b; } __attribute__((aligned(16)));
+long f_char_int128_aligned(char x, __int128 v, struct aligned16_struct q) {
+ return q.a;
+}
+
+// FIXME: alignment on typedefs should be taken into account, but isn't.
+// CHECK-LABEL: define {{.*}} i32 @f_typedef_aligned(i32 noundef signext %x, i32 noundef signext %i)
+typedef int typedef_aligned_int __attribute__((aligned(16)));
+int f_typedef_aligned(int x, typedef_aligned_int i) {
+ return i;
+}
+
// CHECK-LABEL: define{{.*}} i64 @f_emptyvar(i32 noundef zeroext %count, ...)
long f_emptyvar(unsigned count, ...) {
long ret;
More information about the cfe-commits
mailing list