Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[AArch64] Change the coercion type of structs with pointer members. #135064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
Loading
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions 28 clang/lib/CodeGen/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,37 @@ 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.
auto ContainsOnlyPointers = [&](const auto &Self, QualType Ty) {
if (isEmptyRecord(getContext(), Ty, true))
davemgreen marked this conversation as resolved.
Show resolved Hide resolved
return false;
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())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little uncomfortable with making a clone of isHomogeneousAggregate. I don't see any issues with this right now, but the invariants here are pretty subtle, and even if I'm not missing anything right now, something could change.

Not sure what a better solution looks like, though; the current patch might be good enough.


Refactoring isHomogeneousAggregate itself to be reusable might work, although it's sort of complicated, and it has limitations.

The x86 backend has similar code to do type inference. But it's currently known to be slightly broken; see #76017.

Maybe we want some sort of generic "lowering" function that takes a record, and just marks each as either padding, or a suitable primitive type; then targets could process that? Seems a little complicated to design.

Copy link
Collaborator Author

@davemgreen davemgreen May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really an expert in clang but I was hoping this would be simpler. I've tried to rewrite the check to be more clear that it is just replacing i64 with ptr where we know the structure contains only pointers. AFAIU even if we get it 'wrong' the ABI remains the same (weirdness around ptrtoint/inttoptr aside, which are already present). But the idea is this is limited to ptrs and it should be relatively safe.

if (!Self(Self, I.getType()))
return false;
}
return all_of(RD->fields(), [&](FieldDecl *FD) {
QualType FDTy = FD->getType();
if (FDTy->isArrayType())
FDTy = getContext().getBaseElementType(FDTy);
return (FDTy->isPointerOrReferenceType() &&
getContext().getTypeSize(FDTy) == 64) ||
Self(Self, FDTy);
});
};

// 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);
if ((Size == 64 || Size == 128) && Alignment == 64 &&
ContainsOnlyPointers(ContainsOnlyPointers, Ty))
BaseTy = llvm::PointerType::getUnqual(getVMContext());
return ABIArgInfo::getDirect(
Size == Alignment ? BaseTy
: llvm::ArrayType::get(BaseTy, Size / Alignment));
Expand Down
50 changes: 24 additions & 26 deletions 50 clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ struct Sp {
int *x;
};
// CHECK-A64-LABEL: define dso_local void @_Z2Tp2Sp(
// CHECK-A64-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] {
// CHECK-A64-SAME: ptr [[S_COERCE:%.*]]) #[[ATTR0]] {
// 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: [[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: 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
Expand All @@ -58,10 +57,10 @@ struct Spp {
int *x, *y;
};
// CHECK-A64-LABEL: define dso_local void @_Z3Tpp3Spp(
// 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_SPP:%.*]], 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: [[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
Expand Down Expand Up @@ -135,10 +134,10 @@ struct Srp {
int &x, *y;
};
// CHECK-A64-LABEL: define dso_local void @_Z3Trp3Srp(
// 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_SRP:%.*]], 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: [[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
Expand All @@ -160,10 +159,10 @@ struct __attribute__((__packed__)) Spp_packed {
int *x, *y;
};
// CHECK-A64-LABEL: define dso_local void @_Z10Tpp_packed10Spp_packed(
// 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_SPP_PACKED:%.*]], align 1
// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], 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
Expand All @@ -185,11 +184,11 @@ struct __attribute__((__packed__)) Spp_superpacked {
Spp_packed x;
};
// CHECK-A64-LABEL: define dso_local void @_Z15Tpp_superpacked15Spp_superpacked(
// 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_SPP_SUPERPACKED:%.*]], align 1
// CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_SUPERPACKED]], ptr [[S]], i32 0, i32 0
// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[COERCE_DIVE]], align 1
// CHECK-A64-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[COERCE_DIVE]], align 1
// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_SUPERPACKED]], ptr [[S]], i32 0, i32 0
// CHECK-A64-NEXT: [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_PACKED:%.*]], ptr [[X]], i32 0, i32 0
// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X1]], align 1
Expand All @@ -215,12 +214,11 @@ union Upp {
long long *y;
};
// CHECK-A64-LABEL: define dso_local void @_Z11Tupp_packed3Upp(
// CHECK-A64-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] {
// 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: [[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: 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
Expand Down Expand Up @@ -326,10 +324,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
Expand All @@ -353,11 +351,11 @@ struct SSpp {
Spp a;
};
// CHECK-A64-LABEL: define dso_local void @_Z4TSpp4SSpp(
// 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_SSPP:%.*]], align 8
// CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SSPP]], 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: [[A:%.*]] = getelementptr inbounds nuw [[STRUCT_SSPP]], ptr [[S]], i32 0, i32 0
// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP:%.*]], ptr [[A]], i32 0, i32 0
// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 8
Expand All @@ -382,10 +380,10 @@ struct SSp : public Sp {
int* b;
};
// CHECK-A64-LABEL: define dso_local void @_Z3TSp3SSp(
// 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_SSP:%.*]], 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: [[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
Expand Down Expand Up @@ -433,11 +431,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
Expand All @@ -463,11 +461,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
Expand Down Expand Up @@ -523,10 +521,10 @@ struct __attribute__((aligned(16))) Spp_align16 {
int *x, *y;
};
// CHECK-A64-LABEL: define dso_local void @_Z11Tpp_align1611Spp_align16(
// 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_SPP_ALIGN16:%.*]], align 16
// CHECK-A64-NEXT: store [2 x i64] [[S_COERCE]], ptr [[S]], align 16
// CHECK-A64-NEXT: store [2 x ptr] [[S_COERCE]], ptr [[S]], align 16
// CHECK-A64-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_SPP_ALIGN16]], ptr [[S]], i32 0, i32 0
// CHECK-A64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X]], align 16
// CHECK-A64-NEXT: store i32 1, ptr [[TMP0]], align 4
Expand Down
2 changes: 1 addition & 1 deletion 2 clang/test/CodeGen/ptrauth-in-c-struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ void test_copy_constructor_SI(SI *s) {
SI t = *s;
}

// CHECK: define void @test_parameter_SI(i64 %{{.*}})
// CHECK: define void @test_parameter_SI(ptr %{{.*}})
// CHECK-NOT: call
// CHECK: ret void

Expand Down
2 changes: 1 addition & 1 deletion 2 clang/test/CodeGenCXX/ptrauth-qualifier-struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void testMoveAssignment(SA a) {
t = static_cast<SA &&>(a);
}

// CHECK: define {{.*}}void @_Z19testCopyConstructor2SI(i
// CHECK: define {{.*}}void @_Z19testCopyConstructor2SI(
// CHECK: call void @llvm.memcpy.p0.p0.i64(

void testCopyConstructor(SI a) {
Expand Down
13 changes: 5 additions & 8 deletions 13 clang/test/CodeGenCXX/trivial_abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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: }
Expand Down Expand Up @@ -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: }
Expand All @@ -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: }

Expand Down Expand Up @@ -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 }
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.