[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