[Mlir-commits] [clang] [llvm] [mlir] [LLVMABI][AARCH64] Added aggregate return support (PR #229202)
Kunal Dubey
llvmlistbot at llvm.org
Tue Oct 6 03:35:13 PDT 2026
https://github.com/xakep8 updated https://github.com/llvm/llvm-project/pull/229202
>From 7867bcdd41cc059fe2b26d490df46ef63353806e Mon Sep 17 00:00:00 2001
From: Kunal Dubey <xakep8 at protonmail.com>
Date: Tue, 6 Oct 2026 13:35:57 +0530
Subject: [PATCH] [LLVMABI][AArch64] Support aggregate arguments
Implement AArch64 argument classification for fixed-size aggregates.
---
.../Transforms/CallConvLoweringPass.cpp | 10 +-
clang/lib/CodeGen/QualTypeMapper.cpp | 39 ++--
clang/lib/CodeGen/QualTypeMapper.h | 4 +-
.../AArch64/abi-classify-arg-pointer-like.m | 46 ++++
.../CodeGen/AArch64/abi-classify-arg-types.c | 94 ++++++--
.../AArch64/abi-classify-arg-types.cpp | 30 ++-
.../unittests/CodeGen/QualTypeMapperTest.cpp | 71 ++++++
llvm/include/llvm/ABI/Types.h | 36 ++-
llvm/lib/ABI/Targets/AArch64.cpp | 56 ++++-
llvm/lib/ABI/Targets/AMDGPU.cpp | 6 +-
llvm/unittests/ABI/AArch64TargetInfoTest.cpp | 207 +++++++++++++++++-
llvm/unittests/ABI/AMDGPUTargetInfoTest.cpp | 9 +-
llvm/unittests/ABI/TypesTest.cpp | 24 +-
llvm/unittests/ABI/X86TargetInfoTest.cpp | 12 +-
mlir/lib/ABI/ABITypeMapper.cpp | 3 +-
15 files changed, 590 insertions(+), 57 deletions(-)
create mode 100644 clang/test/CodeGen/AArch64/abi-classify-arg-pointer-like.m
diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index 402cd09afd6370..d1cdd1ab8ff5ae 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -361,16 +361,22 @@ static const llvm::abi::Type *mapCIRType(mlir::Type type,
dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
ptrTy.getAddrSpace()))
addrSpace = targetAsAttr.getValue();
+ // CIR pointer types do not retain whether their source type was an
+ // ordinary pointer/reference or another pointer representation such as
+ // nullptr_t or an Objective-C object pointer. Conservatively avoid
+ // source-pointer-only ABI optimizations until CIR carries that detail.
return tb.getPointerType(dl.getTypeSizeInBits(type),
llvm::Align(dl.getTypeABIAlignment(type)),
- addrSpace);
+ addrSpace, llvm::abi::PointerFlags::None);
})
.Case([&](cir::VPtrType) {
// cir::VPtrType carries no address-space parameter yet, so this
// always maps into the default one until that gap closes.
assert(!cir::MissingFeatures::addressSpace());
return tb.getPointerType(dl.getTypeSizeInBits(type),
- llvm::Align(dl.getTypeABIAlignment(type)));
+ llvm::Align(dl.getTypeABIAlignment(type)),
+ /*Addrspace=*/0,
+ llvm::abi::PointerFlags::IsPointerOrReference);
})
.Case([&](cir::BoolType) {
return tb.getIntegerType(dl.getTypeSizeInBits(type),
diff --git a/clang/lib/CodeGen/QualTypeMapper.cpp b/clang/lib/CodeGen/QualTypeMapper.cpp
index 5044883a49ca2e..841e0dcdc90558 100644
--- a/clang/lib/CodeGen/QualTypeMapper.cpp
+++ b/clang/lib/CodeGen/QualTypeMapper.cpp
@@ -103,11 +103,14 @@ const llvm::abi::Type *QualTypeMapper::convertTypeImpl(QualType QT) {
case Type::Builtin:
return convertBuiltinType(cast<BuiltinType>(QT));
case Type::Pointer:
- return createPointerTypeForPointee(cast<PointerType>(QT)->getPointeeType());
+ return createPointerTypeForPointee(
+ cast<PointerType>(QT)->getPointeeType(),
+ llvm::abi::PointerFlags::IsPointerOrReference);
case Type::LValueReference:
case Type::RValueReference:
return createPointerTypeForPointee(
- cast<ReferenceType>(QT)->getPointeeType());
+ cast<ReferenceType>(QT)->getPointeeType(),
+ llvm::abi::PointerFlags::IsPointerOrReference);
case Type::ConstantArray:
case Type::ArrayParameter:
case Type::IncompleteArray:
@@ -129,7 +132,8 @@ const llvm::abi::Type *QualTypeMapper::convertTypeImpl(QualType QT) {
}
case Type::BlockPointer:
case Type::Pipe:
- return createPointerTypeForPointee(ASTCtx.VoidPtrTy);
+ return createPointerTypeForPointee(ASTCtx.VoidPtrTy,
+ llvm::abi::PointerFlags::None);
case Type::ConstantMatrix: {
const auto *MT = cast<ConstantMatrixType>(QT);
return Builder.getArrayType(convertType(MT->getElementType()),
@@ -146,13 +150,18 @@ const llvm::abi::Type *QualTypeMapper::convertTypeImpl(QualType QT) {
}
case Type::ObjCObject:
case Type::ObjCInterface:
- case Type::ObjCObjectPointer:
+ case Type::ObjCObjectPointer: {
// Objective-C objects are represented as pointers in the ABI.
+ llvm::abi::PointerFlags Flags = llvm::abi::PointerFlags::None;
+ if (QT.hasAddressSpace())
+ Flags |= llvm::abi::PointerFlags::IsPointeeAddressSpaceQualified;
return Builder.getPointerType(
ASTCtx.getTargetInfo().getPointerWidth(QT.getAddressSpace()),
llvm::Align(
ASTCtx.getTargetInfo().getPointerAlign(QT.getAddressSpace()) / 8),
- ASTCtx.getTargetInfo().getTargetAddressSpace(QT.getAddressSpace()));
+ ASTCtx.getTargetInfo().getTargetAddressSpace(QT.getAddressSpace()),
+ Flags);
+ }
case Type::OverflowBehavior:
return convertType(cast<OverflowBehaviorType>(QT)->getUnderlyingType());
case Type::Auto:
@@ -178,7 +187,7 @@ QualTypeMapper::convertBuiltinType(const BuiltinType *BT) {
return Builder.getVoidType();
case BuiltinType::NullPtr:
- return createPointerTypeForPointee(QT);
+ return createPointerTypeForPointee(QT, llvm::abi::PointerFlags::None);
case BuiltinType::Bool:
return Builder.getIntegerType(1, getTypeAlign(QT), /*Signed=*/false,
@@ -265,13 +274,13 @@ QualTypeMapper::convertBuiltinType(const BuiltinType *BT) {
case BuiltinType::OCLClkEvent:
case BuiltinType::OCLQueue:
case BuiltinType::OCLReserveID:
- return createPointerTypeForPointee(QT);
+ return createPointerTypeForPointee(QT, llvm::abi::PointerFlags::None);
// Objective-C builtin types are represented as opaque pointers.
case BuiltinType::ObjCId:
case BuiltinType::ObjCClass:
case BuiltinType::ObjCSel:
- return createPointerTypeForPointee(QT);
+ return createPointerTypeForPointee(QT, llvm::abi::PointerFlags::None);
// AArch64 SVE data and predicate types, including the x2/x3/x4 tuples.
#define SVE_VECTOR_TYPE(Name, MangledName, Id, SingletonId) \
@@ -356,7 +365,8 @@ QualTypeMapper::convertArrayType(const clang::ArrayType *AT) {
if (isa<IncompleteArrayType>(AT))
return Builder.getArrayType(ElementType, 0, 0);
if (const auto *VAT = dyn_cast<VariableArrayType>(AT))
- return createPointerTypeForPointee(VAT->getPointeeType());
+ return createPointerTypeForPointee(VAT->getPointeeType(),
+ llvm::abi::PointerFlags::None);
llvm::reportFatalInternalError(
"unexpected array type in ABI lowering (dependent array types should be "
"resolved before reaching this point)");
@@ -480,8 +490,8 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl *RD) {
// Add vtable pointer for polymorphic classes
if (RD->isPolymorphic()) {
- const llvm::abi::Type *VtablePointer =
- createPointerTypeForPointee(ASTCtx.VoidPtrTy);
+ const llvm::abi::Type *VtablePointer = createPointerTypeForPointee(
+ ASTCtx.VoidPtrTy, llvm::abi::PointerFlags::IsPointerOrReference);
Fields.emplace_back(VtablePointer, 0);
}
@@ -620,7 +630,8 @@ llvm::Align QualTypeMapper::getTypeAlign(QualType QT) const {
}
const llvm::abi::Type *
-QualTypeMapper::createPointerTypeForPointee(QualType PointeeType) {
+QualTypeMapper::createPointerTypeForPointee(QualType PointeeType,
+ llvm::abi::PointerFlags Flags) {
auto AddrSpace = PointeeType.getAddressSpace();
auto PointerSize = ASTCtx.getTargetInfo().getPointerWidth(AddrSpace);
llvm::Align Alignment =
@@ -632,8 +643,10 @@ QualTypeMapper::createPointerTypeForPointee(QualType PointeeType) {
PointeeType->isFunctionType() && !PointeeType.hasAddressSpace()
? DL.getProgramAddressSpace()
: ASTCtx.getTargetInfo().getTargetAddressSpace(AddrSpace);
+ if (PointeeType.hasAddressSpace())
+ Flags |= llvm::abi::PointerFlags::IsPointeeAddressSpaceQualified;
return Builder.getPointerType(PointerSize, llvm::Align(Alignment.value() / 8),
- TargetAddrSpace);
+ TargetAddrSpace, Flags);
}
/// Processes the fields of a record (struct/class/union) and populates
diff --git a/clang/lib/CodeGen/QualTypeMapper.h b/clang/lib/CodeGen/QualTypeMapper.h
index f6bb43482c705e..c909468fc04bb0 100644
--- a/clang/lib/CodeGen/QualTypeMapper.h
+++ b/clang/lib/CodeGen/QualTypeMapper.h
@@ -51,7 +51,9 @@ class QualTypeMapper {
const llvm::abi::RecordType *convertStructType(const clang::RecordDecl *RD);
const llvm::abi::RecordType *convertUnionType(const clang::RecordDecl *RD);
- const llvm::abi::Type *createPointerTypeForPointee(QualType PointeeType);
+ const llvm::abi::Type *
+ createPointerTypeForPointee(QualType PointeeType,
+ llvm::abi::PointerFlags Flags);
const llvm::abi::RecordType *convertCXXRecordType(const CXXRecordDecl *RD);
void computeFieldInfo(const clang::RecordDecl *RD,
diff --git a/clang/test/CodeGen/AArch64/abi-classify-arg-pointer-like.m b/clang/test/CodeGen/AArch64/abi-classify-arg-pointer-like.m
new file mode 100644
index 00000000000000..feea53e9c9b2f8
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/abi-classify-arg-pointer-like.m
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fblocks -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fblocks -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+
+// Objective-C object pointers and block pointers are pointer representations,
+// but they are not ordinary C/C++ pointers or references and therefore use
+// integer coercions for aggregate arguments.
+
+ at class Object;
+
+typedef struct {
+ id value;
+} ObjCIdAgg;
+void arg_objc_id(ObjCIdAgg a) {}
+// CHECK: define{{.*}} void @arg_objc_id(i64 %{{.*}})
+
+typedef struct {
+ Class value;
+} ObjCClassAgg;
+void arg_objc_class(ObjCClassAgg a) {}
+// CHECK: define{{.*}} void @arg_objc_class(i64 %{{.*}})
+
+typedef struct {
+ SEL value;
+} ObjCSelAgg;
+void arg_objc_sel(ObjCSelAgg a) {}
+// SEL canonicalizes to an ordinary pointer type.
+// CHECK: define{{.*}} void @arg_objc_sel(ptr %{{.*}})
+
+typedef struct {
+ Object *value;
+} ObjCObjectPointerAgg;
+void arg_objc_object_pointer(ObjCObjectPointerAgg a) {}
+// CHECK: define{{.*}} void @arg_objc_object_pointer(i64 %{{.*}})
+
+typedef struct {
+ id first;
+ id second;
+} ObjCIdPairAgg;
+void arg_objc_id_pair(ObjCIdPairAgg a) {}
+// CHECK: define{{.*}} void @arg_objc_id_pair([2 x i64] %{{.*}})
+
+typedef struct {
+ void (^value)(void);
+} BlockPointerAgg;
+void arg_block_pointer(BlockPointerAgg a) {}
+// CHECK: define{{.*}} void @arg_block_pointer(i64 %{{.*}})
diff --git a/clang/test/CodeGen/AArch64/abi-classify-arg-types.c b/clang/test/CodeGen/AArch64/abi-classify-arg-types.c
index 274c6010ea7e47..7950e3320d379e 100644
--- a/clang/test/CodeGen/AArch64/abi-classify-arg-types.c
+++ b/clang/test/CodeGen/AArch64/abi-classify-arg-types.c
@@ -1,17 +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,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"
+// 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,PTR64
+// 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,PTR64 --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,PTR32
+// 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,PTR32 --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,PTR64
+// 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,PTR64 --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,PTR64
+// 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,PTR64 --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,PTR64
+// 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,PTR64 --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,PTR64
+// 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,PTR64 --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,PTR64
+// 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,PTR64 --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.
@@ -113,6 +113,72 @@ void arg_bitint128(_BitInt(128) x) {}
void arg_bitint129(_BitInt(129) x) {}
// CHECK: define{{.*}} void @arg_bitint129(ptr nofreeobj noundef align 16 dead_on_return dereferenceable(32) %{{.*}})
+// Non-homogeneous fixed-size aggregates of at most 16 bytes are passed
+// directly. Larger aggregates are passed indirectly.
+typedef struct {
+ char x[3];
+} Agg3;
+void arg_agg3(Agg3 a) {}
+// PTR64: define{{.*}} void @arg_agg3(i64 %{{.*}})
+// PTR32: define{{.*}} void @arg_agg3(i32 %{{.*}})
+
+typedef struct {
+ char x[9];
+} Agg9;
+void arg_agg9(Agg9 a) {}
+// PTR64: define{{.*}} void @arg_agg9([2 x i64] %{{.*}})
+// PTR32: define{{.*}} void @arg_agg9([3 x i32] %{{.*}})
+
+typedef struct {
+ long long x, y;
+} Agg16;
+void arg_agg16(Agg16 a) {}
+// CHECK: define{{.*}} void @arg_agg16([2 x i64] %{{.*}})
+
+typedef struct {
+ _Alignas(16) char x[16];
+} Agg16Align16;
+void arg_agg16_align16(Agg16Align16 a) {}
+// CHECK: define{{.*}} void @arg_agg16_align16(i128 %{{.*}})
+
+typedef struct {
+ char x[17];
+} Agg17;
+void arg_agg17(Agg17 a) {}
+// CHECK: define{{.*}} void @arg_agg17(ptr nofreeobj noundef align 1 {{(dead_on_return )?}}dereferenceable(17) %{{.*}})
+
+// Aggregates made entirely of 64-bit default-address-space pointers retain
+// pointer types in their coercion.
+typedef struct {
+ int *p;
+} PtrAgg1;
+void arg_ptr_agg1(PtrAgg1 a) {}
+// PTR64: define{{.*}} void @arg_ptr_agg1(ptr %{{.*}})
+// PTR32: define{{.*}} void @arg_ptr_agg1(i32 %{{.*}})
+
+typedef struct {
+ int *p, *q;
+} PtrAgg2;
+void arg_ptr_agg2(PtrAgg2 a) {}
+// PTR64: define{{.*}} void @arg_ptr_agg2([2 x ptr] %{{.*}})
+// PTR32: define{{.*}} void @arg_ptr_agg2([2 x i32] %{{.*}})
+
+typedef int __attribute__((address_space(0))) as0_int;
+typedef struct {
+ as0_int *p;
+} ExplicitAS0PtrAgg;
+void arg_explicit_as0_ptr_agg(ExplicitAS0PtrAgg a) {}
+// PTR64: define{{.*}} void @arg_explicit_as0_ptr_agg(i64 %{{.*}})
+// PTR32: define{{.*}} void @arg_explicit_as0_ptr_agg(i32 %{{.*}})
+
+typedef int __attribute__((address_space(1))) as1_int;
+typedef struct {
+ as1_int *p;
+} NonDefaultASPtrAgg;
+void arg_nondefault_as_ptr_agg(NonDefaultASPtrAgg a) {}
+// PTR64: define{{.*}} void @arg_nondefault_as_ptr_agg(i64 %{{.*}})
+// PTR32: define{{.*}} void @arg_nondefault_as_ptr_agg(i32 %{{.*}})
+
// Homogeneous floating-point aggregates are coerced to an array of the base
// type. AAPCS sets alignstack from unadjusted alignment (8, or 16 if the
// unadjusted alignment is at least 16). DarwinPCS and Win64 do not.
diff --git a/clang/test/CodeGen/AArch64/abi-classify-arg-types.cpp b/clang/test/CodeGen/AArch64/abi-classify-arg-types.cpp
index 8e0bbb0e06b577..fa384187a43eb0 100644
--- a/clang/test/CodeGen/AArch64/abi-classify-arg-types.cpp
+++ b/clang/test/CodeGen/AArch64/abi-classify-arg-types.cpp
@@ -1,11 +1,11 @@
-// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -std=c++20 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,NOHFAALIGN
-// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -std=c++20 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,NOHFAALIGN --implicit-check-not="not yet implemented"
-// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -target-abi darwinpcs -std=c++20 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,NOHFAALIGN
-// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -target-abi darwinpcs -std=c++20 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,NOHFAALIGN --implicit-check-not="not yet implemented"
-// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++20 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS64
-// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++20 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS64 --implicit-check-not="not yet implemented"
-// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -std=c++20 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS64
-// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -std=c++20 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS64 --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -std=c++20 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,NOHFAALIGN,PTR64
+// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -std=c++20 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,NOHFAALIGN,PTR64 --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -target-abi darwinpcs -std=c++20 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,NOHFAALIGN,PTR32
+// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -target-abi darwinpcs -std=c++20 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,NOHFAALIGN,PTR32 --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++20 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS64,PTR64
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++20 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS64,PTR64 --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -std=c++20 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,AAPCS64,PTR64
+// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -std=c++20 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK,AAPCS64,PTR64 --implicit-check-not="not yet implemented"
// This test is verifying that the LLVM ABI library classifies C++ record
// arguments that cannot be passed in registers the same way Clang does without
@@ -42,6 +42,20 @@ void arg_nontrivial_dtor_and_copy(NonTrivialDtorAndCopy a) {}
void arg_explicit_copy(ExplicitCopy a) {}
// CHECK: define{{.*}} void @arg_explicit_copy(ptr nofreeobj noundef align 2 dead_on_return dereferenceable(2) %{{.*}})
+struct ReferenceAgg {
+ int &r;
+};
+void arg_reference_agg(ReferenceAgg a) {}
+// PTR64: define{{.*}} void @arg_reference_agg(ptr %{{.*}})
+// PTR32: define{{.*}} void @arg_reference_agg(i32 %{{.*}})
+
+struct NullPtrAgg {
+ decltype(nullptr) p;
+};
+void arg_nullptr_agg(NullPtrAgg a) {}
+// PTR64: define{{.*}} void @arg_nullptr_agg(i64 %{{.*}})
+// PTR32: define{{.*}} void @arg_nullptr_agg(i32 %{{.*}})
+
// Homogeneous aggregates that can pass in registers are coerced to an array of
// the base type, including inherited members, nested records, and zero-length
// bitfields.
diff --git a/clang/unittests/CodeGen/QualTypeMapperTest.cpp b/clang/unittests/CodeGen/QualTypeMapperTest.cpp
index 6fdd4318bf4f0d..ad510640387ed7 100644
--- a/clang/unittests/CodeGen/QualTypeMapperTest.cpp
+++ b/clang/unittests/CodeGen/QualTypeMapperTest.cpp
@@ -292,4 +292,75 @@ TEST_F(QualTypeMapperSVETest, PlainVectorIsGeneric) {
EXPECT_EQ(Int32x4->getSizeInBits(), llvm::TypeSize::getFixed(128));
}
+class QualTypeMapperPointerTest : public ::testing::Test {
+protected:
+ QualTypeMapperPointerTest()
+ : AST(makeInputs()), Mapper(AST.context(), DL, Alloc) {}
+
+ const llvm::abi::PointerType *mapTypedef(StringRef Name) {
+ for (Decl *D : AST.context().getTranslationUnitDecl()->decls())
+ if (const auto *TD = dyn_cast<TypedefNameDecl>(D))
+ if (TD->getName() == Name)
+ return dyn_cast<llvm::abi::PointerType>(
+ Mapper.convertType(TD->getUnderlyingType()));
+ ADD_FAILURE() << "no typedef named " << Name;
+ return nullptr;
+ }
+
+private:
+ static TestInputs makeInputs() {
+ TestInputs Inputs(R"c(
+typedef int *ordinary_pointer_t;
+typedef int &reference_t;
+typedef decltype(nullptr) nullptr_t;
+typedef int __attribute__((address_space(1))) *address_space_pointer_t;
+typedef void (^block_pointer_t)(void);
+typedef id objc_id_t;
+typedef Class objc_class_t;
+typedef SEL objc_selector_t;
+ at interface Object
+ at end
+typedef Object *objc_object_pointer_t;
+)c");
+ Inputs.Language = TestLanguage::Lang_OBJCXX;
+ Inputs.ExtraArgs = {"-triple", "aarch64-unknown-linux-gnu", "-fblocks"};
+ return Inputs;
+ }
+
+ TestAST AST;
+ llvm::DataLayout DL;
+ llvm::BumpPtrAllocator Alloc;
+ CodeGen::QualTypeMapper Mapper;
+};
+
+TEST_F(QualTypeMapperPointerTest, SourceTypeFlags) {
+ const auto *Ordinary = mapTypedef("ordinary_pointer_t");
+ ASSERT_NE(Ordinary, nullptr);
+ EXPECT_TRUE(Ordinary->isPointerOrReference());
+ EXPECT_FALSE(Ordinary->isPointeeAddressSpaceQualified());
+
+ const auto *Reference = mapTypedef("reference_t");
+ ASSERT_NE(Reference, nullptr);
+ EXPECT_TRUE(Reference->isPointerOrReference());
+ EXPECT_FALSE(Reference->isPointeeAddressSpaceQualified());
+
+ const auto *AddressSpace = mapTypedef("address_space_pointer_t");
+ ASSERT_NE(AddressSpace, nullptr);
+ EXPECT_TRUE(AddressSpace->isPointerOrReference());
+ EXPECT_TRUE(AddressSpace->isPointeeAddressSpaceQualified());
+
+ for (StringRef Name : {"nullptr_t", "block_pointer_t", "objc_id_t",
+ "objc_class_t", "objc_object_pointer_t"}) {
+ SCOPED_TRACE(Name);
+ const auto *Pointer = mapTypedef(Name);
+ ASSERT_NE(Pointer, nullptr);
+ EXPECT_FALSE(Pointer->isPointerOrReference());
+ }
+
+ // SEL is canonically an ordinary pointer on this target.
+ const auto *Selector = mapTypedef("objc_selector_t");
+ ASSERT_NE(Selector, nullptr);
+ EXPECT_TRUE(Selector->isPointerOrReference());
+}
+
} // namespace
diff --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h
index b7083d181eae88..e8fb5ec9451752 100644
--- a/llvm/include/llvm/ABI/Types.h
+++ b/llvm/include/llvm/ABI/Types.h
@@ -27,6 +27,8 @@
namespace llvm {
namespace abi {
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
enum class TypeKind {
Void,
Atomic,
@@ -202,11 +204,37 @@ class PointerLikeType : public Type {
}
};
+/// Source-level properties for types represented as ABI pointers.
+///
+/// These flags do not describe the target address-space number. In particular,
+/// an explicitly qualified source pointer may map to target address space zero,
+/// while an unqualified source pointer may map to a nonzero program address
+/// space.
+enum class PointerFlags {
+ None = 0,
+ IsPointerOrReference = 1 << 0,
+ IsPointeeAddressSpaceQualified = 1 << 1,
+ LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/IsPointeeAddressSpaceQualified)
+};
+
class PointerType : public PointerLikeType {
+ PointerFlags Flags;
+
public:
- PointerType(uint64_t Size, Align ABIAlign, unsigned AddressSpace = 0)
+ PointerType(uint64_t Size, Align ABIAlign, unsigned AddressSpace,
+ PointerFlags Flags)
: PointerLikeType(TypeKind::Pointer, TypeSize::getFixed(Size), ABIAlign,
- AddressSpace) {}
+ AddressSpace),
+ Flags(Flags) {}
+
+ PointerFlags getFlags() const { return Flags; }
+ bool isPointerOrReference() const {
+ return (Flags & PointerFlags::IsPointerOrReference) != PointerFlags::None;
+ }
+ bool isPointeeAddressSpaceQualified() const {
+ return (Flags & PointerFlags::IsPointeeAddressSpaceQualified) !=
+ PointerFlags::None;
+ }
static bool classof(const Type *T) {
return T->getKind() == TypeKind::Pointer;
@@ -499,9 +527,9 @@ class TypeBuilder {
}
const PointerType *getPointerType(uint64_t Size, Align Align,
- unsigned Addrspace = 0) {
+ unsigned Addrspace, PointerFlags Flags) {
return new (Allocator.Allocate<PointerType>())
- PointerType(Size, Align, Addrspace);
+ PointerType(Size, Align, Addrspace, Flags);
}
const ArrayType *getArrayType(const Type *ElementType, uint64_t NumElements,
diff --git a/llvm/lib/ABI/Targets/AArch64.cpp b/llvm/lib/ABI/Targets/AArch64.cpp
index 9573f31e1a4053..954a4db37ef62d 100644
--- a/llvm/lib/ABI/Targets/AArch64.cpp
+++ b/llvm/lib/ABI/Targets/AArch64.cpp
@@ -84,6 +84,29 @@ static void reportNYI(StringRef Feature) {
<< " is not yet implemented for AArch64 in the LLVM ABI library.\n";
}
+static bool containsOnlyPointers(const Type *Ty) {
+ while (const auto *AT = dyn_cast<ArrayType>(Ty))
+ Ty = AT->getElementType();
+
+ if (const auto *PT = dyn_cast<PointerType>(Ty))
+ return PT->getSizeInBits().getFixedValue() == 64 &&
+ PT->isPointerOrReference() && !PT->isPointeeAddressSpaceQualified();
+
+ const auto *RT = dyn_cast<RecordType>(Ty);
+ if (!RT || RT->isEmpty())
+ return false;
+
+ for (const FieldInfo &Base : RT->getBaseClasses())
+ if (!containsOnlyPointers(Base.FieldType))
+ return false;
+
+ for (const FieldInfo &Field : RT->getFields())
+ if (!containsOnlyPointers(Field.FieldType))
+ return false;
+
+ return true;
+}
+
ArgInfo AArch64TargetInfo::classifyReturnType(const Type *RetTy,
bool IsVariadicFn) const {
if (RetTy->isVoid())
@@ -227,8 +250,37 @@ ArgInfo AArch64TargetInfo::classifyArgumentType(
return ArgInfo::getDirect(CoerceTy, /*Offset=*/0, llvm::Align(TyAlign));
}
- reportNYI("Aggregate argument type handling");
- return ArgInfo::getIgnore();
+ if (Ty->isSVESizelessType()) {
+ reportNYI("Pure scalable aggregate argument type handling");
+ return ArgInfo::getIgnore();
+ }
+
+ // Non-homogeneous aggregates up to 16 bytes are passed directly in registers
+ // or on the stack.
+ if (Size <= 128) {
+ llvm::Align Alignment =
+ Opts.Kind == AArch64ABIKind::AAPCS
+ ? (Ty->getUnadjustedAlignment() < llvm::Align(16) ? llvm::Align(8)
+ : llvm::Align(16))
+ : std::max(Ty->getAlignment(), llvm::Align(Opts.IsILP32 ? 4 : 8));
+ uint64_t AlignmentInBits = Alignment.value() * 8;
+ Size = llvm::alignTo(Size, AlignmentInBits);
+
+ const Type *BaseTy;
+ if ((Size == 64 || Size == 128) && Alignment == llvm::Align(8) &&
+ containsOnlyPointers(Ty))
+ BaseTy = TB.getPointerType(64, llvm::Align(8), /*Addrspace=*/0,
+ PointerFlags::None);
+ else
+ BaseTy = TB.getIntegerType(AlignmentInBits, Alignment, /*Signed=*/false);
+
+ if (Size == AlignmentInBits)
+ return ArgInfo::getDirect(BaseTy);
+ return ArgInfo::getDirect(
+ TB.getArrayType(BaseTy, Size / AlignmentInBits, Size));
+ }
+
+ return getNaturalAlignIndirect(Ty, getAllocaAddrSpace(), /*ByVal=*/false);
}
bool AArch64TargetInfo::passAsAggregateType(const Type *Ty) const {
diff --git a/llvm/lib/ABI/Targets/AMDGPU.cpp b/llvm/lib/ABI/Targets/AMDGPU.cpp
index c6488a1c539fd4..30e189f4270115 100644
--- a/llvm/lib/ABI/Targets/AMDGPU.cpp
+++ b/llvm/lib/ABI/Targets/AMDGPU.cpp
@@ -138,9 +138,9 @@ ArgInfo AMDGPUTargetInfo::classifyKernelArgumentType(const Type *Ty) const {
if (CoerceGenericPtrArgToGlobal) {
if (const auto *PtrTy = dyn_cast<PointerType>(Ty);
PtrTy && PtrTy->getAddrSpace() == AMDGPUAS::FLAT_ADDRESS) {
- const Type *Coerced =
- TB.getPointerType(PtrTy->getSizeInBits().getFixedValue(),
- PtrTy->getAlignment(), AMDGPUAS::GLOBAL_ADDRESS);
+ const Type *Coerced = TB.getPointerType(
+ PtrTy->getSizeInBits().getFixedValue(), PtrTy->getAlignment(),
+ AMDGPUAS::GLOBAL_ADDRESS, PointerFlags::None);
return ArgInfo::getDirect(Coerced, /*Offset=*/0, /*Align=*/std::nullopt,
/*CanBeFlattened=*/false);
}
diff --git a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
index bbb9a52eb48282..d93de25293c59e 100644
--- a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
+++ b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
@@ -106,7 +106,9 @@ class AArch64TargetInfoTest : public ::testing::Test {
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()),
+ Ptr(TB.getPointerType(64, llvm::Align(8), /*Addrspace=*/0,
+ llvm::abi::PointerFlags::IsPointerOrReference)),
+ Void(TB.getVoidType()),
Matrix(TB.getArrayType(F32, /*NumElements=*/4, /*SizeInBits=*/128,
/*IsMatrixType=*/true)),
V2F32(TB.getVectorType(F32, llvm::ElementCount::getFixed(2),
@@ -213,12 +215,57 @@ static void expectExtendInteger(const ArgInfo &Info, const ABIType *Ty,
EXPECT_EQ(Info.getCoerceToType(), Ty);
}
-static void expectDirectCoercedInteger(const ArgInfo &Info, uint64_t BitWidth) {
+static void
+expectDirectCoercedInteger(const ArgInfo &Info, uint64_t BitWidth,
+ llvm::MaybeAlign ExpectedAlignment = std::nullopt) {
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);
+ if (ExpectedAlignment)
+ EXPECT_EQ(IT->getAlignment(), *ExpectedAlignment);
+}
+
+static void expectDirectCoercedIntegerArray(const ArgInfo &Info,
+ uint64_t BitWidth,
+ uint64_t NumElements,
+ llvm::Align Alignment) {
+ EXPECT_TRUE(Info.isDirect());
+ const auto *AT = llvm::dyn_cast<llvm::abi::ArrayType>(Info.getCoerceToType());
+ ASSERT_NE(AT, nullptr);
+ EXPECT_EQ(AT->getNumElements(), NumElements);
+ EXPECT_EQ(AT->getSizeInBits().getFixedValue(), BitWidth * NumElements);
+ EXPECT_EQ(AT->getAlignment(), Alignment);
+ const auto *IT = llvm::dyn_cast<llvm::abi::IntegerType>(AT->getElementType());
+ ASSERT_NE(IT, nullptr);
+ EXPECT_EQ(IT->getSizeInBits().getFixedValue(), BitWidth);
+ EXPECT_EQ(IT->getAlignment(), Alignment);
+}
+
+static void expectDirectCoercedPointer(const ArgInfo &Info) {
+ EXPECT_TRUE(Info.isDirect());
+ const auto *PT =
+ llvm::dyn_cast<llvm::abi::PointerType>(Info.getCoerceToType());
+ ASSERT_NE(PT, nullptr);
+ EXPECT_EQ(PT->getSizeInBits().getFixedValue(), 64u);
+ EXPECT_EQ(PT->getAlignment(), llvm::Align(8));
+ EXPECT_EQ(PT->getAddrSpace(), 0u);
+}
+
+static void expectDirectCoercedPointerArray(const ArgInfo &Info,
+ uint64_t NumElements) {
+ EXPECT_TRUE(Info.isDirect());
+ const auto *AT = llvm::dyn_cast<llvm::abi::ArrayType>(Info.getCoerceToType());
+ ASSERT_NE(AT, nullptr);
+ EXPECT_EQ(AT->getNumElements(), NumElements);
+ EXPECT_EQ(AT->getSizeInBits().getFixedValue(), NumElements * 64);
+ EXPECT_EQ(AT->getAlignment(), llvm::Align(8));
+ const auto *PT = llvm::dyn_cast<llvm::abi::PointerType>(AT->getElementType());
+ ASSERT_NE(PT, nullptr);
+ EXPECT_EQ(PT->getSizeInBits().getFixedValue(), 64u);
+ EXPECT_EQ(PT->getAlignment(), llvm::Align(8));
+ EXPECT_EQ(PT->getAddrSpace(), 0u);
}
static void expectDirectCoercedI32Vector(const ArgInfo &Info,
@@ -901,6 +948,162 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentTransparentUnion) {
}
}
+TEST_F(AArch64TargetInfoTest, ClassifyArgumentFixedAggregatesAAPCS) {
+ const ABIType *Agg24 =
+ makeRecord({FieldInfo(TB.getArrayType(I8, 3, /*SizeInBits=*/24), 0)}, 24,
+ llvm::Align(1), llvm::Align(1));
+ const ABIType *Agg72 =
+ makeRecord({FieldInfo(TB.getArrayType(I8, 9, /*SizeInBits=*/72), 0)}, 72,
+ llvm::Align(1), llvm::Align(1));
+ const ABIType *Agg128Align8 =
+ makeRecord({FieldInfo(I64, 0), FieldInfo(I64, 64)}, 128, llvm::Align(8),
+ llvm::Align(8));
+ const ABIType *Agg128Align16 =
+ makeRecord({FieldInfo(I128, 0)}, 128, llvm::Align(16), llvm::Align(16));
+ const ABIType *Agg136 =
+ makeRecord({FieldInfo(TB.getArrayType(I8, 17, /*SizeInBits=*/136), 0)},
+ 136, llvm::Align(1), llvm::Align(1));
+
+ std::unique_ptr<TargetInfo> TI =
+ createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::AAPCS));
+ auto Classify = [&](const ABIType *Ty) -> ArgInfo {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {Ty});
+ TI->computeInfo(*FI);
+ return FI->getArgInfo(0).Info;
+ };
+
+ expectDirectCoercedInteger(Classify(Agg24), 64, llvm::Align(8));
+ expectDirectCoercedIntegerArray(Classify(Agg72), 64, 2, llvm::Align(8));
+ expectDirectCoercedIntegerArray(Classify(Agg128Align8), 64, 2,
+ llvm::Align(8));
+ expectDirectCoercedInteger(Classify(Agg128Align16), 128, llvm::Align(16));
+ expectNaturalAlignIndirect(Classify(Agg136), llvm::Align(1),
+ /*ByVal=*/false);
+}
+
+TEST_F(AArch64TargetInfoTest, ClassifyArgumentFixedAggregatesILP32) {
+ const ABIType *Agg24 =
+ makeRecord({FieldInfo(TB.getArrayType(I8, 3, /*SizeInBits=*/24), 0)}, 24,
+ llvm::Align(1), llvm::Align(1));
+ const ABIType *Agg72 =
+ makeRecord({FieldInfo(TB.getArrayType(I8, 9, /*SizeInBits=*/72), 0)}, 72,
+ llvm::Align(1), llvm::Align(1));
+
+ AArch64ABIOptions Opts(AArch64ABIKind::DarwinPCS);
+ Opts.IsILP32 = true;
+ std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Opts);
+
+ {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {Agg24});
+ TI->computeInfo(*FI);
+ expectDirectCoercedInteger(FI->getArgInfo(0).Info, 32, llvm::Align(4));
+ }
+ {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {Agg72});
+ TI->computeInfo(*FI);
+ expectDirectCoercedIntegerArray(FI->getArgInfo(0).Info, 32, 3,
+ llvm::Align(4));
+ }
+}
+
+TEST_F(AArch64TargetInfoTest, ClassifyArgumentAggregateAlignmentByABI) {
+ const ABIType *RecordAligned16 =
+ makeRecord({FieldInfo(TB.getArrayType(I8, 16, /*SizeInBits=*/128), 0)},
+ 128, llvm::Align(16), llvm::Align(1));
+
+ for (AArch64ABIKind Kind :
+ {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS}) {
+ std::unique_ptr<TargetInfo> TI =
+ createAArch64TargetInfo(TB, AArch64ABIOptions(Kind));
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {RecordAligned16});
+ TI->computeInfo(*FI);
+ if (Kind == AArch64ABIKind::AAPCS)
+ expectDirectCoercedIntegerArray(FI->getArgInfo(0).Info, 64, 2,
+ llvm::Align(8));
+ else
+ expectDirectCoercedInteger(FI->getArgInfo(0).Info, 128, llvm::Align(16));
+ }
+}
+
+TEST_F(AArch64TargetInfoTest, ClassifyArgumentPointerAggregates) {
+ const ABIType *Ptr1 =
+ makeRecord({FieldInfo(Ptr, 0)}, 64, llvm::Align(8), llvm::Align(8));
+ const ABIType *Reference =
+ TB.getPointerType(64, llvm::Align(8), /*Addrspace=*/0,
+ llvm::abi::PointerFlags::IsPointerOrReference);
+ const ABIType *ReferenceRecord =
+ makeRecord({FieldInfo(Reference, 0)}, 64, llvm::Align(8), llvm::Align(8));
+ const ABIType *Ptr2 = makeRecord({FieldInfo(Ptr, 0), FieldInfo(Ptr, 64)}, 128,
+ llvm::Align(8), llvm::Align(8));
+ const ABIType *PtrArray =
+ makeRecord({FieldInfo(TB.getArrayType(Ptr, 2, /*SizeInBits=*/128), 0)},
+ 128, llvm::Align(8), llvm::Align(8));
+ const ABIType *NestedPtrs =
+ makeRecord({FieldInfo(Ptr1, 0), FieldInfo(Ptr, 64)}, 128, llvm::Align(8),
+ llvm::Align(8));
+ const ABIType *PointerBase =
+ makeRecord({FieldInfo(Ptr, 0)}, 64, llvm::Align(8), llvm::Align(8),
+ passableRecordFlags(/*IsCXX=*/true));
+ const ABIType *DerivedPtrs =
+ makeRecord({FieldInfo(Ptr, 64)}, 128, llvm::Align(8), llvm::Align(8),
+ passableRecordFlags(/*IsCXX=*/true),
+ /*Bases=*/{FieldInfo(PointerBase, 0)});
+ const ABIType *Mixed = makeRecord({FieldInfo(Ptr, 0), FieldInfo(I64, 64)},
+ 128, llvm::Align(8), llvm::Align(8));
+ const ABIType *NonzeroTargetASPtr =
+ TB.getPointerType(64, llvm::Align(8), /*Addrspace=*/1,
+ llvm::abi::PointerFlags::IsPointerOrReference);
+ const ABIType *NonzeroTargetAS = makeRecord(
+ {FieldInfo(NonzeroTargetASPtr, 0)}, 64, llvm::Align(8), llvm::Align(8));
+ const ABIType *AS1Ptr = TB.getPointerType(
+ 64, llvm::Align(8), /*Addrspace=*/1,
+ llvm::abi::PointerFlags::IsPointerOrReference |
+ llvm::abi::PointerFlags::IsPointeeAddressSpaceQualified);
+ const ABIType *NonDefaultAS =
+ makeRecord({FieldInfo(AS1Ptr, 0)}, 64, llvm::Align(8), llvm::Align(8));
+ const ABIType *ExplicitAS0Ptr = TB.getPointerType(
+ 64, llvm::Align(8), /*Addrspace=*/0,
+ llvm::abi::PointerFlags::IsPointerOrReference |
+ llvm::abi::PointerFlags::IsPointeeAddressSpaceQualified);
+ const ABIType *ExplicitAS0 = makeRecord({FieldInfo(ExplicitAS0Ptr, 0)}, 64,
+ llvm::Align(8), llvm::Align(8));
+ const ABIType *OtherPointerLike = TB.getPointerType(
+ 64, llvm::Align(8), /*Addrspace=*/0, llvm::abi::PointerFlags::None);
+ const ABIType *OtherPointerLikeRecord = makeRecord(
+ {FieldInfo(OtherPointerLike, 0)}, 64, llvm::Align(8), llvm::Align(8));
+
+ for (AArch64ABIKind Kind :
+ {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, AArch64ABIKind::Win64,
+ AArch64ABIKind::AAPCSSoft}) {
+ std::unique_ptr<TargetInfo> TI =
+ createAArch64TargetInfo(TB, AArch64ABIOptions(Kind));
+
+ auto Classify = [&](const ABIType *Ty) {
+ std::unique_ptr<FunctionInfo> FI =
+ FunctionInfo::create(llvm::CallingConv::C, Void, {Ty});
+ TI->computeInfo(*FI);
+ return FI->getArgInfo(0).Info;
+ };
+
+ expectDirectCoercedPointer(Classify(Ptr1));
+ expectDirectCoercedPointer(Classify(ReferenceRecord));
+ expectDirectCoercedPointerArray(Classify(Ptr2), 2);
+ expectDirectCoercedPointerArray(Classify(PtrArray), 2);
+ expectDirectCoercedPointerArray(Classify(NestedPtrs), 2);
+ expectDirectCoercedPointerArray(Classify(DerivedPtrs), 2);
+ expectDirectCoercedPointer(Classify(NonzeroTargetAS));
+ expectDirectCoercedIntegerArray(Classify(Mixed), 64, 2, llvm::Align(8));
+ expectDirectCoercedInteger(Classify(NonDefaultAS), 64, llvm::Align(8));
+ expectDirectCoercedInteger(Classify(ExplicitAS0), 64, llvm::Align(8));
+ expectDirectCoercedInteger(Classify(OtherPointerLikeRecord), 64,
+ llvm::Align(8));
+ }
+}
+
// Homogeneous floating-point aggregates of at most four members are returned
// directly under AAPCS, DarwinPCS, and Win64.
TEST_F(AArch64TargetInfoTest, ClassifyReturnHFADirect) {
diff --git a/llvm/unittests/ABI/AMDGPUTargetInfoTest.cpp b/llvm/unittests/ABI/AMDGPUTargetInfoTest.cpp
index ecb298b69215e5..6d0e466c41d19a 100644
--- a/llvm/unittests/ABI/AMDGPUTargetInfoTest.cpp
+++ b/llvm/unittests/ABI/AMDGPUTargetInfoTest.cpp
@@ -469,7 +469,8 @@ TEST_F(AMDGPUTargetInfoTest, KernelGenericPointerCoercesToGlobalUnderHIP) {
std::unique_ptr<FunctionInfo> FI;
std::unique_ptr<TargetInfo> TI = hipTarget();
const ABIType *GenericPtr =
- TB.getPointerType(64, llvm::Align(8), llvm::AMDGPUAS::FLAT_ADDRESS);
+ TB.getPointerType(64, llvm::Align(8), llvm::AMDGPUAS::FLAT_ADDRESS,
+ llvm::abi::PointerFlags::IsPointerOrReference);
const ArgInfo &Info = classifyKernelArg(GenericPtr, FI, TI);
expectDirectPointerAS(Info, llvm::AMDGPUAS::GLOBAL_ADDRESS);
EXPECT_FALSE(Info.getCanBeFlattened());
@@ -480,7 +481,8 @@ TEST_F(AMDGPUTargetInfoTest, KernelGlobalPointerUnchangedUnderHIP) {
std::unique_ptr<FunctionInfo> FI;
std::unique_ptr<TargetInfo> TI = hipTarget();
const ABIType *GlobalPtr =
- TB.getPointerType(64, llvm::Align(8), llvm::AMDGPUAS::GLOBAL_ADDRESS);
+ TB.getPointerType(64, llvm::Align(8), llvm::AMDGPUAS::GLOBAL_ADDRESS,
+ llvm::abi::PointerFlags::IsPointerOrReference);
expectDirectPointerAS(classifyKernelArg(GlobalPtr, FI, TI),
llvm::AMDGPUAS::GLOBAL_ADDRESS);
}
@@ -490,7 +492,8 @@ TEST_F(AMDGPUTargetInfoTest, KernelGenericPointerUnchangedByDefault) {
std::unique_ptr<FunctionInfo> FI;
std::unique_ptr<TargetInfo> TI;
const ABIType *GenericPtr =
- TB.getPointerType(64, llvm::Align(8), llvm::AMDGPUAS::FLAT_ADDRESS);
+ TB.getPointerType(64, llvm::Align(8), llvm::AMDGPUAS::FLAT_ADDRESS,
+ llvm::abi::PointerFlags::IsPointerOrReference);
expectDirectPointerAS(
classifyArg(GenericPtr, FI, TI, CallingConv::AMDGPU_KERNEL),
llvm::AMDGPUAS::FLAT_ADDRESS);
diff --git a/llvm/unittests/ABI/TypesTest.cpp b/llvm/unittests/ABI/TypesTest.cpp
index 93fbde360330cd..ead185a5b6083f 100644
--- a/llvm/unittests/ABI/TypesTest.cpp
+++ b/llvm/unittests/ABI/TypesTest.cpp
@@ -60,6 +60,26 @@ TEST_F(ABITypesTest, AtomicTypeProperties) {
EXPECT_FALSE(Atomic->isEmptyRecord());
}
+TEST_F(ABITypesTest, PointerSourceFlagsAreExplicit) {
+ const auto *NonzeroTargetAS =
+ TB.getPointerType(64, Align(8), /*Addrspace=*/1,
+ llvm::abi::PointerFlags::IsPointerOrReference);
+ EXPECT_TRUE(NonzeroTargetAS->isPointerOrReference());
+ EXPECT_FALSE(NonzeroTargetAS->isPointeeAddressSpaceQualified());
+
+ const auto *ExplicitSourceAS0 = TB.getPointerType(
+ 64, Align(8), /*Addrspace=*/0,
+ llvm::abi::PointerFlags::IsPointerOrReference |
+ llvm::abi::PointerFlags::IsPointeeAddressSpaceQualified);
+ EXPECT_TRUE(ExplicitSourceAS0->isPointerOrReference());
+ EXPECT_TRUE(ExplicitSourceAS0->isPointeeAddressSpaceQualified());
+
+ const auto *OtherPointerRepresentation = TB.getPointerType(
+ 64, Align(8), /*Addrspace=*/0, llvm::abi::PointerFlags::None);
+ EXPECT_FALSE(OtherPointerRepresentation->isPointerOrReference());
+ EXPECT_FALSE(OtherPointerRepresentation->isPointeeAddressSpaceQualified());
+}
+
TEST_F(ABITypesTest, EmptyCRecord) {
const RecordType *Empty = makeRecord({}, 0, RecordFlags::CanPassInRegisters);
EXPECT_TRUE(Empty->isEmpty());
@@ -128,7 +148,9 @@ TEST_F(ABITypesTest, DirectVirtualBasesAndVTablePointer) {
const RecordType *IntField = makeRecord(
{FieldInfo(TB.getIntegerType(32, Align(4), /*Signed=*/true), 0)}, 32,
CXXFlags, /*Bases=*/{}, /*VBases=*/{}, Align(4));
- const llvm::abi::Type *VPtr = TB.getPointerType(64, Align(8));
+ const llvm::abi::Type *VPtr =
+ TB.getPointerType(64, Align(8), /*Addrspace=*/0,
+ llvm::abi::PointerFlags::IsPointerOrReference);
FieldInfo VTable(VPtr, 0);
// Empty vbase with vtable
diff --git a/llvm/unittests/ABI/X86TargetInfoTest.cpp b/llvm/unittests/ABI/X86TargetInfoTest.cpp
index 021add1d8ac26f..171db4ed69fa6e 100644
--- a/llvm/unittests/ABI/X86TargetInfoTest.cpp
+++ b/llvm/unittests/ABI/X86TargetInfoTest.cpp
@@ -328,7 +328,9 @@ TEST_F(X86TargetInfoTest, UnionTailPaddingSizesHighHalfFromUnion) {
const ABIType *U32 = TB.getIntegerType(32, llvm::Align(4), /*Signed=*/false);
const ABIType *Words = TB.getArrayType(U32, /*NumElements=*/3,
/*SizeInBits=*/96);
- const ABIType *Ptr = TB.getPointerType(64, llvm::Align(8));
+ const ABIType *Ptr =
+ TB.getPointerType(64, llvm::Align(8), /*Addrspace=*/0,
+ llvm::abi::PointerFlags::IsPointerOrReference);
const ABIType *U =
unionOf({FieldInfo(Words), FieldInfo(Ptr)}, 128, llvm::Align(8));
llvm::ArrayRef<FieldInfo> Pair = directPair(classifyArg(U, FI, TI));
@@ -344,7 +346,9 @@ TEST_F(X86TargetInfoTest, UnionTailPaddingNarrowsHighHalfToByte) {
std::unique_ptr<TargetInfo> TI;
const ABIType *Bytes = TB.getArrayType(I8, /*NumElements=*/9,
/*SizeInBits=*/72);
- const ABIType *Ptr = TB.getPointerType(64, llvm::Align(8));
+ const ABIType *Ptr =
+ TB.getPointerType(64, llvm::Align(8), /*Addrspace=*/0,
+ llvm::abi::PointerFlags::IsPointerOrReference);
const ABIType *U =
unionOf({FieldInfo(Bytes), FieldInfo(Ptr)}, 128, llvm::Align(8));
llvm::ArrayRef<FieldInfo> Pair = directPair(classifyArg(U, FI, TI));
@@ -360,7 +364,9 @@ TEST_F(X86TargetInfoTest, UnionTailPaddingKeepsHighHalfPastOneByte) {
std::unique_ptr<TargetInfo> TI;
const ABIType *Bytes = TB.getArrayType(I8, /*NumElements=*/10,
/*SizeInBits=*/80);
- const ABIType *Ptr = TB.getPointerType(64, llvm::Align(8));
+ const ABIType *Ptr =
+ TB.getPointerType(64, llvm::Align(8), /*Addrspace=*/0,
+ llvm::abi::PointerFlags::IsPointerOrReference);
const ABIType *U =
unionOf({FieldInfo(Bytes), FieldInfo(Ptr)}, 128, llvm::Align(8));
llvm::ArrayRef<FieldInfo> Pair = directPair(classifyArg(U, FI, TI));
diff --git a/mlir/lib/ABI/ABITypeMapper.cpp b/mlir/lib/ABI/ABITypeMapper.cpp
index fe63df659302a3..2abe8734b2c1bb 100644
--- a/mlir/lib/ABI/ABITypeMapper.cpp
+++ b/mlir/lib/ABI/ABITypeMapper.cpp
@@ -95,7 +95,8 @@ const llvm::abi::Type *ABITypeMapper::mapMemRefType(mlir::MemRefType type) {
if (auto intAttr = dyn_cast<IntegerAttr>(as))
addrSpace = intAttr.getInt();
return builder.getPointerType(sizeInBits.getFixedValue(),
- llvm::Align(abiAlign), addrSpace);
+ llvm::Align(abiAlign), addrSpace,
+ llvm::abi::PointerFlags::None);
}
const llvm::abi::Type *ABITypeMapper::mapNoneType(mlir::NoneType type) {
More information about the Mlir-commits
mailing list