[clang] [llvm] [LLVMABI][AARCH64] Implement Pure Scalable Type handling (PR #227504)
Madhur Amilkanthwar via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 29 22:52:05 PDT 2026
================
@@ -339,6 +375,175 @@ bool AArch64TargetInfo::isIllegalVectorType(const Type *Ty) const {
return false;
}
+// Expand a memory type into a sequence with an element for each non-record,
+// non-array member of the type, with the exception of the padding types, which
+// are retained.
+void AArch64TargetInfo::flattenType(
+ const Type *Ty, SmallVectorImpl<const Type *> &Flattened) const {
+ if (ArgInfo::isPaddingForCoerceAndExpand(Ty)) {
+ Flattened.push_back(Ty);
+ return;
+ }
+
+ if (const auto *AT = dyn_cast<ArrayType>(Ty)) {
+ uint64_t NElt = AT->getNumElements();
+ if (NElt == 0)
+ return;
+
+ SmallVector<const Type *, 4> EltFlattened;
+ flattenType(AT->getElementType(), EltFlattened);
+
+ for (uint64_t I = 0; I < NElt; ++I)
+ llvm::append_range(Flattened, EltFlattened);
+ return;
+ }
+
+ if (const auto *RT = dyn_cast<RecordType>(Ty)) {
+ for (const FieldInfo &Field : RT->getFields())
+ flattenType(Field.FieldType, Flattened);
+ return;
+ }
+
+ Flattened.push_back(Ty);
+}
+
+ArgInfo AArch64TargetInfo::coerceAndExpandPureScalableAggregate(
+ const Type *Ty, bool IsNamedArg, unsigned NVec, unsigned NPred,
+ const SmallVectorImpl<const Type *> &UnpaddedCoerceToSeq, unsigned &NSRN,
+ unsigned &NPRN) const {
+ // An unnamed argument, or one that does not fit in the remaining Z or P
+ // registers, is passed indirectly and does not consume those registers.
+ if (!IsNamedArg || NSRN + NVec > 8 || NPRN + NPred > 4)
+ return getNaturalAlignIndirect(Ty, getAllocaAddrSpace(), /*ByVal=*/false);
+
+ NSRN += NVec;
+ NPRN += NPred;
+
+ // A sizeless SVE tuple is already one register per member.
+ if (Ty->isSVESizelessType())
+ return ArgInfo::getDirect();
+
+ assert(!UnpaddedCoerceToSeq.empty() && "pure scalable type has no members");
+ const Type *UnpaddedCoerceToType =
+ UnpaddedCoerceToSeq.size() == 1
+ ? UnpaddedCoerceToSeq[0]
+ : getStructOfTypes(UnpaddedCoerceToSeq, /*Packed=*/true);
+
+ SmallVector<const Type *, 8> CoerceToSeq;
+ flattenType(convertTypeForMem(Ty), CoerceToSeq);
+ return ArgInfo::getCoerceAndExpand(
+ getStructOfTypes(CoerceToSeq, /*Packed=*/false), UnpaddedCoerceToType);
+}
+
+// A Pure Scalable Type (AAPCS64) is passed in Z and P registers. On success
+// NVec and NPred are how many of each it needs, and CoerceToSeq has one
+// scalable vector per register. A sequence longer than 12 is rejected so the
+// caller treats the type as a large composite.
+bool AArch64TargetInfo::passAsPureScalableType(
+ const Type *Ty, unsigned &NVec, unsigned &NPred,
+ SmallVectorImpl<const Type *> &CoerceToSeq) const {
+ if (const auto *AT = dyn_cast<ArrayType>(Ty)) {
+ if (AT->isMatrixType())
+ return false;
+
+ uint64_t NElt = AT->getNumElements();
+ if (NElt == 0)
+ return false;
+
+ unsigned NV = 0, NP = 0;
+ SmallVector<const Type *, 4> EltCoerceToSeq;
+ if (!passAsPureScalableType(AT->getElementType(), NV, NP, EltCoerceToSeq))
+ return false;
+
+ if (CoerceToSeq.size() + NElt * EltCoerceToSeq.size() > 12)
+ return false;
+
+ for (uint64_t I = 0; I < NElt; ++I)
+ llvm::append_range(CoerceToSeq, EltCoerceToSeq);
+
+ NVec += NElt * NV;
+ NPred += NElt * NP;
+ return true;
+ }
+
+ if (const auto *RT = dyn_cast<RecordType>(Ty)) {
+ if (getRecordArgABI(RT) != RAA_Default)
+ return false;
+ // Pure scalable types are never unions and never contain unions.
+ if (RT->isUnion())
+ return false;
+
+ // Direct virtual bases are not in getBaseClasses(). A record that has
+ // one cannot be passed in registers, and getRecordArgABI rejected it
+ // above.
+ for (const FieldInfo &Base : RT->getBaseClasses()) {
+ if (Base.FieldType->isEmptyRecord())
+ continue;
+ if (!passAsPureScalableType(Base.FieldType, NVec, NPred, CoerceToSeq))
+ return false;
+ }
+ for (const FieldInfo &Field : RT->getFields()) {
+ if (Field.isEmpty())
+ continue;
----------------
madhur13490 wrote:
I think flexible array members slip through here. QualTypeMapper maps T y[] to a zero-element array (clang/lib/CodeGen/QualTypeMapper.cpp:349-350), and FieldInfo::isEmpty() treats zero-element arrays as empty (llvm/lib/ABI/Types.cpp:91). So the field is skipped and the struct is classified as a PST. Clang doesn't skip it, because an incomplete array isn't a constant array in isEmptyField, so Clang rejects the struct as a PST. struct { fixed_int32_t x; fixed_int32_t y[]; } gives Kind mismatch (expected: Direct, given: CoerceAndExpand) at 128 bits and expected: Indirect at 256 and 512. Maybe bail out early on RT->hasFlexibleArrayMember()?
https://github.com/llvm/llvm-project/pull/227504
More information about the llvm-commits
mailing list