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
