[clang] [llvm] [LLVMABI][AARCH64] Handle vector types (PR #225201)
Kazu Hirata via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 14:51:00 PDT 2026
================
@@ -278,6 +401,259 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnScalarsDirectWin64) {
}
}
+// Non-SVE vector types no wider than 128 bits are returned directly. Larger
+// vector types are returned indirectly.
+TEST_F(AArch64TargetInfoTest, ClassifyReturnNonSVEVectors) {
+ for (AArch64ABIKind Kind :
+ {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, AArch64ABIKind::Win64,
+ AArch64ABIKind::AAPCSSoft}) {
+ std::unique_ptr<TargetInfo> TI =
+ createAArch64TargetInfo(TB, AArch64ABIOptions(Kind));
+
+ for (const ABIType *RetTy : {V2F32, V4F32, V16I8}) {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, RetTy, {});
+ FI->getReturnInfo() = ArgInfo::getIgnore();
+ TI->computeInfo(*FI);
+ expectUncoercedDirect(FI->getReturnInfo());
+ }
+
+ for (const ABIType *RetTy : {V8F32, V17I8}) {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, RetTy, {});
+ FI->getReturnInfo() = ArgInfo::getIgnore();
+ TI->computeInfo(*FI);
+ expectAlignedIndirect(FI->getReturnInfo(), llvm::Align(16),
+ /*ByVal=*/true);
+ }
+ }
+}
+
+// Legal 64- and 128-bit non-SVE vectors are passed directly. Illegal vectors
+// are coerced, or passed indirectly if larger than 128 bits.
+TEST_F(AArch64TargetInfoTest, ClassifyArgumentNonSVEVectors) {
+ for (AArch64ABIKind Kind :
+ {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, AArch64ABIKind::Win64,
+ AArch64ABIKind::AAPCSSoft}) {
+ std::unique_ptr<TargetInfo> TI =
+ createAArch64TargetInfo(TB, AArch64ABIOptions(Kind));
+
+ for (const ABIType *ArgTy : {V2F32, V4F32, V16I8}) {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {ArgTy});
+ TI->computeInfo(*FI);
+ expectUncoercedDirect(FI->getArgInfo(0).Info);
+ }
+
+ for (const ABIType *ArgTy : {V3I8, V4I8}) {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {ArgTy});
+ TI->computeInfo(*FI);
+ expectDirectCoercedInteger(FI->getArgInfo(0).Info, 32);
+ }
+
+ {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {V2I8});
+ TI->computeInfo(*FI);
+ expectDirectCoercedInteger(FI->getArgInfo(0).Info, 32);
+ }
+
+ // A vector whose element count is not a power of 2 is coerced based on
+ // its ABI size, which rounds the payload width up to a power of 2. So
+ // 5 x i8 is coerced as 64 bits and 3 x float as 128 bits.
+ {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {V5I8});
+ TI->computeInfo(*FI);
+ expectDirectCoercedI32Vector(FI->getArgInfo(0).Info, 2);
+ }
+
+ {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {V3F32});
+ TI->computeInfo(*FI);
+ expectDirectCoercedI32Vector(FI->getArgInfo(0).Info, 4);
+ }
+
+ // A sub-byte _BitInt element counts as 8 bits towards the size of the
+ // vector, so 4 x _BitInt(2) is an illegal 32-bit vector while
+ // 8 x _BitInt(2) is a legal 64-bit one.
+ {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {V4BitInt2});
+ TI->computeInfo(*FI);
+ expectDirectCoercedInteger(FI->getArgInfo(0).Info, 32);
+ }
+
+ {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {V8BitInt2});
+ TI->computeInfo(*FI);
+ expectUncoercedDirect(FI->getArgInfo(0).Info);
+ }
+
+ {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {V1I128});
+ TI->computeInfo(*FI);
+ expectDirectCoercedI32Vector(FI->getArgInfo(0).Info, 4);
+ }
+
+ for (const ABIType *ArgTy : {V8F32, V17I8}) {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {ArgTy});
+ TI->computeInfo(*FI);
+ expectAlignedIndirect(FI->getArgInfo(0).Info, llvm::Align(16),
+ /*ByVal=*/false);
+ }
+ }
+}
+
+// Android and OHOS coerce illegal vectors of at most 16 bits to i16 rather
+// than i32.
+TEST_F(AArch64TargetInfoTest, ClassifyArgumentIllegalVectorAndroid) {
+ AArch64ABIOptions Opts(AArch64ABIKind::AAPCS);
+ Opts.IsAndroidOrOHOS = true;
+ std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Opts);
+
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {V2I8});
+ TI->computeInfo(*FI);
+ expectDirectCoercedInteger(FI->getArgInfo(0).Info, 16);
+
+ FI = FunctionInfo::create(llvm::CallingConv::C, Void, {V4I8});
+ TI->computeInfo(*FI);
+ expectDirectCoercedInteger(FI->getArgInfo(0).Info, 32);
+}
+
+// arm64_32 MachO treats vectors larger than 32 bits as legal, including
+// sizes that other AArch64 ABIs pass indirectly. Non-power-of-2 element
+// counts are still illegal.
+TEST_F(AArch64TargetInfoTest, ClassifyArgumentVectorILP32MachO) {
+ AArch64ABIOptions Opts(AArch64ABIKind::DarwinPCS);
+ Opts.IsILP32 = true;
+ Opts.IsMachO = true;
+ std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Opts);
+
+ for (const ABIType *ArgTy : {V2F32, V4F32, V16I8, V8F32, V1I128}) {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {ArgTy});
+ TI->computeInfo(*FI);
+ expectUncoercedDirect(FI->getArgInfo(0).Info);
+ }
+
+ {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {V4I8});
+ TI->computeInfo(*FI);
+ expectDirectCoercedInteger(FI->getArgInfo(0).Info, 32);
+ }
+
+ {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {V17I8});
+ TI->computeInfo(*FI);
+ expectAlignedIndirect(FI->getArgInfo(0).Info, llvm::Align(16),
+ /*ByVal=*/false);
+ }
+}
+
+// The sizeless SVE types occupy a register of their own, so they are passed
+// and returned directly, without coercion, under every ABI kind.
+TEST_F(AArch64TargetInfoTest, ClassifySizelessSVETypesDirect) {
+ const ABIType *SVETypes[] = {SVInt32, SVFloat64, SVBool, SVCount};
+
+ for (AArch64ABIKind Kind :
+ {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, AArch64ABIKind::Win64,
+ AArch64ABIKind::AAPCSSoft}) {
+ std::unique_ptr<TargetInfo> TI =
+ createAArch64TargetInfo(TB, AArch64ABIOptions(Kind));
+
+ for (const ABIType *Ty : SVETypes) {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Ty, {Ty});
+ FI->getReturnInfo() = ArgInfo::getIgnore();
+ TI->computeInfo(*FI);
+ expectUncoercedDirect(FI->getReturnInfo());
+ expectUncoercedDirect(FI->getArgInfo(0).Info);
+ }
+ }
+}
+
+// Fixed-length SVE data vectors are coerced to the scalable vector that
+// occupies the same register. The scalable element count depends only on the
+// element size, so the two vector lengths of the same element type coerce to
+// the same scalable type.
+TEST_F(AArch64TargetInfoTest, ClassifyFixedLengthSVEDataCoerced) {
+ for (AArch64ABIKind Kind :
+ {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, AArch64ABIKind::Win64,
+ AArch64ABIKind::AAPCSSoft}) {
+ std::unique_ptr<TargetInfo> TI =
+ createAArch64TargetInfo(TB, AArch64ABIOptions(Kind));
+
+ struct {
+ const ABIType *Ty;
+ const ABIType *EltTy;
+ unsigned MinElts;
+ } Cases[] = {
+ {FixedSVInt8, I8, 16}, {FixedSVInt32, I32, 4},
+ {FixedSVInt32VL512, I32, 4}, {FixedSVUint32, U32, 4},
+ {FixedSVFloat64, F64, 2},
+ };
+
+ for (const auto &Case : Cases) {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Case.Ty, {Case.Ty});
+ FI->getReturnInfo() = ArgInfo::getIgnore();
+ TI->computeInfo(*FI);
+ expectDirectCoercedSVEData(FI->getReturnInfo(), Case.EltTy, Case.MinElts);
+ expectDirectCoercedSVEData(FI->getArgInfo(0).Info, Case.EltTy,
+ Case.MinElts);
----------------
kazutakahirata wrote:
May I suggest structured bindings here?
```suggestion
for (const auto &[Ty, EltTy, MinElts] : Cases) {
std::unique_ptr<FunctionInfo> FI =
FunctionInfo::create(llvm::CallingConv::C, Ty, {Ty});
FI->getReturnInfo() = ArgInfo::getIgnore();
TI->computeInfo(*FI);
expectDirectCoercedSVEData(FI->getReturnInfo(), EltTy, MinElts);
expectDirectCoercedSVEData(FI->getArgInfo(0).Info, EltTy, MinElts);
```
https://github.com/llvm/llvm-project/pull/225201
More information about the llvm-commits
mailing list