https://github.com/xakep8 updated https://github.com/llvm/llvm-project/pull/229202
>From b248e0cd5e29960eb26898f9247e0b6304103da1 Mon Sep 17 00:00:00 2001 From: Kunal Dubey <[email protected]> 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 | 7 +- 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 | 31 ++- 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, 582 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 402cd09afd63700..4d320aa159da97a 100644 --- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp @@ -361,16 +361,19 @@ static const llvm::abi::Type *mapCIRType(mlir::Type type, dyn_cast_if_present<cir::TargetAddressSpaceAttr>( ptrTy.getAddrSpace())) addrSpace = targetAsAttr.getValue(); + // CIR does not preserve source-level pointer-kind information. 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 5044883a49ca2e8..841e0dcdc905589 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 f6bb43482c705ea..c909468fc04bb02 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 000000000000000..feea53e9c9b2f85 --- /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. + +@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 274c6010ea7e472..7950e3320d379ed 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 8e0bbb0e06b5778..fa384187a43eb08 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 6fdd4318bf4f0d3..ad510640387ed73 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; +@interface Object +@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 b7083d181eae88f..405c4f02a4f1c9e 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,32 @@ class PointerLikeType : public Type { } }; +/// Source-level pointer properties, independent of the target 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 +522,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 9573f31e1a4053f..954a4db37ef62d0 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 c6488a1c539fd47..30e189f42701157 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 bbb9a52eb482827..d93de25293c59e2 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 ecb298b69215e55..6d0e466c41d19a1 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 93fbde360330cd8..ead185a5b6083fc 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 021add1d8ac26fb..171db4ed69fa6e7 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 fe63df659302a32..2abe8734b2c1bb9 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) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
