Skip to content
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

[clang][CGRecordLayout] Remove dependency on isZeroSize #96422

Merged
merged 20 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
46d1543
[clang][CGRecordLayout] Remove dependency on isZeroSize
Michael137 Jun 21, 2024
23f298d
fixup! add missing header
Michael137 Jun 23, 2024
a24099d
fixup! remove debug leftovers
Michael137 Jun 23, 2024
a008bae
fixup! fix remaining test failures
Michael137 Jun 24, 2024
7ea1b50
fixup! add helper wrappers for isEmptyField/isEmptyRecord
Michael137 Jun 24, 2024
0ef2c88
fixup! clang-format
Michael137 Jun 24, 2024
5936851
fixup! adjust other parts of CodeGen to use isEmptyFieldForLayout/isE…
Michael137 Jul 1, 2024
3afa138
fixup! decouple isEmptyXXXForLayout from isEmptyXXX
Michael137 Jul 5, 2024
dd38db8
fixup! remove remaining calls to isZeroSize in CGExprConstant and CGC…
Michael137 Jul 9, 2024
2d97ced
fixup! add test for addMemcpyableField in the presence of empty overl…
Michael137 Jul 9, 2024
9041aa7
fixup! update isEmptyRecordForLayout comment
Michael137 Jul 9, 2024
2097deb
fixup! fix anonymous-union-init.c test on Windows
Michael137 Jul 9, 2024
f6693e4
fixup! different layout on msvc for anonymous-union-init.c
Michael137 Jul 10, 2024
2d22773
fixup! don't consider polymorphic classes to be empty for purposes of…
Michael137 Jul 11, 2024
f6c64fe
fixup! adjust back tests after isEmptyRecordForLayout fix
Michael137 Jul 11, 2024
4e5002d
fixup! isDynamicClass instead of isPolymorphic; add test
Michael137 Jul 11, 2024
c7e6468
fixup! add missing test
Michael137 Jul 11, 2024
5312be4
fixup! fix filecheck pattern in new test
Michael137 Jul 12, 2024
02165f5
fixup! fix filecheck pattern in new test
Michael137 Jul 12, 2024
a4b15f0
fixup! add back a test check that was removed in earlier revisions
Michael137 Jul 15, 2024
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
35 changes: 35 additions & 0 deletions clang/lib/CodeGen/ABIInfoImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,41 @@ bool CodeGen::isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays,
return true;
}

bool CodeGen::isEmptyFieldForLayout(const ASTContext &Context,
const FieldDecl *FD) {
if (FD->isZeroLengthBitField(Context))
return true;

if (FD->isUnnamedBitField())
return false;

return isEmptyRecordForLayout(Context, FD->getType());
}

bool CodeGen::isEmptyRecordForLayout(const ASTContext &Context, QualType T) {
const RecordType *RT = T->getAs<RecordType>();
if (!RT)
return false;

const RecordDecl *RD = RT->getDecl();

// If this is a C++ record, check the bases first.
if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
if (CXXRD->isDynamicClass())
return false;

for (const auto &I : CXXRD->bases())
if (!isEmptyRecordForLayout(Context, I.getType()))
return false;
}

for (const auto *I : RD->fields())
if (!isEmptyFieldForLayout(Context, I))
return false;

return true;
}

const Type *CodeGen::isSingleElementStruct(QualType T, ASTContext &Context) {
const RecordType *RT = T->getAs<RecordType>();
if (!RT)
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CodeGen/ABIInfoImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,16 @@ bool isEmptyField(ASTContext &Context, const FieldDecl *FD, bool AllowArrays,
bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays,
bool AsIfNoUniqueAddr = false);

/// isEmptyFieldForLayout - Return true iff the field is "empty", that is,
/// either a zero-width bit-field or an \ref isEmptyRecordForLayout.
bool isEmptyFieldForLayout(const ASTContext &Context, const FieldDecl *FD);

/// isEmptyRecordForLayout - Return true iff a structure contains only empty
Copy link
Collaborator

Choose a reason for hiding this comment

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

Empty base classes and fields.

/// base classes (per \ref isEmptyRecordForLayout) and fields (per
/// \ref isEmptyFieldForLayout). Note, C++ record fields are considered empty
/// if the [[no_unique_address]] attribute would have made them empty.
bool isEmptyRecordForLayout(const ASTContext &Context, QualType T);
Copy link
Contributor

Choose a reason for hiding this comment

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

These functions don't belong here. This file contains helper functions for use in implementations of ABIInfo class.
I guess CGRecordLayout* could be a better place for them.

Copy link
Member Author

Choose a reason for hiding this comment

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

I put those here just to have them next to isEmptyRecord/isEmptyField, which are used outside the implementation of ABIInfo. But agree with your point. These should probably all live in CGRecordLayout instead


/// isSingleElementStruct - Determine if a structure is a "single
/// element struct", i.e. it has exactly one non-empty field or
/// exactly one field which is itself a single element
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/CodeGen/CGClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "ABIInfoImpl.h"
#include "CGBlocks.h"
#include "CGCXXABI.h"
#include "CGDebugInfo.h"
Expand Down Expand Up @@ -933,7 +934,7 @@ namespace {
}

void addMemcpyableField(FieldDecl *F) {
if (F->isZeroSize(CGF.getContext()))
if (isEmptyFieldForLayout(CGF.getContext(), F))
return;
if (!FirstField)
addInitialField(F);
Expand Down Expand Up @@ -1815,7 +1816,7 @@ namespace {
const CXXDestructorDecl *DD)
: Context(Context), EHStack(EHStack), DD(DD), StartIndex(std::nullopt) {}
void PushCleanupForField(const FieldDecl *Field) {
if (Field->isZeroSize(Context))
if (isEmptyFieldForLayout(Context, Field))
return;
unsigned FieldIndex = Field->getFieldIndex();
if (FieldHasTrivialDestructorBody(Context, Field)) {
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "ABIInfoImpl.h"
#include "CGCUDARuntime.h"
#include "CGCXXABI.h"
#include "CGCall.h"
Expand Down Expand Up @@ -4750,7 +4751,7 @@ static Address emitAddrOfZeroSizeField(CodeGenFunction &CGF, Address Base,
/// The resulting address doesn't necessarily have the right type.
static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base,
const FieldDecl *field) {
if (field->isZeroSize(CGF.getContext()))
if (isEmptyFieldForLayout(CGF.getContext(), field))
return emitAddrOfZeroSizeField(CGF, base, field);

const RecordDecl *rec = field->getParent();
Expand Down
17 changes: 11 additions & 6 deletions clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "ABIInfoImpl.h"
#include "CGCXXABI.h"
#include "CGObjCRuntime.h"
#include "CGRecordLayout.h"
Expand Down Expand Up @@ -720,7 +721,7 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {

// Zero-sized fields are not emitted, but their initializers may still
// prevent emission of this struct as a constant.
if (Field->isZeroSize(CGM.getContext())) {
if (isEmptyFieldForLayout(CGM.getContext(), Field)) {
if (Init->HasSideEffects(CGM.getContext()))
return false;
continue;
Expand Down Expand Up @@ -848,7 +849,8 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
continue;

// Don't emit anonymous bitfields or zero-sized fields.
if (Field->isUnnamedBitField() || Field->isZeroSize(CGM.getContext()))
if (Field->isUnnamedBitField() ||
isEmptyFieldForLayout(CGM.getContext(), *Field))
continue;

// Emit the value of the initializer.
Expand Down Expand Up @@ -2495,8 +2497,10 @@ static llvm::Constant *EmitNullConstant(CodeGenModule &CGM,
cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());

// Ignore empty bases.
if (base->isEmpty() ||
CGM.getContext().getASTRecordLayout(base).getNonVirtualSize()
if (isEmptyRecordForLayout(CGM.getContext(), I.getType()) ||
CGM.getContext()
.getASTRecordLayout(base)
.getNonVirtualSize()
.isZero())
continue;

Expand All @@ -2510,7 +2514,8 @@ static llvm::Constant *EmitNullConstant(CodeGenModule &CGM,
for (const auto *Field : record->fields()) {
// Fill in non-bitfields. (Bitfields always use a zero pattern, which we
// will fill in later.)
if (!Field->isBitField() && !Field->isZeroSize(CGM.getContext())) {
if (!Field->isBitField() &&
!isEmptyFieldForLayout(CGM.getContext(), Field)) {
unsigned fieldIndex = layout.getLLVMFieldNo(Field);
elements[fieldIndex] = CGM.EmitNullConstant(Field->getType());
}
Expand All @@ -2532,7 +2537,7 @@ static llvm::Constant *EmitNullConstant(CodeGenModule &CGM,
cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());

// Ignore empty bases.
if (base->isEmpty())
if (isEmptyRecordForLayout(CGM.getContext(), I.getType()))
continue;

unsigned fieldIndex = layout.getVirtualBaseIndex(base);
Expand Down
23 changes: 15 additions & 8 deletions clang/lib/CodeGen/CGOpenMPRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "CGOpenMPRuntime.h"
#include "ABIInfoImpl.h"
#include "CGCXXABI.h"
#include "CGCleanup.h"
#include "CGRecordLayout.h"
Expand Down Expand Up @@ -7729,23 +7730,28 @@ class MappableExprsHandler {
for (const auto &I : RD->bases()) {
if (I.isVirtual())
continue;
const auto *Base = I.getType()->getAsCXXRecordDecl();

QualType BaseTy = I.getType();
const auto *Base = BaseTy->getAsCXXRecordDecl();
// Ignore empty bases.
if (Base->isEmpty() || CGF.getContext()
.getASTRecordLayout(Base)
.getNonVirtualSize()
.isZero())
if (isEmptyRecordForLayout(CGF.getContext(), BaseTy) ||
CGF.getContext()
.getASTRecordLayout(Base)
.getNonVirtualSize()
.isZero())
continue;

unsigned FieldIndex = RL.getNonVirtualBaseLLVMFieldNo(Base);
RecordLayout[FieldIndex] = Base;
}
// Fill in virtual bases.
for (const auto &I : RD->vbases()) {
const auto *Base = I.getType()->getAsCXXRecordDecl();
QualType BaseTy = I.getType();
// Ignore empty bases.
if (Base->isEmpty())
if (isEmptyRecordForLayout(CGF.getContext(), BaseTy))
continue;

const auto *Base = BaseTy->getAsCXXRecordDecl();
unsigned FieldIndex = RL.getVirtualBaseIndex(Base);
if (RecordLayout[FieldIndex])
continue;
Expand All @@ -7756,7 +7762,8 @@ class MappableExprsHandler {
for (const auto *Field : RD->fields()) {
// Fill in non-bitfields. (Bitfields always use a zero pattern, which we
// will fill in later.)
if (!Field->isBitField() && !Field->isZeroSize(CGF.getContext())) {
if (!Field->isBitField() &&
!isEmptyFieldForLayout(CGF.getContext(), Field)) {
unsigned FieldIndex = RL.getLLVMFieldNo(Field);
RecordLayout[FieldIndex] = Field;
}
Expand Down
15 changes: 8 additions & 7 deletions clang/lib/CodeGen/CGRecordLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
//
//===----------------------------------------------------------------------===//

#include "CGRecordLayout.h"
#include "ABIInfoImpl.h"
#include "CGCXXABI.h"
#include "CGRecordLayout.h"
#include "CodeGenTypes.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
Expand Down Expand Up @@ -384,7 +385,7 @@ void CGRecordLowering::accumulateFields(bool isNonVirtualBaseType) {
Field = accumulateBitFields(isNonVirtualBaseType, Field, FieldEnd);
assert((Field == FieldEnd || !Field->isBitField()) &&
"Failed to accumulate all the bitfields");
} else if (Field->isZeroSize(Context)) {
} else if (isEmptyFieldForLayout(Context, *Field)) {
// Empty fields have no storage.
++Field;
} else {
Expand Down Expand Up @@ -634,7 +635,7 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType,
// non-reusable tail padding.
CharUnits LimitOffset;
for (auto Probe = Field; Probe != FieldEnd; ++Probe)
if (!Probe->isZeroSize(Context)) {
if (!isEmptyFieldForLayout(Context, *Probe)) {
// A member with storage sets the limit.
assert((getFieldBitOffset(*Probe) % CharBits) == 0 &&
"Next storage is not byte-aligned");
Expand Down Expand Up @@ -732,7 +733,7 @@ void CGRecordLowering::accumulateBases() {
// Bases can be zero-sized even if not technically empty if they
// contain only a trailing array member.
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
if (!BaseDecl->isEmpty() &&
if (!isEmptyRecordForLayout(Context, Base.getType()) &&
!Context.getASTRecordLayout(BaseDecl).getNonVirtualSize().isZero())
Members.push_back(MemberInfo(Layout.getBaseClassOffset(BaseDecl),
MemberInfo::Base, getStorageType(BaseDecl), BaseDecl));
Expand Down Expand Up @@ -880,7 +881,7 @@ CGRecordLowering::calculateTailClippingOffset(bool isNonVirtualBaseType) const {
if (!isNonVirtualBaseType && isOverlappingVBaseABI())
for (const auto &Base : RD->vbases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
if (BaseDecl->isEmpty())
if (isEmptyRecordForLayout(Context, Base.getType()))
continue;
// If the vbase is a primary virtual base of some base, then it doesn't
// get its own storage location but instead lives inside of that base.
Expand All @@ -896,7 +897,7 @@ CGRecordLowering::calculateTailClippingOffset(bool isNonVirtualBaseType) const {
void CGRecordLowering::accumulateVBases() {
for (const auto &Base : RD->vbases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
if (BaseDecl->isEmpty())
if (isEmptyRecordForLayout(Context, Base.getType()))
continue;
CharUnits Offset = Layout.getVBaseClassOffset(BaseDecl);
// If the vbase is a primary virtual base of some base, then it doesn't
Expand Down Expand Up @@ -1162,7 +1163,7 @@ CodeGenTypes::ComputeRecordLayout(const RecordDecl *D, llvm::StructType *Ty) {
const FieldDecl *FD = *it;

// Ignore zero-sized fields.
if (FD->isZeroSize(getContext()))
if (isEmptyFieldForLayout(getContext(), FD))
continue;

// For non-bit-fields, just check that the LLVM struct offset matches the
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CodeGenTBAA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//

#include "CodeGenTBAA.h"
#include "ABIInfoImpl.h"
#include "CGRecordLayout.h"
#include "CodeGenTypes.h"
#include "clang/AST/ASTContext.h"
Expand Down Expand Up @@ -309,7 +310,7 @@ CodeGenTBAA::CollectFields(uint64_t BaseOffset,
unsigned idx = 0;
for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end();
i != e; ++i, ++idx) {
if ((*i)->isZeroSize(Context))
if (isEmptyFieldForLayout(Context, *i))
continue;

uint64_t Offset =
Expand Down
16 changes: 14 additions & 2 deletions clang/test/CodeGen/2009-06-14-anonymous-union-init.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
// RUN: %clang_cc1 -emit-llvm < %s | grep "zeroinitializer, i16 16877"
// RUN: %clang_cc1 %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefixes=CHECK,EMPTY
// RUN: %clang_cc1 %s -emit-llvm -triple x86_64-windows-msvc -o - | FileCheck %s --check-prefixes=CHECK,EMPTY-MSVC
// PR4390
struct sysfs_dirent {
union { struct sysfs_elem_dir {} s_dir; };
union { struct sysfs_elem_dir { int x; } s_dir; };
unsigned short s_mode;
};
struct sysfs_dirent sysfs_root = { {}, 16877 };

// CHECK: @sysfs_root = {{.*}}global %struct.sysfs_dirent { %union.anon zeroinitializer, i16 16877 }

struct Foo {
union { struct empty {} x; };
unsigned short s_mode;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks weird at first glance, but I guess the union has size zero in C, so this is fine.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup, can confirm that as C++ we'd get:

%struct.sysfs_dirent = type { [2 x i8], i16 }                                    
                                                                                 
@sysfs_root = global %struct.sysfs_dirent { [2 x i8] undef, i16 16877 }, align 2 

};
struct Foo foo = { {}, 16877 };

// EMPTY: @foo = {{.*}}global %struct.Foo { i16 16877 }
// EMPTY-MSVC: @foo = {{.*}}global %struct.Foo { [4 x i8] undef, i16 16877 }
Copy link
Member Author

@Michael137 Michael137 Jul 10, 2024

Choose a reason for hiding this comment

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

@efriedma-quic For MSVC targets we don't emit a zeroinitializer here now despite the empty union occupying some storage. Do we expect there to be a zeroinitializer here? For C++ we don't emit the zeroinitializer with top-of-tree, but not sure what the expectation/promise for C is here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

clang doesn't currently implement the C23 rules for empty-brace initialization... but I guess if you extend those rules to empty structs/unions (which aren't valid C, but ignoring that), we arguably should zero-init? But really, nobody should notice; this weird stuff is only realistically used for stuff like FAMs.

Testcase which MSVC accepts:

struct Foo {
 unsigned short s_mode;
 struct empty {int a[0];} x; 
 };
struct Foo f() { return (struct Foo){16877}; };

See #78034/#97121.

3 changes: 2 additions & 1 deletion clang/test/CodeGen/X86/x86_64-vaarg.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ typedef struct {
// CHECK: vaarg.end:
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP1]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[VAARG_ADDR]], i64 8, i1 false)
// CHECK-NEXT: [[TMP3:%.*]] = load double, ptr [[RETVAL]], align 8
// CHECK-NEXT: [[COERCE:%.*]] = getelementptr inbounds [[STRUCT_S1]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-NEXT: [[TMP3:%.*]] = load double, ptr [[COERCE]], align 8
// CHECK-NEXT: ret double [[TMP3]]
//
s1 f(int z, ...) {
Expand Down
15 changes: 6 additions & 9 deletions clang/test/CodeGen/paren-list-agg-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,13 @@ struct E {
~E() {};
};

// CHECK-DAG: [[STRUCT_F:%.*]] = type { i8 }
struct F {
F (int i = 1);
F (const F &f) = delete;
F (F &&f) = default;
};

// CHECK-DAG: [[STRUCT_G:%.*]] = type <{ i32, [[STRUCT_F]], [3 x i8] }>
// CHECK-DAG: [[STRUCT_G:%.*]] = type <{ i32, [4 x i8] }>
struct G {
int a;
F f;
Expand All @@ -78,12 +77,12 @@ namespace gh61145 {
~Vec();
};

// CHECK-DAG: [[STRUCT_S1:%.*]] = type { [[STRUCT_VEC]] }
// CHECK-DAG: [[STRUCT_S1:%.*]] = type { i8 }
struct S1 {
Vec v;
};

// CHECK-DAG: [[STRUCT_S2:%.*]] = type { [[STRUCT_VEC]], i8 }
// CHECK-DAG: [[STRUCT_S2:%.*]] = type { i8, i8 }
struct S2 {
Vec v;
char c;
Expand Down Expand Up @@ -377,7 +376,7 @@ void foo18() {
// CHECK-NEXT: [[G:%.*g.*]] = alloca [[STRUCT_G]], align 4
// CHECK-NEXT: [[A:%.*a.*]] = getelementptr inbounds [[STRUCT_G]], ptr [[G]], i32 0, i32 0
// CHECK-NEXT: store i32 2, ptr [[A]], align 4
// CHECK-NEXT: [[F:%.*f.*]] = getelementptr inbounds [[STRUCT_G]], ptr [[G]], i32 0, i32 1
// CHECK-NEXT: [[F:%.*]] = getelementptr inbounds i8, ptr [[G]], i64 4
// CHECk-NEXT: call void @{{.*F.*}}(ptr noundef nonnull align 1 dereferenceable(1)) [[F]], ie32 noundef 1)
// CHECK: ret void
void foo19() {
Expand All @@ -392,9 +391,8 @@ namespace gh61145 {
// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*agg.tmp.ensured.*]] = alloca [[STRUCT_S1]], align 1
// a.k.a. Vec::Vec()
// CHECK-NEXT: call void @_ZN7gh611453VecC1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: [[V1:%.*v1.*]] = getelementptr inbounds [[STRUCT_S1]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0
// a.k.a. Vec::Vec(Vec&&)
// CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noundef nonnull align 1 dereferenceable(1) [[V1]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// a.k.a. S1::~S1()
// CHECK-NEXT: call void @_ZN7gh611452S1D1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]])
// a.k.a.Vec::~Vec()
Expand All @@ -413,9 +411,8 @@ namespace gh61145 {
// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*agg.tmp.ensured.*]] = alloca [[STRUCT_S2]], align 1
// a.k.a. Vec::Vec()
// CHECK-NEXT: call void @_ZN7gh611453VecC1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: [[V1:%.*v1.*]] = getelementptr inbounds [[STRUCT_S2]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0
// a.k.a. Vec::Vec(Vec&&)
// CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noundef nonnull align 1 dereferenceable(1) [[V1]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: [[C:%.*c.*]] = getelementptr inbounds [[STRUCT_S2]], ptr [[AGG_TMP_ENSURED]], i32 0, i32
// CHECK-NEXT: store i8 0, ptr [[C]], align 1
// a.k.a. S2::~S2()
Expand Down
Loading