[Clang] Directly create opaque pointers

In CGTypes, directly create opaque pointers, without computing the
LLVM element type. This is not as straightforward as I though it
would be, because apparently computing the LLVM type also causes a
number of side effects.

In particular, we no longer produce diagnostics like -Wpacked for
typed (only) behind pointers, because we no longer depend on their
layout.

Differential Revision: https://reviews.llvm.org/D152505
This commit is contained in:
Nikita Popov 2023-06-09 09:57:21 +02:00
parent 742040a7f4
commit 09d6ee7657
6 changed files with 55 additions and 83 deletions

View File

@ -675,19 +675,15 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
case Type::RValueReference: { case Type::RValueReference: {
const ReferenceType *RTy = cast<ReferenceType>(Ty); const ReferenceType *RTy = cast<ReferenceType>(Ty);
QualType ETy = RTy->getPointeeType(); QualType ETy = RTy->getPointeeType();
llvm::Type *PointeeType = ConvertTypeForMem(ETy);
unsigned AS = getTargetAddressSpace(ETy); unsigned AS = getTargetAddressSpace(ETy);
ResultType = llvm::PointerType::get(PointeeType, AS); ResultType = llvm::PointerType::get(getLLVMContext(), AS);
break; break;
} }
case Type::Pointer: { case Type::Pointer: {
const PointerType *PTy = cast<PointerType>(Ty); const PointerType *PTy = cast<PointerType>(Ty);
QualType ETy = PTy->getPointeeType(); QualType ETy = PTy->getPointeeType();
llvm::Type *PointeeType = ConvertTypeForMem(ETy);
if (PointeeType->isVoidTy())
PointeeType = llvm::Type::getInt8Ty(getLLVMContext());
unsigned AS = getTargetAddressSpace(ETy); unsigned AS = getTargetAddressSpace(ETy);
ResultType = llvm::PointerType::get(PointeeType, AS); ResultType = llvm::PointerType::get(getLLVMContext(), AS);
break; break;
} }
@ -764,15 +760,9 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
break; break;
} }
case Type::ObjCObjectPointer: { case Type::ObjCObjectPointer:
// Protocol qualifications do not influence the LLVM type, we just return a ResultType = llvm::PointerType::getUnqual(getLLVMContext());
// pointer to the underlying interface type. We don't need to worry about
// recursive conversion.
llvm::Type *T =
ConvertTypeForMem(cast<ObjCObjectPointerType>(Ty)->getPointeeType());
ResultType = T->getPointerTo();
break; break;
}
case Type::Enum: { case Type::Enum: {
const EnumDecl *ED = cast<EnumType>(Ty)->getDecl(); const EnumDecl *ED = cast<EnumType>(Ty)->getDecl();
@ -786,18 +776,15 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
} }
case Type::BlockPointer: { case Type::BlockPointer: {
const QualType FTy = cast<BlockPointerType>(Ty)->getPointeeType();
llvm::Type *PointeeType = CGM.getLangOpts().OpenCL
? CGM.getGenericBlockLiteralType()
: ConvertTypeForMem(FTy);
// Block pointers lower to function type. For function type, // Block pointers lower to function type. For function type,
// getTargetAddressSpace() returns default address space for // getTargetAddressSpace() returns default address space for
// function pointer i.e. program address space. Therefore, for block // function pointer i.e. program address space. Therefore, for block
// pointers, it is important to pass the pointee AST address space when // pointers, it is important to pass the pointee AST address space when
// calling getTargetAddressSpace(), to ensure that we get the LLVM IR // calling getTargetAddressSpace(), to ensure that we get the LLVM IR
// address space for data pointers and not function pointers. // address space for data pointers and not function pointers.
const QualType FTy = cast<BlockPointerType>(Ty)->getPointeeType();
unsigned AS = Context.getTargetAddressSpace(FTy.getAddressSpace()); unsigned AS = Context.getTargetAddressSpace(FTy.getAddressSpace());
ResultType = llvm::PointerType::get(PointeeType, AS); ResultType = llvm::PointerType::get(getLLVMContext(), AS);
break; break;
} }

View File

@ -31,20 +31,20 @@ void test_transpose_template1() {
void test_transpose_template2(MyMatrix<double, 7, 6> &M) { void test_transpose_template2(MyMatrix<double, 7, 6> &M) {
// CHECK-LABEL: define{{.*}} void @_Z24test_transpose_template2R8MyMatrixIdLj7ELj6EE( // CHECK-LABEL: define{{.*}} void @_Z24test_transpose_template2R8MyMatrixIdLj7ELj6EE(
// CHECK: call void @_Z9transposeIdLj7ELj6EE8MyMatrixIT_XT1_EXT0_EERKS0_IS1_XT0_EXT1_EE(ptr sret(%struct.MyMatrix.2) align 8 %ref.tmp1, ptr noundef nonnull align 8 dereferenceable(336) %0) // CHECK: call void @_Z9transposeIdLj7ELj6EE8MyMatrixIT_XT1_EXT0_EERKS0_IS1_XT0_EXT1_EE(ptr sret(%struct.MyMatrix.1) align 8 %ref.tmp1, ptr noundef nonnull align 8 dereferenceable(336) %0)
// CHECK-NEXT: call void @_Z9transposeIdLj6ELj7EE8MyMatrixIT_XT1_EXT0_EERKS0_IS1_XT0_EXT1_EE(ptr sret(%struct.MyMatrix.1) align 8 %ref.tmp, ptr noundef nonnull align 8 dereferenceable(336) %ref.tmp1) // CHECK-NEXT: call void @_Z9transposeIdLj6ELj7EE8MyMatrixIT_XT1_EXT0_EERKS0_IS1_XT0_EXT1_EE(ptr sret(%struct.MyMatrix.2) align 8 %ref.tmp, ptr noundef nonnull align 8 dereferenceable(336) %ref.tmp1)
// CHECK-NEXT: call void @_Z9transposeIdLj7ELj6EE8MyMatrixIT_XT1_EXT0_EERKS0_IS1_XT0_EXT1_EE(ptr sret(%struct.MyMatrix.2) align 8 %M2_t, ptr noundef nonnull align 8 dereferenceable(336) %ref.tmp) // CHECK-NEXT: call void @_Z9transposeIdLj7ELj6EE8MyMatrixIT_XT1_EXT0_EERKS0_IS1_XT0_EXT1_EE(ptr sret(%struct.MyMatrix.1) align 8 %M2_t, ptr noundef nonnull align 8 dereferenceable(336) %ref.tmp)
// CHECK-LABEL: define linkonce_odr void @_Z9transposeIdLj7ELj6EE8MyMatrixIT_XT1_EXT0_EERKS0_IS1_XT0_EXT1_EE( // CHECK-LABEL: define linkonce_odr void @_Z9transposeIdLj7ELj6EE8MyMatrixIT_XT1_EXT0_EERKS0_IS1_XT0_EXT1_EE(
// CHECK: [[M:%.*]] = load <42 x double>, ptr {{.*}}, align 8 // CHECK: [[M:%.*]] = load <42 x double>, ptr {{.*}}, align 8
// CHECK-NEXT: [[M_T:%.*]] = call <42 x double> @llvm.matrix.transpose.v42f64(<42 x double> [[M]], i32 7, i32 6) // CHECK-NEXT: [[M_T:%.*]] = call <42 x double> @llvm.matrix.transpose.v42f64(<42 x double> [[M]], i32 7, i32 6)
// CHECK-NEXT: [[RES_ADDR:%.*]] = getelementptr inbounds %struct.MyMatrix.2, ptr %agg.result, i32 0, i32 0 // CHECK-NEXT: [[RES_ADDR:%.*]] = getelementptr inbounds %struct.MyMatrix.1, ptr %agg.result, i32 0, i32 0
// CHECK-NEXT: store <42 x double> [[M_T]], ptr [[RES_ADDR]], align 8 // CHECK-NEXT: store <42 x double> [[M_T]], ptr [[RES_ADDR]], align 8
// CHECK-LABEL: define linkonce_odr void @_Z9transposeIdLj6ELj7EE8MyMatrixIT_XT1_EXT0_EERKS0_IS1_XT0_EXT1_EE( // CHECK-LABEL: define linkonce_odr void @_Z9transposeIdLj6ELj7EE8MyMatrixIT_XT1_EXT0_EERKS0_IS1_XT0_EXT1_EE(
// CHECK: [[M:%.*]] = load <42 x double>, ptr {{.*}}, align 8 // CHECK: [[M:%.*]] = load <42 x double>, ptr {{.*}}, align 8
// CHECK-NEXT: [[M_T:%.*]] = call <42 x double> @llvm.matrix.transpose.v42f64(<42 x double> [[M]], i32 6, i32 7) // CHECK-NEXT: [[M_T:%.*]] = call <42 x double> @llvm.matrix.transpose.v42f64(<42 x double> [[M]], i32 6, i32 7)
// CHECK-NEXT: [[RES_ADDR:%.*]] = getelementptr inbounds %struct.MyMatrix.1, ptr %agg.result, i32 0, i32 0 // CHECK-NEXT: [[RES_ADDR:%.*]] = getelementptr inbounds %struct.MyMatrix.2, ptr %agg.result, i32 0, i32 0
// CHECK-NEXT: store <42 x double> [[M_T]], ptr [[RES_ADDR]], align 8 // CHECK-NEXT: store <42 x double> [[M_T]], ptr [[RES_ADDR]], align 8
MyMatrix<double, 6, 7> M2_t = transpose(transpose(transpose(M))); MyMatrix<double, 6, 7> M2_t = transpose(transpose(transpose(M)));

View File

@ -184,6 +184,6 @@ struct S30_use { // abi15-warning {{packed attribute is unnecessary for 'S30_use
static_assert(sizeof(S30_use) == 3, ""); static_assert(sizeof(S30_use) == 3, "");
// The warnings are emitted when the layout of the structs is computed, so we have to use them. // The warnings are emitted when the layout of the structs is computed, so we have to use them.
void f(S1*, S2*, S3*, S4*, S5*, S6*, S7*, S8*, S9*, S10*, S11*, S12*, S13*, void f(S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13,
S14*, S15*, S16*, S17*, S18*, S19*, S20*, S21*, S22*, S23*, S24*, S25*, S14, S15, S16, S17, S18, S19, S20, S21, S22, S23, S24, S25,
S26*, S27*, S28*, S29*){} S26, S27, S28, S29){}

View File

@ -423,9 +423,10 @@ struct CompareDifferentFieldInIndirectStruct compareIndirectStruct;
// expected-error@second-nested-struct.h:* {{'IndirectStruct::mismatchingField' from module 'Second' is not present in definition of 'struct IndirectStruct' in module 'First.Hidden'}} // expected-error@second-nested-struct.h:* {{'IndirectStruct::mismatchingField' from module 'Second' is not present in definition of 'struct IndirectStruct' in module 'First.Hidden'}}
// expected-note@first-nested-struct.h:* {{declaration of 'mismatchingField' does not match}} // expected-note@first-nested-struct.h:* {{declaration of 'mismatchingField' does not match}}
#elif defined(CASE3) #elif defined(CASE3)
// This currently doesn't produce an error, because there is no dependency
// on the layout of DirectStruct.
// expected-no-diagnostics
struct CompareIndirectStructPointer compareIndirectStructPointer; struct CompareIndirectStructPointer compareIndirectStructPointer;
// expected-error@second-nested-struct.h:* {{'IndirectStruct::mismatchingField' from module 'Second' is not present in definition of 'struct IndirectStruct' in module 'First.Hidden'}}
// expected-note@first-nested-struct.h:* {{declaration of 'mismatchingField' does not match}}
#endif #endif
//--- include/first-anonymous.h //--- include/first-anonymous.h

View File

@ -33,7 +33,7 @@
// RUN: not %clang_cc1 -triple %itanium_abi_triple -DINSTANTIATION -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -emit-llvm-only %s 2> %t.stderr // RUN: not %clang_cc1 -triple %itanium_abi_triple -DINSTANTIATION -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -emit-llvm-only %s 2> %t.stderr
// RUN: grep 'orig_sub2_1' %t.stderr // RUN: grep 'orig_sub2_1' %t.stderr
void qq(orig_sub*) {all();} void qq(orig_sub) {all();}
#ifdef REDECL #ifdef REDECL
float foo() {return 0;} float foo() {return 0;}

View File

@ -147,16 +147,16 @@ int main() {
// This avoid "Can't yet mangle constructors!" for MS ABI. // This avoid "Can't yet mangle constructors!" for MS ABI.
C* c; C* c;
c->foo(); c->foo();
DerivedStruct* v; DerivedStruct v;
H* g; H g;
BaseStruct* u; BaseStruct* u;
I* i; I i;
N* n; N n;
O* o; O o;
P* p; P p;
R* r; R r;
sd *h; sd h;
EV *j; EV j;
return 0; return 0;
} }
@ -206,15 +206,6 @@ int main() {
// CHECK-NEXT: sizeof=80, align=8 // CHECK-NEXT: sizeof=80, align=8
// CHECK-NEXT: nvsize=64, nvalign=8 // CHECK-NEXT: nvsize=64, nvalign=8
// CHECK: %class.D = type { ptr, double }
// CHECK: %class.B = type { ptr, i32 }
// CHECK: %class.A = type { %class.B, i32, i8 }
// CHECK: %class.C = type { %class.D, %class.B, ptr, double, i32, double, i32, [4 x i8], %class.A }
// CHECK: %class.C.base = type { %class.D, %class.B, ptr, double, i32, double, i32 }
// CHECK-LABEL: 0 | struct BaseStruct{{$}} // CHECK-LABEL: 0 | struct BaseStruct{{$}}
// CHECK-NEXT: 0 | double v0 // CHECK-NEXT: 0 | double v0
// CHECK-NEXT: 8 | float v1 // CHECK-NEXT: 8 | float v1
@ -239,8 +230,6 @@ int main() {
// CHECK-NEXT: sizeof=96, align=8 // CHECK-NEXT: sizeof=96, align=8
// CHECK-NEXT: nvsize=96, nvalign=8 // CHECK-NEXT: nvsize=96, nvalign=8
// CHECK: %struct.BaseStruct = type { double, float, %class.C }
// CHECK-LABEL: 0 | struct DerivedStruct{{$}} // CHECK-LABEL: 0 | struct DerivedStruct{{$}}
// CHECK-NEXT: 0 | struct BaseStruct (base) // CHECK-NEXT: 0 | struct BaseStruct (base)
// CHECK-NEXT: 0 | double v0 // CHECK-NEXT: 0 | double v0
@ -267,8 +256,6 @@ int main() {
// CHECK-NEXT: sizeof=104, align=8 // CHECK-NEXT: sizeof=104, align=8
// CHECK-NEXT: nvsize=104, nvalign=8 // CHECK-NEXT: nvsize=104, nvalign=8
// CHECK: %struct.DerivedStruct = type { %struct.BaseStruct, i32 }
// CHECK-LABEL:0 | struct G // CHECK-LABEL:0 | struct G
// CHECK-NEXT: 0 | int g_field // CHECK-NEXT: 0 | int g_field
// CHECK-NEXT: sizeof=4, align=4 // CHECK-NEXT: sizeof=4, align=4
@ -284,8 +271,6 @@ int main() {
// CHECK-NEXT: sizeof=24, align=8 // CHECK-NEXT: sizeof=24, align=8
// CHECK-NEXT: nvsize=8, nvalign=8 // CHECK-NEXT: nvsize=8, nvalign=8
// CHECK: %struct.H = type { %struct.G, ptr, %class.D }
// CHECK-LABEL: 0 | struct I{{$}} // CHECK-LABEL: 0 | struct I{{$}}
// CHECK-NEXT: 0 | (I vftable pointer) // CHECK-NEXT: 0 | (I vftable pointer)
// CHECK-NEXT: 8 | (I vbtable pointer) // CHECK-NEXT: 8 | (I vbtable pointer)
@ -296,9 +281,6 @@ int main() {
// CHECK-NEXT: sizeof=40, align=8 // CHECK-NEXT: sizeof=40, align=8
// CHECK-NEXT: nvsize=24, nvalign=8 // CHECK-NEXT: nvsize=24, nvalign=8
// CHECK: %struct.I = type { ptr, [4 x i8], ptr, double, %class.D }
// CHECK: %struct.I.base = type { ptr, [4 x i8], ptr, double }
// CHECK-LABEL: 0 | struct L{{$}} // CHECK-LABEL: 0 | struct L{{$}}
// CHECK-NEXT: 0 | int l // CHECK-NEXT: 0 | int l
// CHECK-NEXT: sizeof=4, align=4 // CHECK-NEXT: sizeof=4, align=4
@ -316,9 +298,6 @@ int main() {
// CHECK-NEXT: 8 | int k // CHECK-NEXT: 8 | int k
// CHECK-NEXT: sizeof=12, align=4 // CHECK-NEXT: sizeof=12, align=4
//CHECK: %struct.M = type { ptr, i32, %struct.K }
//CHECK: %struct.M.base = type { ptr, i32 }
// CHECK-LABEL: 0 | struct N{{$}} // CHECK-LABEL: 0 | struct N{{$}}
// CHECK-NEXT: 0 | (N vftable pointer) // CHECK-NEXT: 0 | (N vftable pointer)
// CHECK-NEXT: 4 | struct L (base) // CHECK-NEXT: 4 | struct L (base)
@ -331,8 +310,6 @@ int main() {
// CHECK-NEXT: sizeof=20, align=4 // CHECK-NEXT: sizeof=20, align=4
// CHECK-NEXT: nvsize=16, nvalign=4 // CHECK-NEXT: nvsize=16, nvalign=4
//CHECK: %struct.N = type { ptr, %struct.L, %struct.M.base, %struct.K }
// CHECK-LABEL: 0 | struct O{{$}} // CHECK-LABEL: 0 | struct O{{$}}
// CHECK-NEXT: 0 | (O vftable pointer) // CHECK-NEXT: 0 | (O vftable pointer)
// CHECK-NEXT: 8 | struct H (base) // CHECK-NEXT: 8 | struct H (base)
@ -347,9 +324,6 @@ int main() {
// CHECK-NEXT: | [sizeof=40, align=8 // CHECK-NEXT: | [sizeof=40, align=8
// CHECK-NEXT: | nvsize=24, nvalign=8] // CHECK-NEXT: | nvsize=24, nvalign=8]
// CHECK: struct.O = type { ptr, [4 x i8], %struct.H.base, %struct.G, %class.D }
// CHECK: struct.O.base = type { ptr, [4 x i8], %struct.H.base, %struct.G, [4 x i8] }
// CHECK-LABEL: 0 | struct P{{$}} // CHECK-LABEL: 0 | struct P{{$}}
// CHECK-NEXT: 0 | struct M (base) // CHECK-NEXT: 0 | struct M (base)
// CHECK-NEXT: 0 | (M vbtable pointer) // CHECK-NEXT: 0 | (M vbtable pointer)
@ -362,14 +336,10 @@ int main() {
// CHECK-NEXT: sizeof=20, align=4 // CHECK-NEXT: sizeof=20, align=4
// CHECK-NEXT: nvsize=12, nvalign=4 // CHECK-NEXT: nvsize=12, nvalign=4
//CHECK: %struct.P = type { %struct.M.base, i32, %struct.K, %struct.L }
// CHECK-LABEL: 0 | struct R (empty){{$}} // CHECK-LABEL: 0 | struct R (empty){{$}}
// CHECK-NEXT: sizeof=1, align=1 // CHECK-NEXT: sizeof=1, align=1
// CHECK-NEXT: nvsize=0, nvalign=1 // CHECK-NEXT: nvsize=0, nvalign=1
//CHECK: %struct.R = type { i8 }
// CHECK-LABEL: 0 | struct f{{$}} // CHECK-LABEL: 0 | struct f{{$}}
// CHECK-NEXT: 0 | (f vftable pointer) // CHECK-NEXT: 0 | (f vftable pointer)
// CHECK-NEXT: sizeof=4, align=4 // CHECK-NEXT: sizeof=4, align=4
@ -419,12 +389,6 @@ int main() {
// CHECK-NEXT: sizeof=48, align=4 // CHECK-NEXT: sizeof=48, align=4
// CHECK-NEXT: nvsize=12, nvalign=4 // CHECK-NEXT: nvsize=12, nvalign=4
// CHECK: %struct.f = type { ptr }
// CHECK: %struct.s = type { ptr, ptr, i32, i32, %struct.f }
// CHECK: %class.IA = type { ptr }
// CHECK: %class.ICh = type { ptr, ptr, i32, %class.IA }
// CHECK: %struct.sd = type { ptr, i32, i8, i32, %struct.f, %struct.s.base, i32, %class.IA, %class.ICh.base }
// CHECK-LABEL: 0 | struct AV{{$}} // CHECK-LABEL: 0 | struct AV{{$}}
// CHECK-NEXT: 0 | (AV vftable pointer) // CHECK-NEXT: 0 | (AV vftable pointer)
// CHECK-NEXT: sizeof=4, align=4 // CHECK-NEXT: sizeof=4, align=4
@ -445,11 +409,6 @@ int main() {
// CHECK-NEXT: sizeof=12, align=4 // CHECK-NEXT: sizeof=12, align=4
// CHECK-NEXT: nvsize=4, nvalign=4 // CHECK-NEXT: nvsize=4, nvalign=4
// CHECK: %struct.AV = type { ptr }
// CHECK: %struct.BV = type { %struct.AV }
// CHECK: %struct.CV = type { ptr, i32, %struct.BV }
// CHECK: %struct.CV.base = type { ptr }
// CHECK-LABEL: 0 | struct DV{{$}} // CHECK-LABEL: 0 | struct DV{{$}}
// CHECK-NEXT: 0 | struct BV (primary base) // CHECK-NEXT: 0 | struct BV (primary base)
// CHECK-NEXT: 0 | struct AV (primary base) // CHECK-NEXT: 0 | struct AV (primary base)
@ -457,8 +416,6 @@ int main() {
// CHECK-NEXT: sizeof=4, align=4 // CHECK-NEXT: sizeof=4, align=4
// CHECK-NEXT: nvsize=4, nvalign=4 // CHECK-NEXT: nvsize=4, nvalign=4
// CHECK: %struct.DV = type { %struct.BV }
// CHECK-LABEL: 0 | struct EV{{$}} // CHECK-LABEL: 0 | struct EV{{$}}
// CHECK-NEXT: 0 | struct DV (primary base) // CHECK-NEXT: 0 | struct DV (primary base)
// CHECK-NEXT: 0 | struct BV (primary base) // CHECK-NEXT: 0 | struct BV (primary base)
@ -473,6 +430,33 @@ int main() {
// CHECK-NEXT: sizeof=16, align=4 // CHECK-NEXT: sizeof=16, align=4
// CHECK-NEXT: nvsize=8, nvalign=4 // CHECK-NEXT: nvsize=8, nvalign=4
// CHECK: %class.D = type { ptr, double }
// CHECK: %class.B = type { ptr, i32 }
// CHECK: %class.A = type { %class.B, i32, i8 }
// CHECK: %class.C = type { %class.D, %class.B, ptr, double, i32, double, i32, [4 x i8], %class.A }
// CHECK: %class.C.base = type { %class.D, %class.B, ptr, double, i32, double, i32 }
// CHECK: %struct.BaseStruct = type { double, float, %class.C }
// CHECK: %struct.DerivedStruct = type { %struct.BaseStruct, i32 }
// CHECK: %struct.H = type { %struct.G, ptr, %class.D }
// CHECK: %struct.I = type { ptr, [4 x i8], ptr, double, %class.D }
// CHECK: %struct.I.base = type { ptr, [4 x i8], ptr, double }
// CHECK: %struct.M = type { ptr, i32, %struct.K }
// CHECK: %struct.M.base = type { ptr, i32 }
// CHECK: %struct.N = type { ptr, %struct.L, %struct.M.base, %struct.K }
// CHECK: %struct.O = type { ptr, [4 x i8], %struct.H.base, %struct.G, %class.D }
// CHECK: %struct.O.base = type { ptr, [4 x i8], %struct.H.base, %struct.G, [4 x i8] }
// CHECK: %struct.P = type { %struct.M.base, i32, %struct.K, %struct.L }
// CHECK: %struct.R = type { i8 }
// CHECK: %struct.f = type { ptr }
// CHECK: %struct.s = type { ptr, ptr, i32, i32, %struct.f }
// CHECK: %class.IA = type { ptr }
// CHECK: %class.ICh = type { ptr, ptr, i32, %class.IA }
// CHECK: %struct.sd = type { ptr, i32, i8, i32, %struct.f, %struct.s.base, i32, %class.IA, %class.ICh.base }
// CHECK: %struct.AV = type { ptr }
// CHECK: %struct.BV = type { %struct.AV }
// CHECK: %struct.CV = type { ptr, i32, %struct.BV }
// CHECK: %struct.CV.base = type { ptr }
// CHECK: %struct.DV = type { %struct.BV }
// CHECK: %struct.EV = type { %struct.DV, %struct.CV.base, i32, %struct.BV } // CHECK: %struct.EV = type { %struct.DV, %struct.CV.base, i32, %struct.BV }
// CHECK: %struct.EV.base = type { %struct.DV, %struct.CV.base } // CHECK: %struct.EV.base = type { %struct.DV, %struct.CV.base }
@ -482,7 +466,7 @@ namespace test1 {
struct A { virtual void foo(); }; struct A { virtual void foo(); };
struct B : A {}; struct B : A {};
struct C : virtual A, virtual B { C(); virtual void foo(); }; struct C : virtual A, virtual B { C(); virtual void foo(); };
void test() { C *c; } void test() { C c; }
// CHECK-LABEL: 0 | struct test1::C{{$}} // CHECK-LABEL: 0 | struct test1::C{{$}}
// CHECK-NEXT: 0 | (C vbtable pointer) // CHECK-NEXT: 0 | (C vbtable pointer)