[llvm] 930a118 - [LLVMABI][AARCH64] Handle vector types (#225201)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 29 14:58:29 PDT 2026


Author: Andy Kaylor
Date: 2026-09-29T21:58:19Z
New Revision: 930a1185daae179ad2cec09080c4cfe3741bb6ae

URL: https://github.com/llvm/llvm-project/commit/930a1185daae179ad2cec09080c4cfe3741bb6ae
DIFF: https://github.com/llvm/llvm-project/commit/930a1185daae179ad2cec09080c4cfe3741bb6ae.diff

LOG: [LLVMABI][AARCH64] Handle vector types (#225201)

This adds handling for vector types in the AArch64 implementation of the
LLVM ABI library. Legal vectors are passed and returned directly.
Illegal vectors are coerced to an integer or an integer vector, or
passed indirectly if they are too large. Sizeless SVE types are passed
in a register of their own, and fixed-length SVE vectors are coerced to
a scalable vector that occupies the same register. SVE tuples still
report NYI under AAPCS.

Assisted-by: Cursor / claude-opus-5

Added: 
    clang/test/CodeGen/AArch64/abi-classify-sve-tuples.c
    clang/test/CodeGen/AArch64/abi-classify-sve-types.c

Modified: 
    clang/lib/CodeGen/CodeGenModule.cpp
    clang/lib/CodeGen/QualTypeMapper.cpp
    clang/test/CodeGen/AArch64/abi-classify-arg-types.c
    clang/test/CodeGen/AArch64/abi-classify-return-types.c
    llvm/include/llvm/ABI/TargetInfo.h
    llvm/include/llvm/ABI/Types.h
    llvm/lib/ABI/TargetInfo.cpp
    llvm/lib/ABI/Targets/AArch64.cpp
    llvm/unittests/ABI/AArch64TargetInfoTest.cpp
    llvm/unittests/ABI/IRTypeMapperTest.cpp
    llvm/unittests/ABI/TypesTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 7274a8588670f..a058d1fe7a2a3 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -433,6 +433,9 @@ CodeGenModule::getLLVMABITargetInfo(llvm::abi::TypeBuilder &TB) {
 
     Opts.IsILP32 = T.getArch() == llvm::Triple::aarch64_32;
     Opts.IsCXX = getLangOpts().CPlusPlus;
+    Opts.IsMachO = T.isOSBinFormatMachO();
+    Opts.IsAndroidOrOHOS = T.isAndroid() || T.isOHOSFamily();
+    Opts.IsWindowsArm64EC = T.isWindowsArm64EC();
     Opts.IsMicrosoftCXXABI = getTarget().getCXXABI().isMicrosoft();
 
     initializeCommonABICompatInfo(Opts.CompatInfo,

diff  --git a/clang/lib/CodeGen/QualTypeMapper.cpp b/clang/lib/CodeGen/QualTypeMapper.cpp
index 5170ffd1ab81c..84e9ac46d9043 100644
--- a/clang/lib/CodeGen/QualTypeMapper.cpp
+++ b/clang/lib/CodeGen/QualTypeMapper.cpp
@@ -279,7 +279,8 @@ QualTypeMapper::convertBuiltinType(const BuiltinType *BT) {
     return convertSVEBuiltinType(BT);
 
   case BuiltinType::SveCount:
-    return Builder.getSVECountType(getTypeAlign(QT));
+    return Builder.getScalablePredicateOrCountVectorType(
+        getTypeAlign(QT), llvm::abi::VectorKind::SVECount);
 
   // TODO: __mfp8 has no floating-point semantics of its own, so representing
   // it needs a decision about how the ABI library should model opaque

diff  --git a/clang/test/CodeGen/AArch64/abi-classify-arg-types.c b/clang/test/CodeGen/AArch64/abi-classify-arg-types.c
index 7f89b6ed6aa86..274c6010ea7e4 100644
--- a/clang/test/CodeGen/AArch64/abi-classify-arg-types.c
+++ b/clang/test/CodeGen/AArch64/abi-classify-arg-types.c
@@ -1,15 +1,17 @@
-// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,DARWIN,LONG64,NOHFAALIGN
-// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,DARWIN,LONG64,NOHFAALIGN --implicit-check-not="not yet implemented"
-// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -target-abi darwinpcs -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,DARWIN,LONG32,NOHFAALIGN
-// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -target-abi darwinpcs -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,DARWIN,LONG32,NOHFAALIGN --implicit-check-not="not yet implemented"
-// RUN: %clang_cc1 -triple aarch64-linux-gnu -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG64,AAPCS64
-// RUN: %clang_cc1 -triple aarch64-linux-gnu -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG64,AAPCS64 --implicit-check-not="not yet implemented"
-// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG64,AAPCS64
-// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG64,AAPCS64 --implicit-check-not="not yet implemented"
-// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG32,NOHFAALIGN
-// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG32,NOHFAALIGN --implicit-check-not="not yet implemented"
-// RUN: %clang_cc1 -triple arm64ec-pc-windows-msvc -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG32,NOHFAALIGN
-// RUN: %clang_cc1 -triple arm64ec-pc-windows-msvc -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG32,NOHFAALIGN --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,DARWIN,LONG64,NOHFAALIGN,NOHUGEVEC,NOANDROID
+// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,DARWIN,LONG64,NOHFAALIGN,NOHUGEVEC,NOANDROID --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -target-abi darwinpcs -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,DARWIN,LONG32,NOHFAALIGN,HUGEVEC,NOANDROID
+// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -target-abi darwinpcs -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,DARWIN,LONG32,NOHFAALIGN,HUGEVEC,NOANDROID --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG64,AAPCS64,NOHUGEVEC,NOANDROID
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG64,AAPCS64,NOHUGEVEC,NOANDROID --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG64,AAPCS64,NOHUGEVEC,NOANDROID
+// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG64,AAPCS64,NOHUGEVEC,NOANDROID --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64-linux-android -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG64,AAPCS64,NOHUGEVEC,ANDROID
+// RUN: %clang_cc1 -triple aarch64-linux-android -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG64,AAPCS64,NOHUGEVEC,ANDROID --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG32,NOHFAALIGN,NOHUGEVEC,NOANDROID
+// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG32,NOHFAALIGN,NOHUGEVEC,NOANDROID --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple arm64ec-pc-windows-msvc -fenable-matrix -fexperimental-max-bitint-width=1024 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG32,NOHFAALIGN,NOHUGEVEC,NOANDROID
+// RUN: %clang_cc1 -triple arm64ec-pc-windows-msvc -fenable-matrix -fexperimental-max-bitint-width=1024 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS,LONG32,NOHFAALIGN,NOHUGEVEC,NOANDROID --implicit-check-not="not yet implemented"
 
 // This test is verifying that the LLVM ABI library classifies argument types in
 // the same way that Clang does without the library.
@@ -258,3 +260,87 @@ typedef struct {
 } NestedZeroSize;
 void arg_nested_zerosize(NestedZeroSize z) {}
 // CHECK: define{{.*}} void @arg_nested_zerosize()
+
+// Legal 64- and 128-bit vectors are passed directly. Illegal vectors are
+// coerced to an integer or integer vector, or passed indirectly if larger
+// than 128 bits. arm64_32 MachO treats vectors larger than 32 bits as legal.
+
+typedef float v2f32 __attribute__((vector_size(8)));
+void arg_v2f32(v2f32 v) {}
+// CHECK: define{{.*}} void @arg_v2f32(<2 x float> noundef %{{.*}})
+
+typedef float v4f32 __attribute__((vector_size(16)));
+void arg_v4f32(v4f32 v) {}
+// CHECK: define{{.*}} void @arg_v4f32(<4 x float> noundef %{{.*}})
+
+typedef char v16i8 __attribute__((vector_size(16)));
+void arg_v16i8(v16i8 v) {}
+// CHECK: define{{.*}} void @arg_v16i8(<16 x i8> noundef %{{.*}})
+
+typedef char v2i8 __attribute__((vector_size(2)));
+void arg_v2i8(v2i8 v) {}
+// ANDROID: define{{.*}} void @arg_v2i8(i16 noundef %{{.*}})
+// NOANDROID: define{{.*}} void @arg_v2i8(i32{{.*}} %{{.*}})
+
+typedef char v3i8 __attribute__((vector_size(3)));
+void arg_v3i8(v3i8 v) {}
+// CHECK: define{{.*}} void @arg_v3i8(i32{{.*}} %{{.*}})
+
+typedef char v4i8 __attribute__((vector_size(4)));
+void arg_v4i8(v4i8 v) {}
+// CHECK: define{{.*}} void @arg_v4i8(i32{{.*}} %{{.*}})
+
+typedef unsigned __int128 v1i128 __attribute__((vector_size(16)));
+void arg_v1i128(v1i128 v) {}
+// NOHUGEVEC: define{{.*}} void @arg_v1i128(<4 x i32> noundef %{{.*}})
+// HUGEVEC: define{{.*}} void @arg_v1i128(<1 x i128> noundef %{{.*}})
+
+typedef float v8f32 __attribute__((vector_size(32)));
+void arg_v8f32(v8f32 v) {}
+// NOHUGEVEC: define{{.*}} void @arg_v8f32(ptr nofreeobj noundef align 16 dead_on_return dereferenceable(32) %{{.*}})
+// HUGEVEC: define{{.*}} void @arg_v8f32(<8 x float> noundef %{{.*}})
+
+typedef char v17i8 __attribute__((vector_size(17)));
+void arg_v17i8(v17i8 v) {}
+// CHECK: define{{.*}} void @arg_v17i8(ptr nofreeobj noundef align 16 dead_on_return dereferenceable(32) %{{.*}})
+
+// A vector whose element count is not a power of 2 is illegal, and it is
+// coerced based on its ABI size, which is the payload width rounded up to a
+// power of 2. So a 3 x float has 96 bits of payload but is coerced as if it
+// were 128 bits wide.
+
+typedef float v3f32 __attribute__((vector_size(12)));
+void arg_v3f32(v3f32 v) {}
+// CHECK: define{{.*}} void @arg_v3f32(<4 x i32> %{{.*}})
+
+typedef short v3i16 __attribute__((vector_size(6)));
+void arg_v3i16(v3i16 v) {}
+// CHECK: define{{.*}} void @arg_v3i16(<2 x i32> %{{.*}})
+
+typedef char v5i8 __attribute__((vector_size(5)));
+void arg_v5i8(v5i8 v) {}
+// CHECK: define{{.*}} void @arg_v5i8(<2 x i32> %{{.*}})
+
+typedef char v9i8 __attribute__((vector_size(9)));
+void arg_v9i8(v9i8 v) {}
+// CHECK: define{{.*}} void @arg_v9i8(<4 x i32> %{{.*}})
+
+// A _BitInt occupies a whole number of bytes, so a sub-byte element counts as
+// 8 bits towards the size of the vector. That makes 8 x _BitInt(2) a legal
+// 64-bit vector rather than an illegal 16-bit one.
+
+typedef _BitInt(2) b2v4 __attribute__((ext_vector_type(4)));
+void arg_b2v4(b2v4 v) {}
+// CHECK: define{{.*}} void @arg_b2v4(i32 %{{.*}})
+
+typedef _BitInt(2) b2v8 __attribute__((ext_vector_type(8)));
+void arg_b2v8(b2v8 v) {}
+// CHECK: define{{.*}} void @arg_b2v8(<8 x i2> noundef %{{.*}})
+
+typedef _BitInt(4) b4v16 __attribute__((ext_vector_type(16)));
+void arg_b4v16(b4v16 v) {}
+// CHECK: define{{.*}} void @arg_b4v16(<16 x i4> noundef %{{.*}})
+
+typedef _BitInt(32) b32v2 __attribute__((ext_vector_type(2)));
+void arg_b32v2(b32v2 v) {}
+// CHECK: define{{.*}} void @arg_b32v2(<2 x i32> noundef %{{.*}})

diff  --git a/clang/test/CodeGen/AArch64/abi-classify-return-types.c b/clang/test/CodeGen/AArch64/abi-classify-return-types.c
index d86c21395a839..bc00727eff97d 100644
--- a/clang/test/CodeGen/AArch64/abi-classify-return-types.c
+++ b/clang/test/CodeGen/AArch64/abi-classify-return-types.c
@@ -200,3 +200,23 @@ NestedZeroSize ret_nested_zerosize(void) {
   return z;
 }
 // CHECK: define{{.*}} void @ret_nested_zerosize()
+
+typedef float v2f32 __attribute__((vector_size(8)));
+v2f32 ret_v2f32(void) { return (v2f32){1.0f, 2.0f}; }
+// CHECK: define{{.*}} <2 x float> @ret_v2f32
+
+typedef float v4f32 __attribute__((vector_size(16)));
+v4f32 ret_v4f32(void) { return (v4f32){1.0f, 2.0f, 3.0f, 4.0f}; }
+// CHECK: define{{.*}} <4 x float> @ret_v4f32
+
+typedef char v16i8 __attribute__((vector_size(16)));
+v16i8 ret_v16i8(void) { return (v16i8){0}; }
+// CHECK: define{{.*}} <16 x i8> @ret_v16i8
+
+typedef float v8f32 __attribute__((vector_size(32)));
+v8f32 ret_v8f32(void) { return (v8f32){0}; }
+// CHECK: define{{.*}} void @ret_v8f32(ptr dead_on_unwind noalias writable sret(<8 x float>) align 16 %{{.*}})
+
+typedef char v17i8 __attribute__((vector_size(17)));
+v17i8 ret_v17i8(void) { return (v17i8){0}; }
+// CHECK: define{{.*}} void @ret_v17i8(ptr dead_on_unwind noalias writable sret(<17 x i8>) align 16 %{{.*}})

diff  --git a/clang/test/CodeGen/AArch64/abi-classify-sve-tuples.c b/clang/test/CodeGen/AArch64/abi-classify-sve-tuples.c
new file mode 100644
index 0000000000000..612df5ebfaa5c
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/abi-classify-sve-tuples.c
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -target-feature +sve -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -target-feature +sve -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -target-feature +sve -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -target-feature +sve -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -target-feature -fp-armv8 -target-abi aapcs-soft -target-feature +sve -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -target-feature -fp-armv8 -target-abi aapcs-soft -target-feature +sve -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+
+// This test is verifying that the LLVM ABI library classifies AArch64 SVE
+// tuples in the same way that Clang does without the library.
+
+// DarwinPCS, Win64, and the soft-float ABI pass an SVE tuple directly.
+// The tuple is expanded to one scalable vector argument per member, and a
+// returned tuple is a struct of those vectors.
+
+void arg_svint32x2(__clang_svint32x2_t v) {}
+// CHECK: define{{.*}} void @arg_svint32x2(<vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}})
+
+void arg_svint32x3(__clang_svint32x3_t v) {}
+// CHECK: define{{.*}} void @arg_svint32x3(<vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}})
+
+void arg_svint32x4(__clang_svint32x4_t v) {}
+// CHECK: define{{.*}} void @arg_svint32x4(<vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}})
+
+void arg_svboolx2(__clang_svboolx2_t p) {}
+// CHECK: define{{.*}} void @arg_svboolx2(<vscale x 16 x i1> %{{.*}}, <vscale x 16 x i1> %{{.*}})
+
+void arg_svboolx4(__clang_svboolx4_t p) {}
+// CHECK: define{{.*}} void @arg_svboolx4(<vscale x 16 x i1> %{{.*}}, <vscale x 16 x i1> %{{.*}}, <vscale x 16 x i1> %{{.*}}, <vscale x 16 x i1> %{{.*}})
+
+__clang_svint32x2_t ret_svint32x2(__clang_svint32x2_t v) { return v; }
+// CHECK: define{{.*}} { <vscale x 4 x i32>, <vscale x 4 x i32> } @ret_svint32x2(<vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}})
+
+__clang_svint32x3_t ret_svint32x3(__clang_svint32x3_t v) { return v; }
+// CHECK: define{{.*}} { <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32> } @ret_svint32x3(<vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}})
+
+__clang_svint32x4_t ret_svint32x4(__clang_svint32x4_t v) { return v; }
+// CHECK: define{{.*}} { <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32> } @ret_svint32x4(<vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}}, <vscale x 4 x i32> %{{.*}})
+
+__clang_svboolx2_t ret_svboolx2(__clang_svboolx2_t p) { return p; }
+// CHECK: define{{.*}} { <vscale x 16 x i1>, <vscale x 16 x i1> } @ret_svboolx2(<vscale x 16 x i1> %{{.*}}, <vscale x 16 x i1> %{{.*}})
+
+__clang_svboolx4_t ret_svboolx4(__clang_svboolx4_t p) { return p; }
+// CHECK: define{{.*}} { <vscale x 16 x i1>, <vscale x 16 x i1>, <vscale x 16 x i1>, <vscale x 16 x i1> } @ret_svboolx4(<vscale x 16 x i1> %{{.*}}, <vscale x 16 x i1> %{{.*}}, <vscale x 16 x i1> %{{.*}}, <vscale x 16 x i1> %{{.*}})

diff  --git a/clang/test/CodeGen/AArch64/abi-classify-sve-types.c b/clang/test/CodeGen/AArch64/abi-classify-sve-types.c
new file mode 100644
index 0000000000000..304937e345365
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/abi-classify-sve-types.c
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -target-feature +sve -mvscale-min=2 -mvscale-max=2 -DBITS=256 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -target-feature +sve -mvscale-min=2 -mvscale-max=2 -DBITS=256 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -target-feature +sve -mvscale-min=4 -mvscale-max=4 -DBITS=512 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -target-feature +sve -mvscale-min=4 -mvscale-max=4 -DBITS=512 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -target-feature +sve -mvscale-min=2 -mvscale-max=2 -DBITS=256 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -target-feature +sve -mvscale-min=2 -mvscale-max=2 -DBITS=256 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -target-feature +sve -mvscale-min=2 -mvscale-max=2 -DBITS=256 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -target-feature +sve -mvscale-min=2 -mvscale-max=2 -DBITS=256 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -target-feature +sve -mvscale-min=2 -mvscale-max=2 -DBITS=256 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -target-feature +sve -mvscale-min=2 -mvscale-max=2 -DBITS=256 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+
+// This test is verifying that the LLVM ABI library classifies the AArch64 SVE
+// types in the same way that Clang does without the library.
+
+// The test runs at two vector lengths. The classification of a fixed-length
+// type does not depend on the vector length, so both use the same check lines.
+
+typedef __SVInt8_t fixed_int8_t __attribute__((arm_sve_vector_bits(BITS)));
+typedef __SVInt32_t fixed_int32_t __attribute__((arm_sve_vector_bits(BITS)));
+typedef __SVUint32_t fixed_uint32_t __attribute__((arm_sve_vector_bits(BITS)));
+typedef __SVFloat64_t fixed_float64_t __attribute__((arm_sve_vector_bits(BITS)));
+typedef __SVBool_t fixed_bool_t __attribute__((arm_sve_vector_bits(BITS)));
+
+// The sizeless types are passed and returned in their own registers without
+// coercion.
+
+void arg_svint8(__SVInt8_t v) {}
+// CHECK: define{{.*}} void @arg_svint8(<vscale x 16 x i8> %{{.*}})
+
+void arg_svint32(__SVInt32_t v) {}
+// CHECK: define{{.*}} void @arg_svint32(<vscale x 4 x i32> %{{.*}})
+
+void arg_svfloat64(__SVFloat64_t v) {}
+// CHECK: define{{.*}} void @arg_svfloat64(<vscale x 2 x double> %{{.*}})
+
+void arg_svbool(__SVBool_t p) {}
+// CHECK: define{{.*}} void @arg_svbool(<vscale x 16 x i1> %{{.*}})
+
+void arg_svcount(__SVCount_t c) {}
+// CHECK: define{{.*}} void @arg_svcount(target("aarch64.svcount") %{{.*}})
+
+__SVInt32_t ret_svint32(__SVInt32_t v) { return v; }
+// CHECK: define{{.*}} <vscale x 4 x i32> @ret_svint32(<vscale x 4 x i32> %{{.*}})
+
+__SVBool_t ret_svbool(__SVBool_t p) { return p; }
+// CHECK: define{{.*}} <vscale x 16 x i1> @ret_svbool(<vscale x 16 x i1> %{{.*}})
+
+__SVCount_t ret_svcount(__SVCount_t c) { return c; }
+// CHECK: define{{.*}} target("aarch64.svcount") @ret_svcount(target("aarch64.svcount") %{{.*}})
+
+// The fixed-length types are coerced to the sizeless type that occupies the
+// same register, so the element count of the coerced type depends only on the
+// element size, not on the vector length the type was declared with.
+
+void arg_fixed_int8(fixed_int8_t v) {}
+// CHECK: define{{.*}} void @arg_fixed_int8(<vscale x 16 x i8> noundef %{{.*}})
+
+void arg_fixed_int32(fixed_int32_t v) {}
+// CHECK: define{{.*}} void @arg_fixed_int32(<vscale x 4 x i32> noundef %{{.*}})
+
+void arg_fixed_uint32(fixed_uint32_t v) {}
+// CHECK: define{{.*}} void @arg_fixed_uint32(<vscale x 4 x i32> noundef %{{.*}})
+
+void arg_fixed_float64(fixed_float64_t v) {}
+// CHECK: define{{.*}} void @arg_fixed_float64(<vscale x 2 x double> noundef %{{.*}})
+
+void arg_fixed_bool(fixed_bool_t p) {}
+// CHECK: define{{.*}} void @arg_fixed_bool(<vscale x 16 x i1> noundef %{{.*}})
+
+fixed_int32_t ret_fixed_int32(fixed_int32_t v) { return v; }
+// CHECK: define{{.*}} <vscale x 4 x i32> @ret_fixed_int32(<vscale x 4 x i32> noundef %{{.*}})
+
+fixed_bool_t ret_fixed_bool(fixed_bool_t p) { return p; }
+// CHECK: define{{.*}} <vscale x 16 x i1> @ret_fixed_bool(<vscale x 16 x i1> noundef %{{.*}})
+
+// Sizeless and fixed-length types can be mixed in one signature.
+
+void arg_mixed(__SVInt32_t a, __SVBool_t p, fixed_int32_t b) {}
+// CHECK: define{{.*}} void @arg_mixed(<vscale x 4 x i32> %{{.*}}, <vscale x 16 x i1> %{{.*}}, <vscale x 4 x i32> noundef %{{.*}})

diff  --git a/llvm/include/llvm/ABI/TargetInfo.h b/llvm/include/llvm/ABI/TargetInfo.h
index 32d89abff742b..b8da6d6afcc07 100644
--- a/llvm/include/llvm/ABI/TargetInfo.h
+++ b/llvm/include/llvm/ABI/TargetInfo.h
@@ -171,6 +171,9 @@ struct AArch64ABIOptions {
   AArch64ABIKind Kind = AArch64ABIKind::AAPCS;
   bool IsILP32 = false;
   bool IsCXX = false;
+  bool IsMachO = false;
+  bool IsAndroidOrOHOS = false;
+  bool IsWindowsArm64EC = false;
   bool IsMicrosoftCXXABI = false;
   ABICompatInfo CompatInfo;
 

diff  --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h
index 8d94131cf5c29..b7083d181eae8 100644
--- a/llvm/include/llvm/ABI/Types.h
+++ b/llvm/include/llvm/ABI/Types.h
@@ -17,8 +17,10 @@
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/bit.h"
 #include "llvm/Support/Alignment.h"
 #include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/TypeSize.h"
 
@@ -302,10 +304,39 @@ class VectorType : public Type {
   bool isScalable() const { return NumElements.isScalable(); }
   bool isFixedLength() const { return !NumElements.isScalable(); }
 
+  /// Returns the size of this vector as Clang's ASTContext reports it: zero
+  /// for a scalable vector, and otherwise at least one byte and rounded up to
+  /// a power of two. For example, a 3 x float vector has 96 bits of payload
+  /// but an ABI size of 128 bits. getSizeInBits() returns the payload width,
+  /// so classification rules that compare against a Clang type size must use
+  /// this instead.
+  uint64_t getABISizeInBits() const {
+    if (isScalable())
+      return 0;
+
+    // A _BitInt occupies a whole number of bytes, so a sub-byte element is
+    // padded out to 8 bits. Clang only permits power-of-2 _BitInt vector
+    // elements, and a wider one always fills its storage exactly, so this is
+    // the only padding that can occur. A one-bit element is a bool rather
+    // than a _BitInt, and those really are packed one to a bit.
+    uint64_t EltWidth = ElementType->getSizeInBits().getFixedValue();
+    if (const auto *IT = dyn_cast<IntegerType>(ElementType))
+      if (IT->isBitInt() && EltWidth < 8)
+        EltWidth = 8;
+
+    uint64_t Width = EltWidth * NumElements.getKnownMinValue();
+    return bit_ceil(Width < 8 ? uint64_t(8) : Width);
+  }
+
   bool isSVEData() const { return VecKind == VectorKind::SVEData; }
   bool isSVEPredicate() const { return VecKind == VectorKind::SVEPredicate; }
   bool isSVECount() const { return VecKind == VectorKind::SVECount; }
 
+  bool isFixedLengthSVEData() const { return isFixedLength() && isSVEData(); }
+  bool isFixedLengthSVEPredicate() const {
+    return isFixedLength() && isSVEPredicate();
+  }
+
   /// Returns true for any of the AArch64 SVE flavors.
   bool isSVEType() const { return VecKind != VectorKind::Generic; }
 
@@ -495,14 +526,17 @@ class TypeBuilder {
     return new (Allocator.Allocate<TupleType>()) TupleType(Vec, NumVectors);
   }
 
-  /// Creates the AArch64 __SVCount_t type. The type is opaque, so it is
-  /// modeled with the shape of svbool_t: a scalable vector of 16 one-bit
-  /// elements.
-  const VectorType *getSVECountType(Align ABIAlign) {
+  /// Creates a scalable predicate or count vector.
+  /// Note: The AArch64 __SVCount_t type is opaque, so it is modeled with the
+  /// shape of svbool_t: a scalable vector of 16 one-bit elements.
+  const VectorType *getScalablePredicateOrCountVectorType(Align ABIAlign,
+                                                          VectorKind Kind) {
+    assert((Kind == VectorKind::SVEPredicate || Kind == VectorKind::SVECount) &&
+           "expected predicate or count vector kind");
     const Type *PredicateBit =
         getIntegerType(1, Align(1), /*Signed=*/false, /*IsBitInt=*/false);
     return getVectorType(PredicateBit, ElementCount::getScalable(16), ABIAlign,
-                         VectorKind::SVECount);
+                         Kind);
   }
 
   const RecordType *getRecordType(ArrayRef<FieldInfo> Fields, TypeSize Size,

diff  --git a/llvm/lib/ABI/TargetInfo.cpp b/llvm/lib/ABI/TargetInfo.cpp
index 1e03d98880734..71cd3c742bc61 100644
--- a/llvm/lib/ABI/TargetInfo.cpp
+++ b/llvm/lib/ABI/TargetInfo.cpp
@@ -20,7 +20,8 @@ bool TargetInfo::isAggregateTypeForABI(const Type *Ty) const {
     return isAggregateTypeForABI(AT->getValueType());
 
   // Check for fundamental scalar types.
-  if (Ty->isInteger() || Ty->isFloat() || Ty->isPointer() || Ty->isVector())
+  if (Ty->isInteger() || Ty->isFloat() || Ty->isPointer() || Ty->isVector() ||
+      Ty->isTuple())
     return false;
 
   // A matrix type is modeled as an array but lowers to a single flattened

diff  --git a/llvm/lib/ABI/Targets/AArch64.cpp b/llvm/lib/ABI/Targets/AArch64.cpp
index e529540c8ae11..9573f31e1a405 100644
--- a/llvm/lib/ABI/Targets/AArch64.cpp
+++ b/llvm/lib/ABI/Targets/AArch64.cpp
@@ -12,6 +12,7 @@
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MathExtras.h"
+#include "llvm/Support/TypeSize.h"
 #include "llvm/Support/WithColor.h"
 #include <algorithm>
 #include <cstdint>
@@ -54,6 +55,15 @@ class AArch64TargetInfo : public TargetInfo {
 
   bool isDarwinPCS() const { return Opts.Kind == AArch64ABIKind::DarwinPCS; }
   bool isSoftFloat() const { return Opts.Kind == AArch64ABIKind::AAPCSSoft; }
+
+  const VectorType *
+  convertFixedToScalableVectorType(const VectorType *VT) const;
+
+  ArgInfo coerceIllegalVector(const VectorType *VT, unsigned &NSRN,
+                              unsigned &NPRN) const;
+
+  bool isIllegalVectorType(const Type *Ty) const;
+
   bool passAsAggregateType(const Type *Ty) const;
 
   bool isHomogeneousAggregateBaseType(const Type *Ty) const override;
@@ -79,9 +89,15 @@ ArgInfo AArch64TargetInfo::classifyReturnType(const Type *RetTy,
   if (RetTy->isVoid())
     return ArgInfo::getIgnore();
 
-  if (RetTy->isVector()) {
-    reportNYI("Vector return type handling");
-    return ArgInfo::getIgnore();
+  if (const auto *VT = dyn_cast<VectorType>(RetTy)) {
+    if (VT->isFixedLengthSVEData() || VT->isFixedLengthSVEPredicate()) {
+      unsigned NSRN = 0, NPRN = 0;
+      return coerceIllegalVector(VT, NSRN, NPRN);
+    }
+
+    // Large vector types should be returned via memory.
+    if (VT->getABISizeInBits() > 128)
+      return getNaturalAlignIndirect(RetTy, getAllocaAddrSpace());
   }
 
   if (!passAsAggregateType(RetTy)) {
@@ -119,11 +135,17 @@ ArgInfo AArch64TargetInfo::classifyArgumentType(
     unsigned CallingConvention, unsigned &NSRN, unsigned &NPRN) const {
   Ty = useFirstFieldIfTransparentUnion(Ty);
 
-  if (Ty->isVector()) {
-    reportNYI("Vector argument type handling");
+  // Arm64EC variadic functions classify their arguments with the x86-64
+  // rules rather than the AArch64 ones.
+  if (IsVariadicFn && Opts.IsWindowsArm64EC) {
+    reportNYI("Arm64EC variadic argument handling");
     return ArgInfo::getIgnore();
   }
 
+  // Handle illegal vector types here.
+  if (isIllegalVectorType(Ty))
+    return coerceIllegalVector(cast<VectorType>(Ty), NSRN, NPRN);
+
   if (!passAsAggregateType(Ty)) {
     if (const auto *IntTy = dyn_cast<IntegerType>(Ty)) {
       if (IntTy->isBitInt())
@@ -135,10 +157,23 @@ ArgInfo AArch64TargetInfo::classifyArgumentType(
         return ArgInfo::getExtend(IntTy);
     }
 
-    // TODO: Legal vector types will update NSRN or NPRN.
-
-    if (Ty->isFloat())
+    // Predicates and svcount_t are passed in a predicate register. Legal
+    // vectors, SVE data vectors, and floating-point types are passed in a
+    // SIMD and floating-point register. A tuple occupies one register of the
+    // appropriate kind per vector it contains.
+    if (const auto *VT = dyn_cast<VectorType>(Ty)) {
+      if (VT->isSVEPredicate() || VT->isSVECount())
+        NPRN = std::min(NPRN + 1, 4u);
+      else
+        NSRN = std::min(NSRN + 1, 8u);
+    } else if (const auto *TT = dyn_cast<TupleType>(Ty)) {
+      if (TT->getVectorType()->isSVEPredicate())
+        NPRN = std::min(NPRN + TT->getNumVectors(), 4u);
+      else
+        NSRN = std::min(NSRN + TT->getNumVectors(), 8u);
+    } else if (Ty->isFloat()) {
       NSRN = std::min(NSRN + 1, 8u);
+    }
 
     // Everything not handled above is returned directly.
     return ArgInfo::getDirect();
@@ -197,10 +232,113 @@ ArgInfo AArch64TargetInfo::classifyArgumentType(
 }
 
 bool AArch64TargetInfo::passAsAggregateType(const Type *Ty) const {
-  // TODO: Handle SVE types. For now, they don't get through the type mapper.
+  if (Opts.Kind == AArch64ABIKind::AAPCS && Ty->isSVESizelessType()) {
+    // svcount_t and the single-vector types occupy a register of their own,
+    // so only the data and predicate tuples are passed as aggregates.
+    const auto *TupleTy = dyn_cast<TupleType>(Ty);
+    assert((!TupleTy || TupleTy->getNumVectors() > 1) &&
+           "unexpected single vector tuple");
+    return TupleTy && !TupleTy->getVectorType()->isSVECount();
+  }
   return isAggregateTypeForABI(Ty);
 }
 
+/// Returns the scalable vector type that \p VT, a fixed-length SVE vector,
+/// is passed as. A scalable SVE vector holds 128 bits per granule, so the
+/// scalable element count is 128 divided by the element size, regardless of
+/// how many elements the fixed-length type has.
+const VectorType *AArch64TargetInfo::convertFixedToScalableVectorType(
+    const VectorType *VT) const {
+  // TODO: Verify that this correctly handles MFloat8 when we decide on a
+  // mapping for that type.
+
+  if (VT->isFixedLengthSVEPredicate())
+    return TB.getScalablePredicateOrCountVectorType(Align(2),
+                                                    VectorKind::SVEPredicate);
+
+  assert(VT->isFixedLengthSVEData() && "expected a fixed-length SVE vector!");
+
+  const Type *EltTy = VT->getElementType();
+  uint64_t EltBits = EltTy->getSizeInBits().getFixedValue();
+  assert(EltBits >= 8 && EltBits <= 64 && isPowerOf2_64(EltBits) &&
+         "unexpected element type for SVE data vector!");
+
+  return TB.getVectorType(EltTy, ElementCount::getScalable(128 / EltBits),
+                          llvm::Align(16), VectorKind::SVEData);
+}
+
+ArgInfo AArch64TargetInfo::coerceIllegalVector(const VectorType *VT,
+                                               unsigned &NSRN,
+                                               unsigned &NPRN) const {
+  if (VT->isFixedLengthSVEPredicate()) {
+    // Fixed-length predicates are described with 8-bit elements, but they are
+    // passed in a predicate register as a scalable vector of 16 one-bit
+    // elements.
+    assert(isa<IntegerType>(VT->getElementType()) &&
+           VT->getElementType()->getSizeInBits().getFixedValue() == 8 &&
+           "unexpected element type for SVE predicate!");
+    NPRN = std::min(NPRN + 1, 4u);
+    return ArgInfo::getDirect(TB.getScalablePredicateOrCountVectorType(
+        Align(2), VectorKind::SVEPredicate));
+  }
+
+  if (VT->isFixedLengthSVEData()) {
+    NSRN = std::min(NSRN + 1, 8u);
+    return ArgInfo::getDirect(convertFixedToScalableVectorType(VT));
+  }
+
+  uint64_t Size = VT->getABISizeInBits();
+  // Android promotes <2 x i8> to i16, not i32
+  if (Opts.IsAndroidOrOHOS && (Size <= 16)) {
+    auto *ResType = TB.getIntegerType(16, llvm::Align(2), /*Signed=*/false);
+    return ArgInfo::getDirect(ResType);
+  }
+  const Type *I32 = TB.getIntegerType(32, llvm::Align(4), /*Signed=*/false);
+  if (Size <= 32)
+    return ArgInfo::getDirect(I32);
+  if (Size == 64) {
+    NSRN = std::min(NSRN + 1, 8u);
+    return ArgInfo::getDirect(
+        TB.getVectorType(I32, ElementCount::getFixed(2), llvm::Align(8)));
+  }
+  if (Size == 128) {
+    NSRN = std::min(NSRN + 1, 8u);
+    return ArgInfo::getDirect(
+        TB.getVectorType(I32, ElementCount::getFixed(4), llvm::Align(16)));
+  }
+
+  return getNaturalAlignIndirect(VT, getAllocaAddrSpace(), /*ByVal=*/false);
+}
+
+bool AArch64TargetInfo::isIllegalVectorType(const Type *Ty) const {
+  if (const auto *VT = dyn_cast<VectorType>(Ty)) {
+    // Check whether VT is a fixed-length SVE vector. These types are
+    // represented as scalable vectors in function args/return and must be
+    // coerced from fixed vectors.
+    if (VT->isFixedLengthSVEData() || VT->isFixedLengthSVEPredicate())
+      return true;
+
+    // Scalable SVE types are legal.
+    if (VT->isScalable())
+      return false;
+
+    // Check whether VT is legal.
+    unsigned NumElements = VT->getNumElements().getFixedValue();
+    uint64_t Size = VT->getABISizeInBits();
+    // NumElements should be power of 2.
+    if (!isPowerOf2_32(NumElements))
+      return true;
+
+    // arm64_32 has to be compatible with the ARM logic here, which allows huge
+    // vectors for some reason.
+    if (Opts.IsILP32 && Opts.IsMachO)
+      return Size <= 32;
+
+    return Size != 64 && (Size != 128 || NumElements == 1);
+  }
+  return false;
+}
+
 bool AArch64TargetInfo::isHomogeneousAggregateBaseType(const Type *Ty) const {
   // Soft-float ABI: no types are homogeneous aggregates.
   if (isSoftFloat())
@@ -215,11 +353,7 @@ bool AArch64TargetInfo::isHomogeneousAggregateBaseType(const Type *Ty) const {
     if (VT->isScalable() || VT->isSVEData() || VT->isSVEPredicate())
       return false;
 
-    // Clang's getTypeSize for non-power-of-2 vectors rounds the width up to
-    // the next power-of-2 alignment (e.g. 3 x float is 96 bits of payload but
-    // 128 bits of ABI size), so those vectors are short-vector HVA bases.
-    uint64_t VecSize =
-        bit_ceil(std::max<uint64_t>(8, VT->getSizeInBits().getFixedValue()));
+    uint64_t VecSize = VT->getABISizeInBits();
     if (VecSize == 64 || VecSize == 128)
       return true;
   }

diff  --git a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
index 3612865151d18..bbb9a52eb4828 100644
--- a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
+++ b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
@@ -49,20 +49,48 @@ class AArch64TargetInfoTest : public ::testing::Test {
   const ABIType *U32;
   const ABIType *I64;
   const ABIType *U64;
+  const ABIType *I128;
   const ABIType *F16;
   const ABIType *F32;
   const ABIType *F64;
   const ABIType *Ptr;
   const ABIType *Void;
   const ABIType *Matrix;
+  const ABIType *V2F32;
+  const ABIType *V4F32;
+  const ABIType *V8F32;
+  const ABIType *V16I8;
+  const ABIType *V17I8;
+  const ABIType *V2I8;
+  const ABIType *V3I8;
+  const ABIType *V4I8;
+  const ABIType *V3F32;
+  const ABIType *V5I8;
+  const ABIType *V1I128;
+  const llvm::abi::VectorType *SVInt32;
+  const llvm::abi::VectorType *SVBool;
+  const llvm::abi::VectorType *SVCount;
+  const llvm::abi::VectorType *SVFloat64;
+  const llvm::abi::VectorType *FixedSVInt8;
+  const llvm::abi::VectorType *FixedSVInt32;
+  const llvm::abi::VectorType *FixedSVInt32VL512;
+  const llvm::abi::VectorType *FixedSVUint32;
+  const llvm::abi::VectorType *FixedSVFloat64;
+  const llvm::abi::VectorType *FixedSVBool;
+  const ABIType *SVInt32x2;
+  const ABIType *SVInt32x3;
+  const ABIType *SVInt32x4;
+  const ABIType *SVBoolx2;
+  const ABIType *SVBoolx4;
   const ABIType *BitInt7;
   const ABIType *UBitInt7;
   const ABIType *BitInt65;
   const ABIType *BitInt128;
   const ABIType *BitInt129;
+  const ABIType *BitInt2;
+  const ABIType *V4BitInt2;
+  const ABIType *V8BitInt2;
   const ABIType *ComplexFloat;
-  const ABIType *V2F32;
-  const ABIType *V4F32;
 
   AArch64TargetInfoTest()
       : TB(Alloc), Bool(TB.getIntegerType(1, llvm::Align(1), /*Signed=*/false)),
@@ -74,12 +102,69 @@ class AArch64TargetInfoTest : public ::testing::Test {
         U32(TB.getIntegerType(32, llvm::Align(4), /*Signed=*/false)),
         I64(TB.getIntegerType(64, llvm::Align(8), /*Signed=*/true)),
         U64(TB.getIntegerType(64, llvm::Align(8), /*Signed=*/false)),
+        I128(TB.getIntegerType(128, llvm::Align(16), /*Signed=*/false)),
         F16(TB.getFloatType(llvm::APFloat::IEEEhalf(), llvm::Align(2))),
         F32(TB.getFloatType(llvm::APFloat::IEEEsingle(), llvm::Align(4))),
         F64(TB.getFloatType(llvm::APFloat::IEEEdouble(), llvm::Align(8))),
         Ptr(TB.getPointerType(64, llvm::Align(8))), Void(TB.getVoidType()),
         Matrix(TB.getArrayType(F32, /*NumElements=*/4, /*SizeInBits=*/128,
                                /*IsMatrixType=*/true)),
+        V2F32(TB.getVectorType(F32, llvm::ElementCount::getFixed(2),
+                               llvm::Align(8))),
+        V4F32(TB.getVectorType(F32, llvm::ElementCount::getFixed(4),
+                               llvm::Align(16))),
+        V8F32(TB.getVectorType(F32, llvm::ElementCount::getFixed(8),
+                               llvm::Align(16))),
+        V16I8(TB.getVectorType(I8, llvm::ElementCount::getFixed(16),
+                               llvm::Align(16))),
+        V17I8(TB.getVectorType(I8, llvm::ElementCount::getFixed(17),
+                               llvm::Align(16))),
+        V2I8(TB.getVectorType(I8, llvm::ElementCount::getFixed(2),
+                              llvm::Align(2))),
+        V3I8(TB.getVectorType(I8, llvm::ElementCount::getFixed(3),
+                              llvm::Align(4))),
+        V4I8(TB.getVectorType(I8, llvm::ElementCount::getFixed(4),
+                              llvm::Align(4))),
+        V3F32(TB.getVectorType(F32, llvm::ElementCount::getFixed(3),
+                               llvm::Align(16))),
+        V5I8(TB.getVectorType(I8, llvm::ElementCount::getFixed(5),
+                              llvm::Align(8))),
+        V1I128(TB.getVectorType(I128, llvm::ElementCount::getFixed(1),
+                                llvm::Align(16))),
+        SVInt32(TB.getVectorType(I32, llvm::ElementCount::getScalable(4),
+                                 llvm::Align(16),
+                                 llvm::abi::VectorKind::SVEData)),
+        SVBool(TB.getVectorType(Bool, llvm::ElementCount::getScalable(16),
+                                llvm::Align(2),
+                                llvm::abi::VectorKind::SVEPredicate)),
+        SVCount(TB.getScalablePredicateOrCountVectorType(
+            llvm::Align(2), llvm::abi::VectorKind::SVECount)),
+        SVFloat64(TB.getVectorType(F64, llvm::ElementCount::getScalable(2),
+                                   llvm::Align(16),
+                                   llvm::abi::VectorKind::SVEData)),
+        FixedSVInt8(TB.getVectorType(I8, llvm::ElementCount::getFixed(32),
+                                     llvm::Align(16),
+                                     llvm::abi::VectorKind::SVEData)),
+        FixedSVInt32(TB.getVectorType(I32, llvm::ElementCount::getFixed(8),
+                                      llvm::Align(16),
+                                      llvm::abi::VectorKind::SVEData)),
+        FixedSVInt32VL512(
+            TB.getVectorType(I32, llvm::ElementCount::getFixed(16),
+                             llvm::Align(16), llvm::abi::VectorKind::SVEData)),
+        FixedSVUint32(TB.getVectorType(U32, llvm::ElementCount::getFixed(8),
+                                       llvm::Align(16),
+                                       llvm::abi::VectorKind::SVEData)),
+        FixedSVFloat64(TB.getVectorType(F64, llvm::ElementCount::getFixed(4),
+                                        llvm::Align(16),
+                                        llvm::abi::VectorKind::SVEData)),
+        FixedSVBool(TB.getVectorType(U8, llvm::ElementCount::getFixed(32),
+                                     llvm::Align(2),
+                                     llvm::abi::VectorKind::SVEPredicate)),
+        SVInt32x2(TB.getTupleType(SVInt32, /*NumVectors=*/2)),
+        SVInt32x3(TB.getTupleType(SVInt32, /*NumVectors=*/3)),
+        SVInt32x4(TB.getTupleType(SVInt32, /*NumVectors=*/4)),
+        SVBoolx2(TB.getTupleType(SVBool, /*NumVectors=*/2)),
+        SVBoolx4(TB.getTupleType(SVBool, /*NumVectors=*/4)),
         BitInt7(TB.getIntegerType(7, llvm::Align(1), /*Signed=*/true,
                                   /*IsBitInt=*/true)),
         UBitInt7(TB.getIntegerType(7, llvm::Align(1), /*Signed=*/false,
@@ -90,11 +175,13 @@ class AArch64TargetInfoTest : public ::testing::Test {
                                     /*IsBitInt=*/true)),
         BitInt129(TB.getIntegerType(129, llvm::Align(16), /*Signed=*/true,
                                     /*IsBitInt=*/true)),
-        ComplexFloat(TB.getComplexType(F32, llvm::Align(4))),
-        V2F32(TB.getVectorType(F32, llvm::ElementCount::getFixed(2),
-                               llvm::Align(8))),
-        V4F32(TB.getVectorType(F32, llvm::ElementCount::getFixed(4),
-                               llvm::Align(16))) {}
+        BitInt2(TB.getIntegerType(2, llvm::Align(1), /*Signed=*/true,
+                                  /*IsBitInt=*/true)),
+        V4BitInt2(TB.getVectorType(BitInt2, llvm::ElementCount::getFixed(4),
+                                   llvm::Align(4))),
+        V8BitInt2(TB.getVectorType(BitInt2, llvm::ElementCount::getFixed(8),
+                                   llvm::Align(8))),
+        ComplexFloat(TB.getComplexType(F32, llvm::Align(4))) {}
 
   static RecordFlags passableRecordFlags(bool IsCXX = false) {
     unsigned Flags = RecordFlags::CanPassInRegisters;
@@ -126,6 +213,51 @@ static void expectExtendInteger(const ArgInfo &Info, const ABIType *Ty,
   EXPECT_EQ(Info.getCoerceToType(), Ty);
 }
 
+static void expectDirectCoercedInteger(const ArgInfo &Info, uint64_t BitWidth) {
+  EXPECT_TRUE(Info.isDirect());
+  const llvm::abi::IntegerType *IT =
+      llvm::dyn_cast<llvm::abi::IntegerType>(Info.getCoerceToType());
+  ASSERT_NE(IT, nullptr);
+  EXPECT_EQ(IT->getSizeInBits().getFixedValue(), BitWidth);
+}
+
+static void expectDirectCoercedI32Vector(const ArgInfo &Info,
+                                         unsigned NumElts) {
+  EXPECT_TRUE(Info.isDirect());
+  const llvm::abi::VectorType *VT =
+      llvm::dyn_cast<llvm::abi::VectorType>(Info.getCoerceToType());
+  ASSERT_NE(VT, nullptr);
+  EXPECT_EQ(VT->getNumElements().getKnownMinValue(), NumElts);
+  EXPECT_EQ(VT->getElementType()->getSizeInBits().getFixedValue(), 32u);
+}
+
+// Checks that \p Info is Direct with a coercion to a scalable SVE data
+// vector of \p MinElts copies of \p EltTy.
+static void expectDirectCoercedSVEData(const ArgInfo &Info,
+                                       const ABIType *EltTy, unsigned MinElts) {
+  EXPECT_TRUE(Info.isDirect());
+  const auto *VT =
+      llvm::dyn_cast<llvm::abi::VectorType>(Info.getCoerceToType());
+  ASSERT_NE(VT, nullptr);
+  EXPECT_TRUE(VT->isSVEData());
+  EXPECT_TRUE(VT->isScalable());
+  EXPECT_EQ(VT->getNumElements().getKnownMinValue(), MinElts);
+  // The element type is carried over unchanged, so signedness survives.
+  EXPECT_EQ(VT->getElementType(), EltTy);
+}
+
+// Checks that \p Info is Direct with a coercion to svbool_t.
+static void expectDirectCoercedSVEPredicate(const ArgInfo &Info) {
+  EXPECT_TRUE(Info.isDirect());
+  const auto *VT =
+      llvm::dyn_cast<llvm::abi::VectorType>(Info.getCoerceToType());
+  ASSERT_NE(VT, nullptr);
+  EXPECT_TRUE(VT->isSVEPredicate());
+  EXPECT_TRUE(VT->isScalable());
+  EXPECT_EQ(VT->getNumElements().getKnownMinValue(), 16u);
+  EXPECT_EQ(VT->getElementType()->getSizeInBits().getFixedValue(), 1u);
+}
+
 static void expectAlignedIndirect(const ArgInfo &Info, llvm::Align Align,
                                   bool ByVal = false) {
   EXPECT_TRUE(Info.isIndirect());
@@ -279,6 +411,280 @@ 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);
+    }
+  }
+}
+
+// SVE data and predicate tuples are passed and returned directly, without
+// coercion, under DarwinPCS, Win64, and the soft-float ABI.
+TEST_F(AArch64TargetInfoTest, ClassifySVETuplesDirect) {
+  const ABIType *TupleTypes[] = {SVInt32x2, SVInt32x3, SVInt32x4, SVBoolx2,
+                                 SVBoolx4};
+
+  for (AArch64ABIKind Kind : {AArch64ABIKind::DarwinPCS, AArch64ABIKind::Win64,
+                              AArch64ABIKind::AAPCSSoft}) {
+    std::unique_ptr<TargetInfo> TI =
+        createAArch64TargetInfo(TB, AArch64ABIOptions(Kind));
+
+    for (const ABIType *Ty : TupleTypes) {
+      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 &[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);
+    }
+  }
+}
+
+// Fixed-length SVE predicates are described with 8-bit elements, but they are
+// coerced to svbool_t, which has one-bit elements.
+TEST_F(AArch64TargetInfoTest, ClassifyFixedLengthSVEPredicateCoerced) {
+  for (AArch64ABIKind Kind :
+       {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, AArch64ABIKind::Win64,
+        AArch64ABIKind::AAPCSSoft}) {
+    std::unique_ptr<TargetInfo> TI =
+        createAArch64TargetInfo(TB, AArch64ABIOptions(Kind));
+
+    std::unique_ptr<FunctionInfo> FI =
+        FunctionInfo::create(llvm::CallingConv::C, FixedSVBool, {FixedSVBool});
+    FI->getReturnInfo() = ArgInfo::getIgnore();
+    TI->computeInfo(*FI);
+    expectDirectCoercedSVEPredicate(FI->getReturnInfo());
+    expectDirectCoercedSVEPredicate(FI->getArgInfo(0).Info);
+  }
+}
+
+// Arm64EC variadic functions classify their arguments with the x86-64 rules,
+// which are not implemented yet. Every argument is deferred, including the
+// named ones. Non-variadic functions are unaffected.
+TEST_F(AArch64TargetInfoTest,
+       ClassifyArgumentArm64ECVariadicNotYetImplemented) {
+  AArch64ABIOptions Opts(AArch64ABIKind::Win64);
+  Opts.IsWindowsArm64EC = true;
+  std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Opts);
+
+  std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(
+      llvm::CallingConv::C, Void, {I32, F64}, RequiredArgs(1));
+  TI->computeInfo(*FI);
+  EXPECT_TRUE(FI->getArgInfo(0).Info.isIgnore());
+  EXPECT_TRUE(FI->getArgInfo(1).Info.isIgnore());
+
+  FI = FunctionInfo::create(llvm::CallingConv::C, Void, {I32, F64},
+                            RequiredArgs::All);
+  TI->computeInfo(*FI);
+  expectUncoercedDirect(FI->getArgInfo(0).Info);
+  expectUncoercedDirect(FI->getArgInfo(1).Info);
+}
+
 // Non-aggregate scalars, matrix types, and promotable integers take the Direct
 // argument path under AAPCS.
 TEST_F(AArch64TargetInfoTest, ClassifyArgumentScalarsDirectAAPCS) {
@@ -719,12 +1125,12 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnCXXCannotPassInRegistersIndirect) {
   } Cases[] = {{NonPassableHFA, llvm::Align(4)},
                {VirtualDerived, llvm::Align(8)}};
 
-  for (const auto &Case : Cases) {
+  for (const auto &[RetTy, ExpectedAlign] : Cases) {
     std::unique_ptr<FunctionInfo> FI =
-        FunctionInfo::create(llvm::CallingConv::C, Case.RetTy, {});
+        FunctionInfo::create(llvm::CallingConv::C, RetTy, {});
     FI->getReturnInfo() = ArgInfo::getIgnore();
     TI->computeInfo(*FI);
-    expectNaturalAlignIndirect(FI->getReturnInfo(), Case.ExpectedAlign,
+    expectNaturalAlignIndirect(FI->getReturnInfo(), ExpectedAlign,
                                /*ByVal=*/false);
   }
 }

diff  --git a/llvm/unittests/ABI/IRTypeMapperTest.cpp b/llvm/unittests/ABI/IRTypeMapperTest.cpp
index f324aa049a471..0cf2061f520d2 100644
--- a/llvm/unittests/ABI/IRTypeMapperTest.cpp
+++ b/llvm/unittests/ABI/IRTypeMapperTest.cpp
@@ -71,7 +71,9 @@ TEST_F(IRTypeMapperTest, SVEPredicateVectorMapsToScalableI1Vector) {
 }
 
 TEST_F(IRTypeMapperTest, SVECountMapsToAArch64SVCount) {
-  const llvm::abi::VectorType *SVCount = TB.getSVECountType(llvm::Align(2));
+  const llvm::abi::VectorType *SVCount =
+      TB.getScalablePredicateOrCountVectorType(llvm::Align(2),
+                                               llvm::abi::VectorKind::SVECount);
 
   auto *TET = llvm::dyn_cast<llvm::TargetExtType>(Mapper.convertType(SVCount));
   ASSERT_NE(TET, nullptr);

diff  --git a/llvm/unittests/ABI/TypesTest.cpp b/llvm/unittests/ABI/TypesTest.cpp
index a69295e2e052d..93fbde360330c 100644
--- a/llvm/unittests/ABI/TypesTest.cpp
+++ b/llvm/unittests/ABI/TypesTest.cpp
@@ -210,7 +210,8 @@ TEST_F(ABITypesTest, SVEPredicateVector) {
 }
 
 TEST_F(ABITypesTest, SVECount) {
-  const VectorType *SVCount = TB.getSVECountType(Align(2));
+  const VectorType *SVCount =
+      TB.getScalablePredicateOrCountVectorType(Align(2), VectorKind::SVECount);
 
   EXPECT_TRUE(SVCount->isSVECount());
   EXPECT_TRUE(SVCount->isSVEType());


        


More information about the llvm-commits mailing list