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;


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to