https://github.com/davemgreen updated https://github.com/llvm/llvm-project/pull/135064
>From 33a204bcc884178971c4327528b1e3b75336914e Mon Sep 17 00:00:00 2001 From: David Green <david.gr...@arm.com> Date: Wed, 9 Apr 2025 11:18:25 +0100 Subject: [PATCH 1/6] [AArch64] Add a test case for the coerced arguments. NFC --- .../AArch64/struct-coerce-using-ptr.cpp | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp diff --git a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp new file mode 100644 index 0000000000000..295a5158f7e97 --- /dev/null +++ b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp @@ -0,0 +1,94 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -triple aarch64-none-elf -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s + +struct Sp { + int *x; +}; +// CHECK-LABEL: define dso_local void @_Z2Tp2Sp( +// CHECK-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SP:%.*]], align 8 +// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[COERCE_VAL_IP:%.*]] = inttoptr i64 [[S_COERCE]] to ptr +// CHECK-NEXT: store ptr [[COERCE_VAL_IP]], ptr [[COERCE_DIVE]], align 8 +// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret void +// +void Tp(Sp s) { *s.x = 1; } + +struct Spp { + int *x, *y; +}; +// CHECK-LABEL: define dso_local void @_Z3Tpp3Spp( +// CHECK-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP:%.*]], align 8 +// CHECK-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret void +// +void Tpp(Spp s) { *s.x = 1; } + +struct Sppp { + int *x, *y, *z; +}; +// CHECK-LABEL: define dso_local void @_Z4Tppp4Sppp( +// CHECK-SAME: ptr noundef [[S:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[S_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[S]], ptr [[S_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPPP:%.*]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret void +// +void Tppp(Sppp s) { *s.x = 1; } + +struct Spi { + int *x, y; +}; +// CHECK-LABEL: define dso_local void @_Z3Tpi3Spi( +// CHECK-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SPI:%.*]], align 8 +// CHECK-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPI]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret void +// +void Tpi(Spi s) { *s.x = 1; } + +struct Srp { + int &x, *y; +}; +// CHECK-LABEL: define dso_local void @_Z3Trp3Srp( +// CHECK-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SRP:%.*]], align 8 +// CHECK-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SRP]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret void +// +void Trp(Srp s) { s.x = 1; } + +struct __attribute__((__packed__)) Spp_packed { + int *x, *y; +}; +// CHECK-LABEL: define dso_local void @_Z10Tpp_packed10Spp_packed( +// CHECK-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP_PACKED:%.*]], align 1 +// CHECK-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 1 +// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_PACKED]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 1 +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret void +// +void Tpp_packed(Spp_packed s) { *s.x = 1; } >From 662912d1559a23f91915e996ff0de3cd10ac99d8 Mon Sep 17 00:00:00 2001 From: David Green <david.gr...@arm.com> Date: Wed, 9 Apr 2025 19:07:04 +0100 Subject: [PATCH 2/6] [AArch64] Change the coercion type of structs with pointer members. The aim here is to avoid a ptrtoint->inttoptr round-trip throught the function argument whilst keeping the calling convention the same. Given a struct which is <= 128bits in size, which can only contain either 1 or 2 pointers, we convert to a ptr or [2 x ptr] as opposed to the old coercion that uses i64 or [2 x i64]. --- clang/lib/CodeGen/Targets/AArch64.cpp | 18 ++++++++++++++++++ .../AArch64/struct-coerce-using-ptr.cpp | 17 ++++++++--------- clang/test/CodeGenCXX/trivial_abi.cpp | 13 +++++-------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp index 073ca3cc82690..9dc5f824254eb 100644 --- a/clang/lib/CodeGen/Targets/AArch64.cpp +++ b/clang/lib/CodeGen/Targets/AArch64.cpp @@ -485,6 +485,24 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty, bool IsVariadicFn, } Size = llvm::alignTo(Size, Alignment); + // If the Aggregate is made up of pointers, use an array of pointers for the + // coerced type. This prevents having to convert ptr2int->int2ptr through + // the call, allowing alias analysis to produce better code. + if (const RecordType *RT = Ty->getAs<RecordType>()) { + if (const RecordDecl *RD = RT->getDecl()) { + if (all_of(RD->fields(), [](FieldDecl *FD) { + return FD->getType()->isPointerOrReferenceType(); + })) { + assert((Size == 64 || Size == 128) && + "Expected a 64 or 128bit struct containing pointers"); + llvm::Type *PtrTy = llvm::PointerType::getUnqual(getVMContext()); + if (Size == 128) + PtrTy = llvm::ArrayType::get(PtrTy, 2); + return ABIArgInfo::getDirect(PtrTy); + } + } + } + // We use a pair of i64 for 16-byte aggregate with 8-byte alignment. // For aggregates with 16-byte alignment, we use i128. llvm::Type *BaseTy = llvm::Type::getIntNTy(getVMContext(), Alignment); diff --git a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp index 295a5158f7e97..c2d68ae7ef6cd 100644 --- a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp +++ b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp @@ -5,12 +5,11 @@ struct Sp { int *x; }; // CHECK-LABEL: define dso_local void @_Z2Tp2Sp( -// CHECK-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SP:%.*]], align 8 // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 -// CHECK-NEXT: [[COERCE_VAL_IP:%.*]] = inttoptr i64 [[S_COERCE]] to ptr -// CHECK-NEXT: store ptr [[COERCE_VAL_IP]], ptr [[COERCE_DIVE]], align 8 +// CHECK-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 // CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 // CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 @@ -22,10 +21,10 @@ struct Spp { int *x, *y; }; // CHECK-LABEL: define dso_local void @_Z3Tpp3Spp( -// CHECK-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP:%.*]], align 8 -// CHECK-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 8 // CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP]], ptr [[S]], i32 0, i32 0 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 // CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 @@ -67,10 +66,10 @@ struct Srp { int &x, *y; }; // CHECK-LABEL: define dso_local void @_Z3Trp3Srp( -// CHECK-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SRP:%.*]], align 8 -// CHECK-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 8 // CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SRP]], ptr [[S]], i32 0, i32 0 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 // CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 @@ -82,10 +81,10 @@ struct __attribute__((__packed__)) Spp_packed { int *x, *y; }; // CHECK-LABEL: define dso_local void @_Z10Tpp_packed10Spp_packed( -// CHECK-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP_PACKED:%.*]], align 1 -// CHECK-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 1 +// CHECK-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 1 // CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_PACKED]], ptr [[S]], i32 0, i32 0 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 1 // CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 diff --git a/clang/test/CodeGenCXX/trivial_abi.cpp b/clang/test/CodeGenCXX/trivial_abi.cpp index 90054dbf37ae3..b8cc0d1cc6528 100644 --- a/clang/test/CodeGenCXX/trivial_abi.cpp +++ b/clang/test/CodeGenCXX/trivial_abi.cpp @@ -68,11 +68,10 @@ struct D0 : B0, B1 { Small D0::m0() { return {}; } -// CHECK: define{{.*}} void @_Z14testParamSmall5Small(i64 %[[A_COERCE:.*]]) +// CHECK: define{{.*}} void @_Z14testParamSmall5Small(ptr %[[A_COERCE:.*]]) // CHECK: %[[A:.*]] = alloca %[[STRUCT_SMALL]], align 8 // CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds nuw %[[STRUCT_SMALL]], ptr %[[A]], i32 0, i32 0 -// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[A_COERCE]] to ptr -// CHECK: store ptr %[[COERCE_VAL_IP]], ptr %[[COERCE_DIVE]], align 8 +// CHECK: store ptr %[[A_COERCE]], ptr %[[COERCE_DIVE]], align 8 // CHECK: %[[CALL:.*]] = call noundef ptr @_ZN5SmallD1Ev(ptr {{[^,]*}} %[[A]]) // CHECK: ret void // CHECK: } @@ -101,8 +100,7 @@ Small testReturnSmall() { // CHECK: %[[CALL1:.*]] = call noundef ptr @_ZN5SmallC1ERKS_(ptr {{[^,]*}} %[[AGG_TMP]], ptr noundef nonnull align 8 dereferenceable(8) %[[T]]) // CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds nuw %[[STRUCT_SMALL]], ptr %[[AGG_TMP]], i32 0, i32 0 // CHECK: %[[V0:.*]] = load ptr, ptr %[[COERCE_DIVE]], align 8 -// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint ptr %[[V0]] to i64 -// CHECK: call void @_Z14testParamSmall5Small(i64 %[[COERCE_VAL_PI]]) +// CHECK: call void @_Z14testParamSmall5Small(ptr %[[V0]]) // CHECK: %[[CALL2:.*]] = call noundef ptr @_ZN5SmallD1Ev(ptr {{[^,]*}} %[[T]]) // CHECK: ret void // CHECK: } @@ -120,8 +118,7 @@ void testCallSmall0() { // CHECK: store ptr %[[COERCE_VAL_IP]], ptr %[[COERCE_DIVE]], align 8 // CHECK: %[[COERCE_DIVE1:.*]] = getelementptr inbounds nuw %[[STRUCT_SMALL]], ptr %[[AGG_TMP]], i32 0, i32 0 // CHECK: %[[V0:.*]] = load ptr, ptr %[[COERCE_DIVE1]], align 8 -// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint ptr %[[V0]] to i64 -// CHECK: call void @_Z14testParamSmall5Small(i64 %[[COERCE_VAL_PI]]) +// CHECK: call void @_Z14testParamSmall5Small(ptr %[[V0]]) // CHECK: ret void // CHECK: } @@ -226,7 +223,7 @@ NonTrivial testReturnHasNonTrivial() { // CHECK: call noundef ptr @_ZN5SmallC1Ev(ptr {{[^,]*}} %[[AGG_TMP]]) // CHECK: invoke noundef ptr @_ZN5SmallC1Ev(ptr {{[^,]*}} %[[AGG_TMP1]]) -// CHECK: call void @_Z20calleeExceptionSmall5SmallS_(i64 %{{.*}}, i64 %{{.*}}) +// CHECK: call void @_Z20calleeExceptionSmall5SmallS_(ptr %{{.*}}, ptr %{{.*}}) // CHECK-NEXT: ret void // CHECK: landingpad { ptr, i32 } >From 4277570fb3374568cae5e1acf321c9751c7c35e7 Mon Sep 17 00:00:00 2001 From: David Green <david.gr...@arm.com> Date: Thu, 10 Apr 2025 16:56:35 +0100 Subject: [PATCH 3/6] Test update --- .../AArch64/struct-coerce-using-ptr.cpp | 461 +++++++++++++++--- 1 file changed, 405 insertions(+), 56 deletions(-) diff --git a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp index c2d68ae7ef6cd..efbf6cb04de95 100644 --- a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp +++ b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp @@ -1,93 +1,442 @@ // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 -// RUN: %clang_cc1 -triple aarch64-none-elf -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-none-elf -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-A64 +// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-A64_32 struct Sp { int *x; }; -// CHECK-LABEL: define dso_local void @_Z2Tp2Sp( -// CHECK-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SP:%.*]], align 8 -// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 -// CHECK-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 -// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 -// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 -// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 -// CHECK-NEXT: ret void +// CHECK-A64-LABEL: define dso_local void @_Z2Tp2Sp( +// CHECK-A64-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SP:%.*]], align 8 +// CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z2Tp2Sp( +// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SP:%.*]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void // void Tp(Sp s) { *s.x = 1; } struct Spp { int *x, *y; }; -// CHECK-LABEL: define dso_local void @_Z3Tpp3Spp( -// CHECK-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP:%.*]], align 8 -// CHECK-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 8 -// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP]], ptr [[S]], i32 0, i32 0 -// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 -// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 -// CHECK-NEXT: ret void +// CHECK-A64-LABEL: define dso_local void @_Z3Tpp3Spp( +// CHECK-A64-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP:%.*]], align 8 +// CHECK-A64-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z3Tpp3Spp( +// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP:%.*]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void // void Tpp(Spp s) { *s.x = 1; } struct Sppp { int *x, *y, *z; }; -// CHECK-LABEL: define dso_local void @_Z4Tppp4Sppp( -// CHECK-SAME: ptr noundef [[S:%.*]]) #[[ATTR0]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[S_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: store ptr [[S]], ptr [[S_INDIRECT_ADDR]], align 8 -// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPPP:%.*]], ptr [[S]], i32 0, i32 0 -// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 -// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 -// CHECK-NEXT: ret void +// CHECK-A64-LABEL: define dso_local void @_Z4Tppp4Sppp( +// CHECK-A64-SAME: ptr noundef [[S:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-A64-NEXT: store ptr [[S]], ptr [[S_INDIRECT_ADDR]], align 8 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPPP:%.*]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z4Tppp4Sppp( +// CHECK-A64_32-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPPP:%.*]], align 4 +// CHECK-A64_32-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPPP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void // void Tppp(Sppp s) { *s.x = 1; } struct Spi { int *x, y; }; -// CHECK-LABEL: define dso_local void @_Z3Tpi3Spi( -// CHECK-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SPI:%.*]], align 8 -// CHECK-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 -// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPI]], ptr [[S]], i32 0, i32 0 -// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 -// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 -// CHECK-NEXT: ret void +// CHECK-A64-LABEL: define dso_local void @_Z3Tpi3Spi( +// CHECK-A64-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SPI:%.*]], align 8 +// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPI]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z3Tpi3Spi( +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPI:%.*]], align 4 +// CHECK-A64_32-NEXT: store i64 [[S_COERCE]], ptr [[S]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPI]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void // void Tpi(Spi s) { *s.x = 1; } struct Srp { int &x, *y; }; -// CHECK-LABEL: define dso_local void @_Z3Trp3Srp( -// CHECK-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SRP:%.*]], align 8 -// CHECK-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 8 -// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SRP]], ptr [[S]], i32 0, i32 0 -// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 -// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 -// CHECK-NEXT: ret void +// CHECK-A64-LABEL: define dso_local void @_Z3Trp3Srp( +// CHECK-A64-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SRP:%.*]], align 8 +// CHECK-A64-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SRP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z3Trp3Srp( +// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SRP:%.*]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SRP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SRP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void // void Trp(Srp s) { s.x = 1; } struct __attribute__((__packed__)) Spp_packed { int *x, *y; }; -// CHECK-LABEL: define dso_local void @_Z10Tpp_packed10Spp_packed( -// CHECK-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP_PACKED:%.*]], align 1 -// CHECK-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 1 -// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_PACKED]], ptr [[S]], i32 0, i32 0 -// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 1 -// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4 -// CHECK-NEXT: ret void +// CHECK-A64-LABEL: define dso_local void @_Z10Tpp_packed10Spp_packed( +// CHECK-A64-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP_PACKED:%.*]], align 1 +// CHECK-A64-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 1 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_PACKED]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 1 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z10Tpp_packed10Spp_packed( +// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP_PACKED:%.*]], align 1 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_PACKED]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 1 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_PACKED]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 1 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void // void Tpp_packed(Spp_packed s) { *s.x = 1; } + +union Upp { + int *x; + long long *y; +}; +// CHECK-A64-LABEL: define dso_local void @_Z11Tupp_packed3Upp( +// CHECK-A64-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[UNION_UPP:%.*]], align 8 +// CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[UNION_UPP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[S]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z11Tupp_packed3Upp( +// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[UNION_UPP:%.*]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[UNION_UPP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[S]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void +// +void Tupp_packed(Upp s) { *s.x = 1; } + +union USpp { + Spp s; + long long y; +}; +// CHECK-A64-LABEL: define dso_local void @_Z12TUSpp_packed4USpp( +// CHECK-A64-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[UNION_USPP:%.*]], align 8 +// CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[UNION_USPP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP:%.*]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z12TUSpp_packed4USpp( +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[UNION_USPP:%.*]], align 8 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[UNION_USPP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store i64 [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP:%.*]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void +// +void TUSpp_packed(USpp s) { *s.s.x = 1; } + +struct Spf { + int *x; + int z[]; +}; +// CHECK-A64-LABEL: define dso_local void @_Z3Tpf3Spf( +// CHECK-A64-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SPF:%.*]], align 8 +// CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPF]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[COERCE_VAL_IP:%.*]] = inttoptr i64 [[S_COERCE]] to ptr +// CHECK-A64-NEXT: store ptr [[COERCE_VAL_IP]], ptr [[COERCE_DIVE]], align 8 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPF]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z3Tpf3Spf( +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPF:%.*]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPF]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[COERCE_VAL_II:%.*]] = trunc i64 [[S_COERCE]] to i32 +// CHECK-A64_32-NEXT: store i32 [[COERCE_VAL_II]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPF]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void +// +void Tpf(Spf s) { *s.x = 1; } + +struct Sppf { + int *x, *y; + int z[]; +}; +// CHECK-A64-LABEL: define dso_local void @_Z4Tppf4Sppf( +// CHECK-A64-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SPPF:%.*]], align 8 +// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPPF]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z4Tppf4Sppf( +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPPF:%.*]], align 4 +// CHECK-A64_32-NEXT: store i64 [[S_COERCE]], ptr [[S]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPPF]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void +// +void Tppf(Sppf s) { *s.x = 1; } + +struct SSpSp { + struct Sp a, b; +}; +// CHECK-A64-LABEL: define dso_local void @_Z5TSpSp5SSpSp( +// CHECK-A64-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SSPSP:%.*]], align 8 +// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-A64-NEXT: [[A:%.*]] = getelementptr inbounds nuw [[STRUCT_SSPSP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP:%.*]], ptr [[A]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z5TSpSp5SSpSp( +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SSPSP:%.*]], align 4 +// CHECK-A64_32-NEXT: store i64 [[S_COERCE]], ptr [[S]], align 4 +// CHECK-A64_32-NEXT: [[A:%.*]] = getelementptr inbounds nuw [[STRUCT_SSPSP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP:%.*]], ptr [[A]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void +// +void TSpSp(SSpSp s) { *s.a.x = 1; } + +struct SSp : public Sp { + int* b; +}; +// CHECK-A64-LABEL: define dso_local void @_Z3TSp3SSp( +// CHECK-A64-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SSP:%.*]], align 8 +// CHECK-A64-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP:%.*]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z3TSp3SSp( +// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SSP:%.*]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SSP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[COERCE_DIVE1:%.*]] = getelementptr inbounds nuw [[STRUCT_SP:%.*]], ptr [[COERCE_DIVE]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE1]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void +// +void TSp(SSp s) { *s.x = 1; } + +struct Si { + int x; +}; +struct SSpi : public Si { + int* y; +}; +// CHECK-A64-LABEL: define dso_local void @_Z4TSpi4SSpi( +// CHECK-A64-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SSPI:%.*]], align 8 +// CHECK-A64-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SI:%.*]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: store i32 1, ptr [[X]], align 8 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z4TSpi4SSpi( +// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SSPI:%.*]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SSPI]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[COERCE_DIVE1:%.*]] = getelementptr inbounds nuw [[STRUCT_SI:%.*]], ptr [[COERCE_DIVE]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE1]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SI]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store i32 1, ptr [[X]], align 4 +// CHECK-A64_32-NEXT: ret void +// +void TSpi(SSpi s) { s.x = 1; } + +struct Spa { + int* xs[1]; +}; +// CHECK-A64-LABEL: define dso_local void @_Z3Tpa3Spa( +// CHECK-A64-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SPA:%.*]], align 8 +// CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: store i64 [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 +// CHECK-A64-NEXT: [[XS:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [1 x ptr], ptr [[XS]], i64 0, i64 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z3Tpa3Spa( +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPA:%.*]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[COERCE_VAL_II:%.*]] = trunc i64 [[S_COERCE]] to i32 +// CHECK-A64_32-NEXT: store i32 [[COERCE_VAL_II]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: [[XS:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [1 x ptr], ptr [[XS]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARRAYIDX]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void +// +void Tpa(Spa s) { *s.xs[0] = 1; } + +struct Spa2 { + int* xs[2]; +}; +// CHECK-A64-LABEL: define dso_local void @_Z4Tpa24Spa2( +// CHECK-A64-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SPA2:%.*]], align 8 +// CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA2]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 +// CHECK-A64-NEXT: [[XS:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA2]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x ptr], ptr [[XS]], i64 0, i64 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z4Tpa24Spa2( +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPA2:%.*]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA2]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store i64 [[S_COERCE]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: [[XS:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA2]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x ptr], ptr [[XS]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARRAYIDX]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void +// +void Tpa2(Spa2 s) { *s.xs[0] = 1; } + +struct Spa3 { + int* xs[3]; +}; +// CHECK-A64-LABEL: define dso_local void @_Z4Tpa34Spa3( +// CHECK-A64-SAME: ptr noundef [[S:%.*]]) #[[ATTR0]] { +// CHECK-A64-NEXT: [[ENTRY:.*:]] +// CHECK-A64-NEXT: [[S_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-A64-NEXT: store ptr [[S]], ptr [[S_INDIRECT_ADDR]], align 8 +// CHECK-A64-NEXT: [[XS:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA3:%.*]], ptr [[S]], i32 0, i32 0 +// CHECK-A64-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [3 x ptr], ptr [[XS]], i64 0, i64 0 +// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8 +// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64-NEXT: ret void +// +// CHECK-A64_32-LABEL: define void @_Z4Tpa34Spa3( +// CHECK-A64_32-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-NEXT: [[ENTRY:.*:]] +// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPA3:%.*]], align 4 +// CHECK-A64_32-NEXT: [[TMP_COERCE:%.*]] = alloca [2 x i64], align 8 +// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA3]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store [2 x i64] [[S_COERCE]], ptr [[TMP_COERCE]], align 8 +// CHECK-A64_32-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[COERCE_DIVE]], ptr align 8 [[TMP_COERCE]], i32 12, i1 false) +// CHECK-A64_32-NEXT: [[XS:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA3]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [3 x ptr], ptr [[XS]], i32 0, i32 0 +// CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARRAYIDX]], align 4 +// CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 +// CHECK-A64_32-NEXT: ret void +// +void Tpa3(Spa3 s) { *s.xs[0] = 1; } + +//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +// CHECK: {{.*}} >From 6d0fa9f764219f759bb9fb882cdbad75dc0748e3 Mon Sep 17 00:00:00 2001 From: David Green <david.gr...@arm.com> Date: Thu, 10 Apr 2025 17:20:18 +0100 Subject: [PATCH 4/6] Update to recursive check fields, check bases, exclude aarch64_32, more tests. --- clang/lib/CodeGen/Targets/AArch64.cpp | 37 +++++++---- .../AArch64/struct-coerce-using-ptr.cpp | 63 +++++++++---------- 2 files changed, 55 insertions(+), 45 deletions(-) diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp index 9dc5f824254eb..61ac69c39c89b 100644 --- a/clang/lib/CodeGen/Targets/AArch64.cpp +++ b/clang/lib/CodeGen/Targets/AArch64.cpp @@ -488,19 +488,32 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty, bool IsVariadicFn, // If the Aggregate is made up of pointers, use an array of pointers for the // coerced type. This prevents having to convert ptr2int->int2ptr through // the call, allowing alias analysis to produce better code. - if (const RecordType *RT = Ty->getAs<RecordType>()) { - if (const RecordDecl *RD = RT->getDecl()) { - if (all_of(RD->fields(), [](FieldDecl *FD) { - return FD->getType()->isPointerOrReferenceType(); - })) { - assert((Size == 64 || Size == 128) && - "Expected a 64 or 128bit struct containing pointers"); - llvm::Type *PtrTy = llvm::PointerType::getUnqual(getVMContext()); - if (Size == 128) - PtrTy = llvm::ArrayType::get(PtrTy, 2); - return ABIArgInfo::getDirect(PtrTy); - } + std::function<bool(QualType Ty)> ContainsOnlyPointers = [&](QualType Ty) { + const RecordType *RT = Ty->getAs<RecordType>(); + if (!RT) + return false; + const RecordDecl *RD = RT->getDecl(); + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { + for (const auto &I : CXXRD->bases()) + if (!ContainsOnlyPointers(I.getType())) + return false; } + return all_of(RD->fields(), [&](FieldDecl *FD) { + QualType FDTy = FD->getType(); + return FDTy->isPointerOrReferenceType() || + (FDTy->isArrayType() && + FDTy->getBaseElementTypeUnsafe()->isPointerOrReferenceType()) || + ContainsOnlyPointers(FDTy); + }); + }; + if (getTarget().getTriple().getArch() != llvm::Triple::aarch64_32 && + ContainsOnlyPointers(Ty)) { + assert((Size == 64 || Size == 128) && + "Expected a 64 or 128bit struct containing pointers"); + llvm::Type *PtrTy = llvm::PointerType::getUnqual(getVMContext()); + if (Size == 128) + PtrTy = llvm::ArrayType::get(PtrTy, 2); + return ABIArgInfo::getDirect(PtrTy); } // We use a pair of i64 for 16-byte aggregate with 8-byte alignment. diff --git a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp index efbf6cb04de95..e8b17a91b3302 100644 --- a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp +++ b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp @@ -17,11 +17,12 @@ struct Sp { // CHECK-A64-NEXT: ret void // // CHECK-A64_32-LABEL: define void @_Z2Tp2Sp( -// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { // CHECK-A64_32-NEXT: [[ENTRY:.*:]] // CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SP:%.*]], align 4 // CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 -// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_VAL_II:%.*]] = trunc i64 [[S_COERCE]] to i32 +// CHECK-A64_32-NEXT: store i32 [[COERCE_VAL_II]], ptr [[COERCE_DIVE]], align 4 // CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 // CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 // CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 @@ -43,11 +44,10 @@ struct Spp { // CHECK-A64-NEXT: ret void // // CHECK-A64_32-LABEL: define void @_Z3Tpp3Spp( -// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64_32-NEXT: [[ENTRY:.*:]] // CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP:%.*]], align 4 -// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP]], ptr [[S]], i32 0, i32 0 -// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: store i64 [[S_COERCE]], ptr [[S]], align 4 // CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP]], ptr [[S]], i32 0, i32 0 // CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 // CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 @@ -69,10 +69,12 @@ struct Sppp { // CHECK-A64-NEXT: ret void // // CHECK-A64_32-LABEL: define void @_Z4Tppp4Sppp( -// CHECK-A64_32-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64_32-NEXT: [[ENTRY:.*:]] // CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPPP:%.*]], align 4 -// CHECK-A64_32-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 4 +// CHECK-A64_32-NEXT: [[TMP_COERCE:%.*]] = alloca [2 x i64], align 8 +// CHECK-A64_32-NEXT: store [2 x i64] [[S_COERCE]], ptr [[TMP_COERCE]], align 8 +// CHECK-A64_32-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[S]], ptr align 8 [[TMP_COERCE]], i32 12, i1 false) // CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPPP]], ptr [[S]], i32 0, i32 0 // CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 // CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 @@ -119,11 +121,10 @@ struct Srp { // CHECK-A64-NEXT: ret void // // CHECK-A64_32-LABEL: define void @_Z3Trp3Srp( -// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64_32-NEXT: [[ENTRY:.*:]] // CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SRP:%.*]], align 4 -// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SRP]], ptr [[S]], i32 0, i32 0 -// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: store i64 [[S_COERCE]], ptr [[S]], align 4 // CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SRP]], ptr [[S]], i32 0, i32 0 // CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 // CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 @@ -145,11 +146,10 @@ struct __attribute__((__packed__)) Spp_packed { // CHECK-A64-NEXT: ret void // // CHECK-A64_32-LABEL: define void @_Z10Tpp_packed10Spp_packed( -// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64_32-NEXT: [[ENTRY:.*:]] // CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SPP_PACKED:%.*]], align 1 -// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_PACKED]], ptr [[S]], i32 0, i32 0 -// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 1 +// CHECK-A64_32-NEXT: store i64 [[S_COERCE]], ptr [[S]], align 1 // CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_PACKED]], ptr [[S]], i32 0, i32 0 // CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 1 // CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 @@ -172,11 +172,12 @@ union Upp { // CHECK-A64-NEXT: ret void // // CHECK-A64_32-LABEL: define void @_Z11Tupp_packed3Upp( -// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64_32-NEXT: [[ENTRY:.*:]] // CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[UNION_UPP:%.*]], align 4 // CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[UNION_UPP]], ptr [[S]], i32 0, i32 0 -// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 4 +// CHECK-A64_32-NEXT: [[COERCE_VAL_II:%.*]] = trunc i64 [[S_COERCE]] to i32 +// CHECK-A64_32-NEXT: store i32 [[COERCE_VAL_II]], ptr [[COERCE_DIVE]], align 4 // CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[S]], align 4 // CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 // CHECK-A64_32-NEXT: ret void @@ -271,10 +272,10 @@ struct SSpSp { struct Sp a, b; }; // CHECK-A64-LABEL: define dso_local void @_Z5TSpSp5SSpSp( -// CHECK-A64-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64-NEXT: [[ENTRY:.*:]] // CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SSPSP:%.*]], align 8 -// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-A64-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 8 // CHECK-A64-NEXT: [[A:%.*]] = getelementptr inbounds nuw [[STRUCT_SSPSP]], ptr [[S]], i32 0, i32 0 // CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP:%.*]], ptr [[A]], i32 0, i32 0 // CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8 @@ -308,13 +309,11 @@ struct SSp : public Sp { // CHECK-A64-NEXT: ret void // // CHECK-A64_32-LABEL: define void @_Z3TSp3SSp( -// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64_32-NEXT: [[ENTRY:.*:]] // CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SSP:%.*]], align 4 -// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SSP]], ptr [[S]], i32 0, i32 0 -// CHECK-A64_32-NEXT: [[COERCE_DIVE1:%.*]] = getelementptr inbounds nuw [[STRUCT_SP:%.*]], ptr [[COERCE_DIVE]], i32 0, i32 0 -// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE1]], align 4 -// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store i64 [[S_COERCE]], ptr [[S]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SP:%.*]], ptr [[S]], i32 0, i32 0 // CHECK-A64_32-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 4 // CHECK-A64_32-NEXT: store i32 1, ptr [[TMP0]], align 4 // CHECK-A64_32-NEXT: ret void @@ -328,22 +327,20 @@ struct SSpi : public Si { int* y; }; // CHECK-A64-LABEL: define dso_local void @_Z4TSpi4SSpi( -// CHECK-A64-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64-NEXT: [[ENTRY:.*:]] // CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SSPI:%.*]], align 8 -// CHECK-A64-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 8 +// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 8 // CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SI:%.*]], ptr [[S]], i32 0, i32 0 // CHECK-A64-NEXT: store i32 1, ptr [[X]], align 8 // CHECK-A64-NEXT: ret void // // CHECK-A64_32-LABEL: define void @_Z4TSpi4SSpi( -// CHECK-A64_32-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64_32-NEXT: [[ENTRY:.*:]] // CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SSPI:%.*]], align 4 -// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SSPI]], ptr [[S]], i32 0, i32 0 -// CHECK-A64_32-NEXT: [[COERCE_DIVE1:%.*]] = getelementptr inbounds nuw [[STRUCT_SI:%.*]], ptr [[COERCE_DIVE]], i32 0, i32 0 -// CHECK-A64_32-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE1]], align 4 -// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SI]], ptr [[S]], i32 0, i32 0 +// CHECK-A64_32-NEXT: store i64 [[S_COERCE]], ptr [[S]], align 4 +// CHECK-A64_32-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SI:%.*]], ptr [[S]], i32 0, i32 0 // CHECK-A64_32-NEXT: store i32 1, ptr [[X]], align 4 // CHECK-A64_32-NEXT: ret void // @@ -353,11 +350,11 @@ struct Spa { int* xs[1]; }; // CHECK-A64-LABEL: define dso_local void @_Z3Tpa3Spa( -// CHECK-A64-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64-NEXT: [[ENTRY:.*:]] // CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SPA:%.*]], align 8 // CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA]], ptr [[S]], i32 0, i32 0 -// CHECK-A64-NEXT: store i64 [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 +// CHECK-A64-NEXT: store ptr [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 // CHECK-A64-NEXT: [[XS:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA]], ptr [[S]], i32 0, i32 0 // CHECK-A64-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [1 x ptr], ptr [[XS]], i64 0, i64 0 // CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8 @@ -383,11 +380,11 @@ struct Spa2 { int* xs[2]; }; // CHECK-A64-LABEL: define dso_local void @_Z4Tpa24Spa2( -// CHECK-A64-SAME: [2 x i64] [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-SAME: [2 x ptr] [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64-NEXT: [[ENTRY:.*:]] // CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SPA2:%.*]], align 8 // CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA2]], ptr [[S]], i32 0, i32 0 -// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 +// CHECK-A64-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[COERCE_DIVE]], align 8 // CHECK-A64-NEXT: [[XS:%.*]] = getelementptr inbounds nuw [[STRUCT_SPA2]], ptr [[S]], i32 0, i32 0 // CHECK-A64-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x ptr], ptr [[XS]], i64 0, i64 0 // CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8 >From 1f84d45d32d4d8838400d1accc87c139aa544f49 Mon Sep 17 00:00:00 2001 From: David Green <david.gr...@arm.com> Date: Thu, 10 Apr 2025 21:13:59 +0100 Subject: [PATCH 5/6] Exclude empty structs, which fixes the arm64-microsoft-arguments.cpp test. --- clang/lib/CodeGen/Targets/AArch64.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp index 61ac69c39c89b..ad20b06b7a383 100644 --- a/clang/lib/CodeGen/Targets/AArch64.cpp +++ b/clang/lib/CodeGen/Targets/AArch64.cpp @@ -489,6 +489,8 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty, bool IsVariadicFn, // coerced type. This prevents having to convert ptr2int->int2ptr through // the call, allowing alias analysis to produce better code. std::function<bool(QualType Ty)> ContainsOnlyPointers = [&](QualType Ty) { + if (isEmptyRecord(getContext(), Ty, true)) + return false; const RecordType *RT = Ty->getAs<RecordType>(); if (!RT) return false; >From eef827553c3be21cc5a3b2bc442b1b00950d407d Mon Sep 17 00:00:00 2001 From: David Green <david.gr...@arm.com> Date: Fri, 11 Apr 2025 08:02:58 +0100 Subject: [PATCH 6/6] Check the TypeSize of the pointer directly. --- clang/lib/CodeGen/Targets/AArch64.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp index ad20b06b7a383..5e4660a29fffa 100644 --- a/clang/lib/CodeGen/Targets/AArch64.cpp +++ b/clang/lib/CodeGen/Targets/AArch64.cpp @@ -502,14 +502,14 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty, bool IsVariadicFn, } return all_of(RD->fields(), [&](FieldDecl *FD) { QualType FDTy = FD->getType(); - return FDTy->isPointerOrReferenceType() || - (FDTy->isArrayType() && - FDTy->getBaseElementTypeUnsafe()->isPointerOrReferenceType()) || + if (FDTy->isArrayType()) + FDTy = QualType(FDTy->getBaseElementTypeUnsafe(), 0); + return (FDTy->isPointerOrReferenceType() && + getContext().getTypeSize(FDTy) == 64) || ContainsOnlyPointers(FDTy); }); }; - if (getTarget().getTriple().getArch() != llvm::Triple::aarch64_32 && - ContainsOnlyPointers(Ty)) { + if (ContainsOnlyPointers(Ty)) { assert((Size == 64 || Size == 128) && "Expected a 64 or 128bit struct containing pointers"); llvm::Type *PtrTy = llvm::PointerType::getUnqual(getVMContext()); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits