Skip to content

Commit fd37b4b

Browse files
jacobly0tstellar
authored andcommitted
[Clang][CodeGen] Fix this argument type for certain destructors
With the Microsoft ABI, some destructors need to offset a parameter to get the derived this pointer, in which case the type of that parameter should not be a pointer to the derived type. Reviewed By: efriedma Differential Revision: https://reviews.llvm.org/D143233 (cherry picked from commit 6740991)
1 parent 791a2ec commit fd37b4b

10 files changed

+96
-58
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,9 @@ Windows Support
10151015
by Linux distributions such as Fedora. Also improved such setups by
10161016
avoiding to include ``/usr/include`` among the include paths when cross
10171017
compiling with a cross sysroot based in ``/usr``.
1018+
- Fix incorrect alignment attribute on the this parameter of certain
1019+
non-complete destructors when using the Microsoft ABI.
1020+
`Issue 60465 <https://github.com/llvm/llvm-project/issues/60465>`_.
10181021

10191022
LoongArch Support
10201023
^^^^^^^^^^^^^^^^^

clang/lib/CodeGen/CGCXXABI.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -379,9 +379,8 @@ class CGCXXABI {
379379
/// zero if no specific type is applicable, e.g. if the ABI expects the "this"
380380
/// parameter to point to some artificial offset in a complete object due to
381381
/// vbases being reordered.
382-
virtual const CXXRecordDecl *
383-
getThisArgumentTypeForMethod(const CXXMethodDecl *MD) {
384-
return MD->getParent();
382+
virtual const CXXRecordDecl *getThisArgumentTypeForMethod(GlobalDecl GD) {
383+
return cast<CXXMethodDecl>(GD.getDecl())->getParent();
385384
}
386385

387386
/// Perform ABI-specific "this" argument adjustment required prior to

clang/lib/CodeGen/CGCall.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,9 @@ CodeGenTypes::arrangeCXXStructorDeclaration(GlobalDecl GD) {
322322

323323
SmallVector<CanQualType, 16> argTypes;
324324
SmallVector<FunctionProtoType::ExtParameterInfo, 16> paramInfos;
325-
argTypes.push_back(DeriveThisType(MD->getParent(), MD));
325+
326+
const CXXRecordDecl *ThisType = TheCXXABI.getThisArgumentTypeForMethod(GD);
327+
argTypes.push_back(DeriveThisType(ThisType, MD));
326328

327329
bool PassParams = true;
328330

clang/lib/CodeGen/CGExprCXX.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,20 @@ struct MemberCallInfo {
3333
}
3434

3535
static MemberCallInfo
36-
commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD,
36+
commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, GlobalDecl GD,
3737
llvm::Value *This, llvm::Value *ImplicitParam,
3838
QualType ImplicitParamTy, const CallExpr *CE,
3939
CallArgList &Args, CallArgList *RtlArgs) {
40+
auto *MD = cast<CXXMethodDecl>(GD.getDecl());
41+
4042
assert(CE == nullptr || isa<CXXMemberCallExpr>(CE) ||
4143
isa<CXXOperatorCallExpr>(CE));
4244
assert(MD->isInstance() &&
4345
"Trying to emit a member or operator call expr on a static method!");
4446

4547
// Push the this ptr.
4648
const CXXRecordDecl *RD =
47-
CGF.CGM.getCXXABI().getThisArgumentTypeForMethod(MD);
49+
CGF.CGM.getCXXABI().getThisArgumentTypeForMethod(GD);
4850
Args.add(RValue::get(This), CGF.getTypes().DeriveThisType(RD, MD));
4951

5052
// If there is an implicit parameter (e.g. VTT), emit it.
@@ -110,7 +112,7 @@ RValue CodeGenFunction::EmitCXXDestructorCall(
110112
}
111113

112114
CallArgList Args;
113-
commonEmitCXXMemberOrOperatorCall(*this, DtorDecl, This, ImplicitParam,
115+
commonEmitCXXMemberOrOperatorCall(*this, Dtor, This, ImplicitParam,
114116
ImplicitParamTy, CE, Args, nullptr);
115117
return EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(Dtor), Callee,
116118
ReturnValueSlot(), Args, nullptr, CE && CE == MustTailCall,
@@ -285,7 +287,8 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
285287
assert(ReturnValue.isNull() && "Constructor shouldn't have return value");
286288
CallArgList Args;
287289
commonEmitCXXMemberOrOperatorCall(
288-
*this, Ctor, This.getPointer(*this), /*ImplicitParam=*/nullptr,
290+
*this, {Ctor, Ctor_Complete}, This.getPointer(*this),
291+
/*ImplicitParam=*/nullptr,
289292
/*ImplicitParamTy=*/QualType(), CE, Args, nullptr);
290293

291294
EmitCXXConstructorCall(Ctor, Ctor_Complete, /*ForVirtualBase=*/false,

clang/lib/CodeGen/MicrosoftCXXABI.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,24 @@ class MicrosoftCXXABI : public CGCXXABI {
235235

236236
void EmitCXXDestructors(const CXXDestructorDecl *D) override;
237237

238-
const CXXRecordDecl *
239-
getThisArgumentTypeForMethod(const CXXMethodDecl *MD) override {
240-
if (MD->isVirtual() && !isa<CXXDestructorDecl>(MD)) {
238+
const CXXRecordDecl *getThisArgumentTypeForMethod(GlobalDecl GD) override {
239+
auto *MD = cast<CXXMethodDecl>(GD.getDecl());
240+
241+
if (MD->isVirtual()) {
242+
GlobalDecl LookupGD = GD;
243+
if (const auto *DD = dyn_cast<CXXDestructorDecl>(MD)) {
244+
// Complete dtors take a pointer to the complete object,
245+
// thus don't need adjustment.
246+
if (GD.getDtorType() == Dtor_Complete)
247+
return MD->getParent();
248+
249+
// There's only Dtor_Deleting in vftable but it shares the this
250+
// adjustment with the base one, so look up the deleting one instead.
251+
LookupGD = GlobalDecl(DD, Dtor_Deleting);
252+
}
241253
MethodVFTableLocation ML =
242-
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(MD);
254+
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD);
255+
243256
// The vbases might be ordered differently in the final overrider object
244257
// and the complete object, so the "this" argument may sometimes point to
245258
// memory that has no particular type (e.g. past the complete object).

clang/test/CodeGenCXX/constructor-destructor-return-this.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ D::~D() { }
153153
// CHECKFUCHSIA-LABEL: define{{.*}} %class.D* @_ZN1DD1Ev(%class.D* {{[^,]*}} returned{{[^,]*}} %this)
154154

155155
// CHECKMS-LABEL: define dso_local x86_thiscallcc noundef %class.D* @"??0D@@QAE@XZ"(%class.D* {{[^,]*}} returned{{[^,]*}} %this, i32 noundef %is_most_derived)
156-
// CHECKMS-LABEL: define dso_local x86_thiscallcc void @"??1D@@UAE@XZ"(%class.D* {{[^,]*}} %this)
156+
// CHECKMS-LABEL: define dso_local x86_thiscallcc void @"??1D@@UAE@XZ"(i8*{{[^,]*}} %this.coerce)
157157

158158
class E {
159159
public:

clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,13 @@ void delete_D(D *d) { delete d; }
9090
// For MS, we don't add a new vtable slot to the primary vtable for the virtual
9191
// destructor. Instead we cast to the VDel base class.
9292
// CHECK-MSABI: bitcast {{.*}} %[[d]]
93-
// CHECK-MSABI64-NEXT: getelementptr {{.*}}, i64 8
94-
// CHECK-MSABI32-NEXT: getelementptr {{.*}}, i32 4
95-
// CHECK-MSABI-NEXT: %[[d:.*]] = bitcast i8*
93+
// CHECK-MSABI64-NEXT: %[[d:.*]] = getelementptr {{.*}}, i64 8
94+
// CHECK-MSABI32-NEXT: %[[d:.*]] = getelementptr {{.*}}, i32 4
9695
//
9796
// CHECK: %[[VTABLE:.*]] = load
9897
// CHECK: %[[DTOR:.*]] = load
9998
//
100-
// CHECK: call {{void|noundef i8\*|x86_thiscallcc noundef i8\*}} %[[DTOR]](%{{.*}}* {{[^,]*}} %[[d]]
99+
// CHECK: call {{void|noundef i8\*|x86_thiscallcc noundef i8\*}} %[[DTOR]]({{.*}}*{{[^,]*}} %[[d]]
101100
// CHECK-MSABI-SAME: , i32 noundef 1)
102101
// CHECK-NOT: call
103102
// CHECK: }

clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,6 @@ C::C() { foo(); }
216216
// WIN32-NOT: load
217217
// WIN32: getelementptr i8, i8* %{{.*}}, i32 4
218218
// WIN32-NOT: load
219-
// WIN32: bitcast i8* %{{.*}} to %"struct.crash_on_partial_destroy::B"*
220219
// WIN32: call x86_thiscallcc void @"??1B@crash_on_partial_destroy@@UAE@XZ"
221220
//
222221
// WIN32-NOT: load

clang/test/CodeGenCXX/microsoft-abi-structors.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,28 +155,24 @@ struct C : A, B {
155155
};
156156

157157
C::~C() {
158-
// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1C@dtor_in_second_nvbase@@UAE@XZ"
159-
// CHECK: (%"struct.dtor_in_second_nvbase::C"* {{[^,]*}} %this)
158+
// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1C@dtor_in_second_nvbase@@UAE@XZ"(i8*{{[^,]*}} %this.coerce)
160159
// No this adjustment!
161160
// CHECK-NOT: getelementptr
162161
// CHECK: load %"struct.dtor_in_second_nvbase::C"*, %"struct.dtor_in_second_nvbase::C"** %{{.*}}
163162
// Now we this-adjust before calling ~B.
164163
// CHECK: bitcast %"struct.dtor_in_second_nvbase::C"* %{{.*}} to i8*
165164
// CHECK: getelementptr inbounds i8, i8* %{{.*}}, i32 4
166165
// CHECK: bitcast i8* %{{.*}} to %"struct.dtor_in_second_nvbase::B"*
167-
// CHECK: call x86_thiscallcc void @"??1B@dtor_in_second_nvbase@@UAE@XZ"
168-
// CHECK: (%"struct.dtor_in_second_nvbase::B"* {{[^,]*}} %{{.*}})
166+
// CHECK: call x86_thiscallcc void @"??1B@dtor_in_second_nvbase@@UAE@XZ"(%"struct.dtor_in_second_nvbase::B"*{{[^,]*}} %{{.*}})
169167
// CHECK: ret void
170168
}
171169

172170
void foo() {
173171
C c;
174172
}
175-
// DTORS2-LABEL: define linkonce_odr dso_local x86_thiscallcc i8* @"??_EC@dtor_in_second_nvbase@@W3AEPAXI@Z"
176-
// DTORS2: (%"struct.dtor_in_second_nvbase::C"* %this, i32 %should_call_delete)
173+
// DTORS2-LABEL: define linkonce_odr dso_local x86_thiscallcc i8* @"??_EC@dtor_in_second_nvbase@@W3AEPAXI@Z"(i8* %this.coerce, i32 %should_call_delete)
177174
// Do an adjustment from B* to C*.
178175
// DTORS2: getelementptr i8, i8* %{{.*}}, i32 -4
179-
// DTORS2: bitcast i8* %{{.*}} to %"struct.dtor_in_second_nvbase::C"*
180176
// DTORS2: %[[CALL:.*]] = tail call x86_thiscallcc i8* @"??_GC@dtor_in_second_nvbase@@UAEPAXI@Z"
181177
// DTORS2: ret i8* %[[CALL]]
182178
}
@@ -195,7 +191,7 @@ struct E : virtual C { int e; };
195191
struct F : D, E { ~F(); int f; };
196192

197193
F::~F() {
198-
// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1F@test2@@UAE@XZ"(%"struct.test2::F"*{{[^,]*}})
194+
// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1F@test2@@UAE@XZ"(i8*{{[^,]*}})
199195
// Do an adjustment from C vbase subobject to F as though F was the
200196
// complete type.
201197
// CHECK: getelementptr inbounds i8, i8* %{{.*}}, i32 -20
@@ -209,7 +205,6 @@ void foo() {
209205
// DTORS3-LABEL: define linkonce_odr dso_local x86_thiscallcc void @"??_DF@test2@@QAEXXZ"({{.*}} {{.*}} comdat
210206
// Do an adjustment from C* to F*.
211207
// DTORS3: getelementptr i8, i8* %{{.*}}, i32 20
212-
// DTORS3: bitcast i8* %{{.*}} to %"struct.test2::F"*
213208
// DTORS3: call x86_thiscallcc void @"??1F@test2@@UAE@XZ"
214209
// DTORS3: ret void
215210

clang/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ B::B() {
5353
B::~B() {
5454
// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1B@@UAE@XZ"
5555
// Store initial this:
56+
// CHECK: %[[THIS:.*]] = alloca %struct.B*
5657
// CHECK: %[[THIS_ADDR:.*]] = alloca %struct.B*
5758
// CHECK: store %struct.B* %{{.*}}, %struct.B** %[[THIS_ADDR]], align 4
5859
// Reload and adjust the this parameter:
@@ -90,15 +91,15 @@ B::~B() {
9091
// CHECK2: %[[THIS:.*]] = load %struct.B*, %struct.B** {{.*}}
9192
// CHECK2: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
9293
// CHECK2: %[[B_i8:.*]] = getelementptr i8, i8* %[[THIS_i8]], i32 8
93-
// CHECK2: %[[B:.*]] = bitcast i8* %[[B_i8]] to %struct.B*
94-
// CHECK2: call x86_thiscallcc void @"??1B@@UAE@XZ"(%struct.B* {{[^,]*}} %[[B]])
94+
// CHECK2: call x86_thiscallcc void @"??1B@@UAE@XZ"(i8*{{[^,]*}} %[[B_i8]])
9595
// CHECK2: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
9696
// CHECK2: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 8
9797
// CHECK2: %[[VBASE:.*]] = bitcast i8* %[[VBASE_i8]] to %struct.VBase*
9898
// CHECK2: call x86_thiscallcc void @"??1VBase@@UAE@XZ"(%struct.VBase* {{[^,]*}} %[[VBASE]])
9999
// CHECK2: ret
100100

101101
// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef i8* @"??_GB@@UAEPAXI@Z"
102+
// CHECK2: store %struct.B* %{{.*}}, %struct.B** %[[THIS:.*]], align 4
102103
// CHECK2: store %struct.B* %{{.*}}, %struct.B** %[[THIS_ADDR:.*]], align 4
103104
// CHECK2: %[[THIS:.*]] = load %struct.B*, %struct.B** %[[THIS_ADDR]]
104105
// CHECK2: %[[THIS_PARAM_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
@@ -195,8 +196,7 @@ void delete_B(B *obj) {
195196
// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, i32* %[[VBTABLE]], i32 1
196197
// CHECK: %[[VBOFFSET32:.*]] = load i32, i32* %[[VBENTRY]]
197198
// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
198-
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
199-
// CHECK: %[[VBASE:.*]] = bitcast i8* %[[VBASE_i8]] to %struct.B*
199+
// CHECK: %[[VBASE:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
200200
//
201201
// CHECK: %[[OBJ_i8:.*]] = bitcast %struct.B* %[[OBJ]] to i8*
202202
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 0
@@ -206,12 +206,12 @@ void delete_B(B *obj) {
206206
// CHECK: %[[VBOFFSET32:.*]] = load i32, i32* %[[VBENTRY]]
207207
// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
208208
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
209-
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VBASE_i8]] to i8* (%struct.B*, i32)***
210-
// CHECK: %[[VFTABLE:.*]] = load i8* (%struct.B*, i32)**, i8* (%struct.B*, i32)*** %[[VFPTR]]
211-
// CHECK: %[[VFUN:.*]] = getelementptr inbounds i8* (%struct.B*, i32)*, i8* (%struct.B*, i32)** %[[VFTABLE]], i64 0
212-
// CHECK: %[[VFUN_VALUE:.*]] = load i8* (%struct.B*, i32)*, i8* (%struct.B*, i32)** %[[VFUN]]
209+
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VBASE_i8]] to i8* (i8*, i32)***
210+
// CHECK: %[[VFTABLE:.*]] = load i8* (i8*, i32)**, i8* (i8*, i32)*** %[[VFPTR]]
211+
// CHECK: %[[VFUN:.*]] = getelementptr inbounds i8* (i8*, i32)*, i8* (i8*, i32)** %[[VFTABLE]], i64 0
212+
// CHECK: %[[VFUN_VALUE:.*]] = load i8* (i8*, i32)*, i8* (i8*, i32)** %[[VFUN]]
213213
//
214-
// CHECK: call x86_thiscallcc noundef i8* %[[VFUN_VALUE]](%struct.B* {{[^,]*}} %[[VBASE]], i32 noundef 1)
214+
// CHECK: call x86_thiscallcc noundef i8* %[[VFUN_VALUE]](i8* {{[^,]*}} %[[VBASE]], i32 noundef 1)
215215
// CHECK: ret void
216216
}
217217

@@ -295,8 +295,9 @@ struct D : virtual Z, B, C {
295295
} d;
296296

297297
D::~D() {
298-
// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1D@diamond@@UAE@XZ"(%"struct.diamond::D"*{{.*}})
298+
// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1D@diamond@@UAE@XZ"(i8*{{.*}})
299299
// Store initial this:
300+
// CHECK: %[[THIS:.*]] = alloca %"struct.diamond::D"*
300301
// CHECK: %[[THIS_ADDR:.*]] = alloca %"struct.diamond::D"*
301302
// CHECK: store %"struct.diamond::D"* %{{.*}}, %"struct.diamond::D"** %[[THIS_ADDR]], align 4
302303
//
@@ -310,16 +311,13 @@ D::~D() {
310311
// CHECK: %[[C_i8:.*]] = getelementptr inbounds i8, i8* %[[D_i8]], i32 4
311312
// CHECK: %[[C:.*]] = bitcast i8* %[[C_i8]] to %"struct.diamond::C"*
312313
// CHECK: %[[C_i8:.*]] = bitcast %"struct.diamond::C"* %[[C]] to i8*
313-
// CHECK: %[[ARG_i8:.*]] = getelementptr i8, i8* %{{.*}}, i32 16
314-
// FIXME: We might consider changing the dtor this parameter type to i8*.
315-
// CHECK: %[[ARG:.*]] = bitcast i8* %[[ARG_i8]] to %"struct.diamond::C"*
316-
// CHECK: call x86_thiscallcc void @"??1C@diamond@@UAE@XZ"(%"struct.diamond::C"* {{[^,]*}} %[[ARG]])
314+
// CHECK: %[[ARG:.*]] = getelementptr i8, i8* %{{.*}}, i32 16
315+
// CHECK: call x86_thiscallcc void @"??1C@diamond@@UAE@XZ"(i8*{{[^,]*}} %[[ARG]])
317316

318317
// CHECK: %[[B:.*]] = bitcast %"struct.diamond::D"* %[[THIS]] to %"struct.diamond::B"*
319318
// CHECK: %[[B_i8:.*]] = bitcast %"struct.diamond::B"* %[[B]] to i8*
320-
// CHECK: %[[ARG_i8:.*]] = getelementptr i8, i8* %[[B_i8]], i32 4
321-
// CHECK: %[[ARG:.*]] = bitcast i8* %[[ARG_i8]] to %"struct.diamond::B"*
322-
// CHECK: call x86_thiscallcc void @"??1B@diamond@@UAE@XZ"(%"struct.diamond::B"* {{[^,]*}} %[[ARG]])
319+
// CHECK: %[[ARG:.*]] = getelementptr i8, i8* %[[B_i8]], i32 4
320+
// CHECK: call x86_thiscallcc void @"??1B@diamond@@UAE@XZ"(i8*{{[^,]*}} %[[ARG]])
323321
// CHECK: ret void
324322
}
325323

@@ -443,9 +441,10 @@ struct E : D, B, virtual A {
443441
};
444442

445443
E::~E() {
446-
// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1E@test4@@UAE@XZ"(%"struct.test4::E"* {{[^,]*}} %this)
444+
// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1E@test4@@UAE@XZ"(i8*{{[^,]*}} %this.coerce)
447445

448446
// In this case "this" points to the most derived class, so no GEPs needed.
447+
// CHECK: bitcast i8* %this.coerce
449448
// CHECK-NOT: getelementptr
450449
// CHECK-NOT: bitcast
451450
// CHECK: %[[VFPTR_i8:.*]] = bitcast %"struct.test4::E"* %{{.*}} to i32 (...)***
@@ -458,16 +457,14 @@ void destroy(E *obj) {
458457

459458
// CHECK-NOT: getelementptr
460459
// CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test4::E"* %[[OBJ]] to i8*
461-
// CHECK: %[[B_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 4
462-
// FIXME: in fact, the call should take i8* and the bitcast is redundant.
463-
// CHECK: %[[B_as_E:.*]] = bitcast i8* %[[B_i8]] to %"struct.test4::E"*
460+
// CHECK: %[[THIS_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 4
464461
// CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test4::E"* %[[OBJ:.*]] to i8*
465462
// CHECK: %[[B_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 4
466-
// CHECK: %[[VPTR:.*]] = bitcast i8* %[[B_i8]] to i8* (%"struct.test4::E"*, i32)***
467-
// CHECK: %[[VFTABLE:.*]] = load i8* (%"struct.test4::E"*, i32)**, i8* (%"struct.test4::E"*, i32)*** %[[VPTR]]
468-
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds i8* (%"struct.test4::E"*, i32)*, i8* (%"struct.test4::E"*, i32)** %[[VFTABLE]], i64 0
469-
// CHECK: %[[VFUN:.*]] = load i8* (%"struct.test4::E"*, i32)*, i8* (%"struct.test4::E"*, i32)** %[[VFTENTRY]]
470-
// CHECK: call x86_thiscallcc noundef i8* %[[VFUN]](%"struct.test4::E"* {{[^,]*}} %[[B_as_E]], i32 noundef 1)
463+
// CHECK: %[[VPTR:.*]] = bitcast i8* %[[B_i8]] to i8* (i8*, i32)***
464+
// CHECK: %[[VFTABLE:.*]] = load i8* (i8*, i32)**, i8* (i8*, i32)*** %[[VPTR]]
465+
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds i8* (i8*, i32)*, i8* (i8*, i32)** %[[VFTABLE]], i64 0
466+
// CHECK: %[[VFUN:.*]] = load i8* (i8*, i32)*, i8* (i8*, i32)** %[[VFTENTRY]]
467+
// CHECK: call x86_thiscallcc noundef i8* %[[VFUN]](i8*{{[^,]*}} %[[THIS_i8]], i32 noundef 1)
471468
delete obj;
472469
}
473470

@@ -549,9 +546,37 @@ struct B {
549546
struct C : virtual B {};
550547
struct D : virtual A, C {};
551548
D d;
552-
// CHECK-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef i8* @"??_GD@pr36921@@UAEPAXI@Z"(
553-
// CHECK: %[[THIS:.*]] = load %"struct.pr36921::D"*, %"struct.pr36921::D"**
554-
// CHECK: %[[THIS_UNADJ_i8:.*]] = bitcast %"struct.pr36921::D"* %[[THIS_RELOAD]] to i8*
555-
// CHECK: %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_UNADJ_i8]], i32 -4
556-
// CHECK: %[[THIS:.*]] = bitcast i8* %[[THIS_ADJ_i8]] to %"struct.pr36921::D"*
549+
// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef i8* @"??_GD@pr36921@@UAEPAXI@Z"(
550+
// CHECK2: %[[THIS:.*]] = load %"struct.pr36921::D"*, %"struct.pr36921::D"**
551+
// CHECK2: %[[THIS_RELOAD:.*]] = load %"struct.pr36921::D"*, %"struct.pr36921::D"**
552+
// CHECK2: %[[THIS_UNADJ_i8:.*]] = bitcast %"struct.pr36921::D"* %[[THIS_RELOAD]] to i8*
553+
// CHECK2: %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_UNADJ_i8]], i32 -4
554+
// CHECK2: %[[THIS:.*]] = bitcast i8* %[[THIS_ADJ_i8]] to %"struct.pr36921::D"*
555+
}
556+
557+
namespace issue_60465 {
558+
// We used to assume the first argument to all destructors was the derived type
559+
// even when there was a 'this' adjustment.
560+
struct A {
561+
virtual ~A();
562+
};
563+
564+
struct alignas(2 * sizeof(void *)) B : virtual A {
565+
~B();
566+
void *x, *y;
567+
};
568+
569+
B::~B() {
570+
// The 'this' parameter should not have a type of %"struct.issue_60465::B"* and
571+
// must not have 'align 8', since at least B's copy of A is only 'align 4'.
572+
// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1B@issue_60465@@UAE@XZ"(i8* noundef %this.coerce)
573+
// CHECK: %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, i8* %{{.*}}, i32 -12
574+
// CHECK: %[[THIS_ADJ:.*]] = bitcast i8* %[[THIS_ADJ_i8]] to %"struct.issue_60465::B"*
575+
// CHECK: %[[X:.*]] = getelementptr inbounds %"struct.issue_60465::B", %"struct.issue_60465::B"* %[[THIS_ADJ]], i32 0, i32 1
576+
// CHECK: store i8* null, i8** %[[X]], align 4
577+
// CHECK: %[[Y:.*]] = getelementptr inbounds %"struct.issue_60465::B", %"struct.issue_60465::B"* %[[THIS_ADJ]], i32 0, i32 2
578+
// CHECK: store i8* null, i8** %[[Y]], align 8
579+
x = nullptr;
580+
y = nullptr;
581+
}
557582
}

0 commit comments

Comments
 (0)