brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.8 KiB · b54775f Raw
513 lines · cpp
1// RUN: %clang_cc1 %s -fno-rtti -std=c++11 -Wno-inaccessible-base -triple=i386-pc-win32 -emit-llvm -o %t2// RUN: FileCheck %s < %t3// RUN: FileCheck --check-prefix=CHECK2 %s < %t4 5// For now, just make sure x86_64 doesn't crash.6// RUN: %clang_cc1 %s -fno-rtti -std=c++11 -Wno-inaccessible-base -triple=x86_64-pc-win32 -emit-llvm -o %t7 8struct VBase {9  virtual ~VBase();10  virtual void foo();11  virtual void bar();12  int field;13};14 15struct B : virtual VBase {16  B();17  virtual ~B();18  virtual void foo();19  virtual void bar();20};21 22B::B() {23  // CHECK-LABEL: define dso_local x86_thiscallcc noundef ptr @"??0B@@QAE@XZ"24  // CHECK:   %[[THIS:.*]] = load ptr, ptr25  // CHECK:   br i1 %{{.*}}, label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]]26 27  // Don't check the INIT_VBASES case as it's covered by the ctor tests.28 29  // CHECK: %[[SKIP_VBASES]]30  // CHECK:   %[[VBPTR:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i32 031  // ...32  // CHECK:   %[[VFPTR_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i32 %{{.*}}33  // CHECK:   store ptr @"??_7B@@6B@", ptr %[[VFPTR_i8]]34 35  // Initialize vtorDisp:36  // CHECK:   %[[VBPTR:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i32 037  // ...38  // CHECK:   %[[VBASE_OFFSET:.*]] = add nsw i32 0, %{{.*}}39  // CHECK:   %[[VTORDISP_VAL:.*]] = sub i32 %[[VBASE_OFFSET]], 840  // CHECK:   %[[VBASE_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i32 %[[VBASE_OFFSET]]41  // CHECK:   %[[VTORDISP_i8:.*]] = getelementptr i8, ptr %[[VBASE_i8]], i32 -442  // CHECK:   store i32 %[[VTORDISP_VAL]], ptr %[[VTORDISP_i8]]43 44  // CHECK: ret45}46 47B::~B() {48  // CHECK-LABEL: define dso_local x86_thiscallcc void @"??1B@@UAE@XZ"49  // Store initial this:50  // CHECK:   %[[THIS_ADDR:.*]] = alloca ptr51  // CHECK:   store ptr %{{.*}}, ptr %[[THIS_ADDR]], align 452  // Reload and adjust the this parameter:53  // CHECK:   %[[THIS_RELOAD:.*]] = load ptr, ptr %[[THIS_ADDR]]54  // CHECK:   %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_RELOAD]], i32 -855 56  // Restore the vfptr that could have been changed by a subclass.57  // CHECK:   %[[VBPTR:.*]] = getelementptr inbounds i8, ptr %[[THIS_ADJ_i8]], i32 058  // ...59  // CHECK:   %[[VFPTR_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_ADJ_i8]], i32 %{{.*}}60  // CHECK:   store ptr @"??_7B@@6B@", ptr %[[VFPTR_i8]]61 62  // Initialize vtorDisp:63  // CHECK:   %[[VBPTR:.*]] = getelementptr inbounds i8, ptr %[[THIS_ADJ_i8]], i32 064  // ...65  // CHECK:   %[[VBASE_OFFSET:.*]] = add nsw i32 0, %{{.*}}66  // CHECK:   %[[VTORDISP_VAL:.*]] = sub i32 %[[VBASE_OFFSET]], 867  // CHECK:   %[[VBASE_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_ADJ_i8]], i32 %[[VBASE_OFFSET]]68  // CHECK:   %[[VTORDISP_i8:.*]] = getelementptr i8, ptr %[[VBASE_i8]], i32 -469  // CHECK:   store i32 %[[VTORDISP_VAL]], ptr %[[VTORDISP_i8]]70 71  foo();  // Avoid the "trivial destructor" optimization.72 73  // CHECK: ret74 75  // CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc void @"??_DB@@QAEXXZ"(ptr76  // CHECK2: %[[THIS:.*]] = load ptr, ptr {{.*}}77  // CHECK2: %[[B_i8:.*]] = getelementptr i8, ptr %[[THIS]], i32 878  // CHECK2: call x86_thiscallcc void @"??1B@@UAE@XZ"(ptr{{[^,]*}} %[[B_i8]])79  // CHECK2: %[[VBASE_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i32 880  // CHECK2: call x86_thiscallcc void @"??1VBase@@UAE@XZ"(ptr {{[^,]*}} %[[VBASE_i8]])81  // CHECK2: ret82 83  // CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_GB@@UAEPAXI@Z"84  // CHECK2:   store ptr %{{.*}}, ptr %[[THIS_ADDR:.*]], align 485  // CHECK2:   %[[THIS_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_PARAM_i8:.*]], i32 -886  // CHECK2:   call x86_thiscallcc void @"??_DB@@QAEXXZ"(ptr {{[^,]*}} %[[THIS_i8]])87  // ...88  // CHECK2: ret89}90 91void B::foo() {92// CHECK-LABEL: define dso_local x86_thiscallcc void @"?foo@B@@UAEXXZ"(ptr93//94// B::foo gets 'this' cast to VBase* in ECX (i.e. this+8) so we95// need to adjust 'this' before use.96//97// Coerce this to correct type:98// CHECK:   %[[THIS_ADDR:.*]] = alloca ptr99//100// Store initial this:101// CHECK:   store ptr {{.*}}, ptr %[[THIS_ADDR]], align 4102//103// Reload and adjust the this parameter:104// CHECK:   %[[THIS_RELOAD:.*]] = load ptr, ptr %[[THIS_ADDR]]105// CHECK:   %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_RELOAD]], i32 -8106 107  field = 42;108// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, ptr %[[THIS_ADJ_i8]], i32 0109// CHECK: %[[VBTABLE:.*]] = load ptr, ptr %[[VBPTR]]110// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, ptr %[[VBTABLE]], i32 1111// CHECK: %[[VBOFFSET32:.*]] = load i32, ptr %[[VBENTRY]]112// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]113// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_ADJ_i8]], i32 %[[VBOFFSET]]114// CHECK: %[[FIELD:.*]] = getelementptr inbounds nuw %struct.VBase, ptr %[[VBASE_i8]], i32 0, i32 1115// CHECK: store i32 42, ptr %[[FIELD]], align 4116//117// CHECK: ret void118}119 120void call_vbase_bar(B *obj) {121// CHECK-LABEL: define dso_local void @"?call_vbase_bar@@YAXPAUB@@@Z"(ptr noundef %obj)122// CHECK: %[[OBJ:.*]] = load ptr123 124  obj->bar();125// When calling a vbase's virtual method, one needs to adjust 'this'126// at the caller site.127//128// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i32 0129// CHECK: %[[VBTABLE:.*]] = load ptr, ptr %[[VBPTR]]130// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, ptr %[[VBTABLE]], i32 1131// CHECK: %[[VBOFFSET32:.*]] = load i32, ptr %[[VBENTRY]]132// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]133// CHECK: %[[VBASE:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i32 %[[VBOFFSET]]134//135// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i32 0136// CHECK: %[[VBTABLE:.*]] = load ptr, ptr %[[VBPTR]]137// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, ptr %[[VBTABLE]], i32 1138// CHECK: %[[VBOFFSET32:.*]] = load i32, ptr %[[VBENTRY]]139// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]140// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i32 %[[VBOFFSET]]141// CHECK: %[[VFTABLE:.*]] = load ptr, ptr %[[VBASE_i8]]142// CHECK: %[[VFUN:.*]] = getelementptr inbounds ptr, ptr %[[VFTABLE]], i64 2143// CHECK: %[[VFUN_VALUE:.*]] = load ptr, ptr %[[VFUN]]144//145// CHECK: call x86_thiscallcc void %[[VFUN_VALUE]](ptr noundef %[[VBASE]])146//147// CHECK: ret void148}149 150void delete_B(B *obj) {151// CHECK-LABEL: define dso_local void @"?delete_B@@YAXPAUB@@@Z"(ptr noundef %obj)152// CHECK: %[[OBJ:.*]] = load ptr153 154  delete obj;155// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i32 0156// CHECK: %[[VBTABLE:.*]] = load ptr, ptr %[[VBPTR]]157// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, ptr %[[VBTABLE]], i32 1158// CHECK: %[[VBOFFSET32:.*]] = load i32, ptr %[[VBENTRY]]159// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]160// CHECK: %[[VBASE:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i32 %[[VBOFFSET]]161//162// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i32 0163// CHECK: %[[VBTABLE:.*]] = load ptr, ptr %[[VBPTR]]164// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, ptr %[[VBTABLE]], i32 1165// CHECK: %[[VBOFFSET32:.*]] = load i32, ptr %[[VBENTRY]]166// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]167// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i32 %[[VBOFFSET]]168// CHECK: %[[VFTABLE:.*]] = load ptr, ptr %[[VBASE_i8]]169// CHECK: %[[VFUN:.*]] = getelementptr inbounds ptr, ptr %[[VFTABLE]], i64 0170// CHECK: %[[VFUN_VALUE:.*]] = load ptr, ptr %[[VFUN]]171//172// CHECK: call x86_thiscallcc noundef ptr %[[VFUN_VALUE]](ptr {{[^,]*}} %[[VBASE]], i32 noundef 1)173// CHECK: ret void174}175 176void call_complete_dtor() {177  // CHECK-LABEL: define dso_local void @"?call_complete_dtor@@YAXXZ"178  B b;179  // CHECK: call x86_thiscallcc noundef ptr @"??0B@@QAE@XZ"(ptr {{[^,]*}} %[[B:.*]], i32 noundef 1)180  // CHECK-NOT: getelementptr181  // CHECK: call x86_thiscallcc void @"??_DB@@QAEXXZ"(ptr {{[^,]*}} %[[B]])182  // CHECK: ret183}184 185struct C : B {186  C();187  // has an implicit vdtor.188};189 190// Used to crash on an assertion.191C::C() {192// CHECK-LABEL: define dso_local x86_thiscallcc noundef ptr @"??0C@@QAE@XZ"193}194 195namespace multiple_vbases {196struct A {197  virtual void a();198};199 200struct B {201  virtual void b();202};203 204struct C {205  virtual void c();206};207 208struct D : virtual A, virtual B, virtual C {209  virtual void a();210  virtual void b();211  virtual void c();212  D();213};214 215D::D() {216  // CHECK-LABEL: define dso_local x86_thiscallcc noundef ptr @"??0D@multiple_vbases@@QAE@XZ"217  // Just make sure we emit 3 vtordisps after initializing vfptrs.218  // CHECK: store ptr @"??_7D@multiple_vbases@@6BA@1@@", ptr %{{.*}}219  // CHECK: store ptr @"??_7D@multiple_vbases@@6BB@1@@", ptr %{{.*}}220  // CHECK: store ptr @"??_7D@multiple_vbases@@6BC@1@@", ptr %{{.*}}221  // ...222  // CHECK: store i32 %{{.*}}, ptr %{{.*}}223  // CHECK: store i32 %{{.*}}, ptr %{{.*}}224  // CHECK: store i32 %{{.*}}, ptr %{{.*}}225  // CHECK: ret226}227}228 229namespace diamond {230struct A {231  A();232  virtual ~A();233};234 235struct B : virtual A {236  B();237  ~B();238};239 240struct C : virtual A {241  C();242  ~C();243  int c1, c2, c3;244};245 246struct Z {247  int z;248};249 250struct D : virtual Z, B, C {251  D();252  ~D();253} d;254 255D::~D() {256  // CHECK-LABEL: define dso_local x86_thiscallcc void @"??1D@diamond@@UAE@XZ"(ptr{{.*}})257  // Store initial this:258  // CHECK: %[[THIS_ADDR:.*]] = alloca ptr259  // CHECK: store ptr %{{.*}}, ptr %[[THIS_ADDR]], align 4260  //261  // Reload and adjust the this parameter:262  // CHECK: %[[THIS_RELOAD:.*]] = load ptr, ptr %[[THIS_ADDR]]263  // CHECK: %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_RELOAD]], i32 -24264  //265  // CHECK: %[[C_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_ADJ_i8]], i32 4266  // CHECK: %[[ARG:.*]] = getelementptr i8, ptr %{{.*}}, i32 16267  // CHECK: call x86_thiscallcc void @"??1C@diamond@@UAE@XZ"(ptr{{[^,]*}} %[[ARG]])268 269  // CHECK: %[[ARG:.*]] = getelementptr i8, ptr %[[THIS_ADJ_i8]], i32 4270  // CHECK: call x86_thiscallcc void @"??1B@diamond@@UAE@XZ"(ptr{{[^,]*}} %[[ARG]])271  // CHECK: ret void272}273 274}275 276namespace test2 {277struct A { A(); };278struct B : virtual A { B() {} };279struct C : B, A { C() {} };280 281// PR18435: Order mattered here.  We were generating code for the delegating282// call to B() from C().283void callC() { C x; }284 285// CHECK-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??0C@test2@@QAE@XZ"286// CHECK:           (ptr {{[^,]*}} returned align 4 dereferenceable(8) %this, i32 noundef %is_most_derived)287// CHECK: br i1288//   Virtual bases289// CHECK: call x86_thiscallcc noundef ptr @"??0A@test2@@QAE@XZ"(ptr {{[^,]*}} %{{.*}})290// CHECK: br label291//   Non-virtual bases292// CHECK: call x86_thiscallcc noundef ptr @"??0B@test2@@QAE@XZ"(ptr {{[^,]*}} %{{.*}}, i32 noundef 0)293// CHECK: call x86_thiscallcc noundef ptr @"??0A@test2@@QAE@XZ"(ptr {{[^,]*}} %{{.*}})294// CHECK: ret295 296// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??0B@test2@@QAE@XZ"297// CHECK2:           (ptr {{[^,]*}} returned align 4 dereferenceable(4) %this, i32 noundef %is_most_derived)298// CHECK2: call x86_thiscallcc noundef ptr @"??0A@test2@@QAE@XZ"(ptr {{[^,]*}} %{{.*}})299// CHECK2: ret300 301}302 303namespace test3 {304// PR19104: A non-virtual call of a virtual method doesn't use vftable thunks,305// so requires only static adjustment which is different to the one used306// for virtual calls.307struct A {308  virtual void foo();309};310 311struct B : virtual A {312  virtual void bar();313};314 315struct C : virtual A {316  virtual void foo();317};318 319struct D : B, C {320  virtual void bar();321  int field;  // Laid out between C and A subobjects in D.322};323 324void D::bar() {325  // CHECK-LABEL: define dso_local x86_thiscallcc void @"?bar@D@test3@@UAEXXZ"(ptr {{[^,]*}} %this)326 327  C::foo();328  // Shouldn't need any vbtable lookups.  All we have to do is adjust to C*,329  // then compensate for the adjustment performed in the C::foo() prologue.330  // CHECK: %[[C_i8:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i32 8331  // CHECK: %[[ARG:.*]] = getelementptr i8, ptr %[[C_i8]], i32 4332  // CHECK: call x86_thiscallcc void @"?foo@C@test3@@UAEXXZ"(ptr noundef %[[ARG]])333  // CHECK: ret334}335}336 337namespace test4{338// PR19172: We used to merge method vftable locations wrong.339 340struct A {341  virtual ~A() {}342};343 344struct B {345  virtual ~B() {}346};347 348struct C : virtual A, B {349  virtual ~C();350};351 352void foo(void*);353 354C::~C() {355  // CHECK-LABEL: define dso_local x86_thiscallcc void @"??1C@test4@@UAE@XZ"(ptr {{[^,]*}} %this)356 357  // In this case "this" points to the most derived class, so no GEPs needed.358  // CHECK-NOT: getelementptr359  // CHECK: store ptr @"??_7C@test4@@6BB@1@@", ptr %{{.*}}360 361  foo(this);362  // CHECK: ret363}364 365void destroy(C *obj) {366  // CHECK-LABEL: define dso_local void @"?destroy@test4@@YAXPAUC@1@@Z"(ptr noundef %obj)367 368  delete obj;369  // CHECK: %[[OBJ:.*]] = load ptr, ptr370  // CHECK: %[[VFTABLE:.*]] = load ptr, ptr %[[OBJ]]371  // CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds ptr, ptr %[[VFTABLE]], i64 0372  // CHECK: %[[VFUN:.*]] = load ptr, ptr %[[VFTENTRY]]373  // CHECK: call x86_thiscallcc noundef ptr %[[VFUN]](ptr {{[^,]*}} %[[OBJ]], i32 noundef 1)374  // CHECK: ret375}376 377struct D {378  virtual void d();379};380 381// The first non-virtual base doesn't have a vdtor,382// but "this adjustment" is not needed.383struct E : D, B, virtual A {384  virtual ~E();385};386 387E::~E() {388  // CHECK-LABEL: define dso_local x86_thiscallcc void @"??1E@test4@@UAE@XZ"(ptr{{[^,]*}} %this)389 390  // In this case "this" points to the most derived class, so no GEPs needed.391  // CHECK-NOT: getelementptr392  // CHECK: store ptr @"??_7E@test4@@6BD@1@@", ptr %{{.*}}393  foo(this);394}395 396void destroy(E *obj) {397  // CHECK-LABEL: define dso_local void @"?destroy@test4@@YAXPAUE@1@@Z"(ptr noundef %obj)398 399  // CHECK-NOT: getelementptr400  // CHECK: %[[THIS_i8:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i32 4401  // CHECK: %[[B_i8:.*]] = getelementptr inbounds i8, ptr %[[OBJ:.*]], i32 4402  // CHECK: %[[VFTABLE:.*]] = load ptr, ptr %[[B_i8]]403  // CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds ptr, ptr %[[VFTABLE]], i64 0404  // CHECK: %[[VFUN:.*]] = load ptr, ptr %[[VFTENTRY]]405  // CHECK: call x86_thiscallcc noundef ptr %[[VFUN]](ptr{{[^,]*}} %[[THIS_i8]], i32 noundef 1)406  delete obj;407}408 409}410 411namespace test5 {412// PR25370: Don't zero-initialize vbptrs in virtual bases.413struct A {414  virtual void f();415};416 417struct B : virtual A {418  int Field;419};420 421struct C : B {422  C();423};424 425C::C() : B() {}426// CHECK-LABEL: define dso_local x86_thiscallcc noundef ptr @"??0C@test5@@QAE@XZ"(427// CHECK:   %[[THIS:.*]] = load ptr, ptr428// CHECK:   br i1 %{{.*}}, label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]]429 430// CHECK: %[[SKIP_VBASES]]431// CHECK:   %[[FIELD:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i32 4432// CHECK:   call void @llvm.memset.p0.i32(ptr align 4 %[[FIELD]], i8 0, i32 4, i1 false)433}434 435namespace pr27621 {436// Devirtualization through a static_cast used to make us compute the 'this'437// adjustment for B::g instead of C::g. When we directly call C::g, 'this' is a438// B*, and the prologue of C::g will adjust it to a C*.439struct A { virtual void f(); };440struct B { virtual void g(); };441struct C final : A, B {442  virtual void h();443  void g() override;444};445void callit(C *p) {446  static_cast<B*>(p)->g();447}448// CHECK-LABEL: define dso_local void @"?callit@pr27621@@YAXPAUC@1@@Z"(ptr noundef %{{.*}})449// CHECK: %[[B_i8:.*]] = getelementptr i8, ptr %{{.*}}, i32 4450// CHECK: call x86_thiscallcc void @"?g@C@pr27621@@UAEXXZ"(ptr noundef %[[B_i8]])451}452 453namespace test6 {454class A {};455class B : virtual A {};456class C : virtual B {457  virtual void m_fn1();458  float field;459};460class D : C {461  D();462};463D::D() : C() {}464// CHECK-LABEL: define dso_local x86_thiscallcc noundef ptr @"??0D@test6@@AAE@XZ"(465// CHECK:   %[[THIS:.*]] = load ptr, ptr466// CHECK:   br i1 %{{.*}}, label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]]467 468// CHECK: %[[SKIP_VBASES]]469// CHECK:   %[[FIELD:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i32 8470// CHECK:   call void @llvm.memset.p0.i32(ptr align 4 %[[FIELD]], i8 0, i32 4, i1 false)471}472 473namespace pr36921 {474struct A {475  virtual ~A() {}476};477struct B {478  virtual ~B() {}479};480struct C : virtual B {};481struct D : virtual A, C {};482D d;483// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_GD@pr36921@@UAEPAXI@Z"(484// CHECK2:   %[[THIS_RELOAD:.*]] = load ptr, ptr485// CHECK2:   %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_RELOAD]], i32 -4486}487 488namespace issue_60465 {489// We used to assume the first argument to all destructors was the derived type490// even when there was a 'this' adjustment.491struct A {492  virtual ~A();493};494 495struct alignas(2 * sizeof(void *)) B : virtual A {496  ~B();497  void *x, *y;498};499 500B::~B() {501// The 'this' parameter should not have a type of ptr and502// must not have 'align 8', since at least B's copy of A is only 'align 4'.503// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1B@issue_60465@@UAE@XZ"(ptr noundef %this)504// CHECK:   %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i32 -12505// CHECK:   %[[X:.*]] = getelementptr inbounds nuw %"struct.issue_60465::B", ptr %[[THIS_ADJ_i8]], i32 0, i32 1506// CHECK:   store ptr null, ptr %[[X]], align 4507// CHECK:   %[[Y:.*]] = getelementptr inbounds nuw %"struct.issue_60465::B", ptr %[[THIS_ADJ_i8]], i32 0, i32 2508// CHECK:   store ptr null, ptr %[[Y]], align 8509  x = nullptr;510  y = nullptr;511}512}513