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