| // RUN: %clang_cc1 -triple x86_64-windows-msvc -std=c++17 -emit-llvm -o - %s | FileCheck %s | |
| // Test for the fix in EmitNullBaseClassInitialization where the calculation | |
| // of SplitAfterSize was incorrect when multiple vbptrs are present. | |
| namespace test { | |
| class Base { | |
| public: | |
| virtual ~Base() {} | |
| }; | |
| class Left : public virtual Base { | |
| }; | |
| class Right : public virtual Base { | |
| }; | |
| class Diamond : public Left, public Right { | |
| }; | |
| // Test 1: Diamond inheritance in a template that triggers the bug | |
| template<typename T> | |
| class Derived : public Diamond { | |
| public: | |
| // CHECK-LABEL: define {{.*}} @"??0?$Derived@H@test@@QEAA@XZ" | |
| // Layout of Derived<int>: | |
| // offset 0: vbptr for Left (8 bytes) | |
| // offset 8: vbptr for Right (8 bytes) | |
| // offset 16+: virtual base Base | |
| // CHECK: call {{.*}} @"??0Diamond@test@@QEAA@XZ" | |
| // EmitNullBaseClassInitialization now correctly calculates memory regions | |
| // around the vbptrs without hitting negative size assertion | |
| // No memset is generated since there are no gaps to zero | |
| // CHECK-NOT: call void @llvm.memset | |
| // CHECK: ret | |
| Derived() : Diamond() {} | |
| }; | |
| // Explicit instantiation to trigger code generation | |
| template class Derived<int>; | |
| // Test 2: Diamond in a template, with data members (calls memset) | |
| class DiamondWithData : public Left, public Right { | |
| public: | |
| int x; | |
| int y; | |
| }; | |
| template<typename T> | |
| class DerivedWithData : public DiamondWithData { | |
| public: | |
| DerivedWithData(); | |
| }; | |
| // CHECK-LABEL: define {{.*}} @"??0?$DerivedWithData@H@test@@QEAA@XZ" | |
| // | |
| // Layout of DerivedWithData<int>: | |
| // offset 0: vbptr for Left (8 bytes) | |
| // offset 8: vbptr for Right (8 bytes) | |
| // offset 16: x (4 bytes) | |
| // offset 20: y (4 bytes) | |
| // offset 24+: virtual base Base | |
| // | |
| // EmitNullBaseClassInitialization zero-initializes the data members [16, 24) | |
| // while skipping both vbptrs [0, 16) | |
| // | |
| // memset zeros 8 bytes for x and y at offset 16 | |
| // CHECK: call void @llvm.memset.p0.i64(ptr {{.*}}, i8 0, i64 8, i1 false) | |
| // CHECK: ret | |
| template<typename T> | |
| DerivedWithData<T>::DerivedWithData() : DiamondWithData() { | |
| } | |
| template struct DerivedWithData<int>; | |
| // Test 3: Three vbptrs test case | |
| // Three separate classes that virtually inherit from Base | |
| class Middle : public virtual Base { | |
| }; | |
| // TriDiamond has three vbptrs - one from each base class | |
| class TriDiamond : public Left, public Middle, public Right { | |
| }; | |
| // Test 3a: Template instantiation with three vbptrs (no data members) | |
| template<typename T> | |
| class TriDerived : public TriDiamond { | |
| public: | |
| // CHECK-LABEL: define {{.*}} @"??0?$TriDerived@H@test@@QEAA@XZ" | |
| // Layout of TriDerived<int>: | |
| // offset 0: vbptr for Left (8 bytes) | |
| // offset 8: vbptr for Middle (8 bytes) | |
| // offset 16: vbptr for Right (8 bytes) | |
| // offset 24+: virtual base Base | |
| // CHECK: call {{.*}} @"??0TriDiamond@test@@QEAA@XZ" | |
| // No memset is generated since there are no gaps to zero | |
| // CHECK-NOT: call void @llvm.memset | |
| // CHECK: ret | |
| TriDerived() : TriDiamond() {} | |
| }; | |
| // Explicit instantiation to trigger code generation | |
| template class TriDerived<int>; | |
| // Test 3b: Three vbptrs with data members (calls memset) | |
| class TriDiamondWithData : public Left, public Middle, public Right { | |
| public: | |
| int a; | |
| int b; | |
| }; | |
| template<typename T> | |
| class TriDerivedWithData : public TriDiamondWithData { | |
| public: | |
| TriDerivedWithData(); | |
| }; | |
| // CHECK-LABEL: define {{.*}} @"??0?$TriDerivedWithData@H@test@@QEAA@XZ" | |
| // | |
| // Layout of TriDerivedWithData<int>: | |
| // offset 0: vbptr for Left (8 bytes) | |
| // offset 8: vbptr for Middle (8 bytes) | |
| // offset 16: vbptr for Right (8 bytes) | |
| // offset 24: a (4 bytes) | |
| // offset 28: b (4 bytes) | |
| // offset 32+: virtual base Base | |
| // memset zeros 8 bytes [24, 32) | |
| // CHECK: call void @llvm.memset.p0.i64(ptr {{.*}}, i8 0, i64 8, i1 false) | |
| // CHECK: ret | |
| template<typename T> | |
| TriDerivedWithData<T>::TriDerivedWithData() : TriDiamondWithData() { | |
| } | |
| template struct TriDerivedWithData<int>; | |
| // Test 4: Another case which triggers the bug (similar to Test 1, non-template) | |
| class Interface { | |
| public: | |
| virtual ~Interface() {} | |
| }; | |
| class Base1If : public virtual Interface { | |
| }; | |
| class Base2If : public virtual Interface { | |
| }; | |
| class BaseIf : public Base1If, public Base2If { | |
| }; | |
| class DerivedClass : public BaseIf { | |
| public: | |
| // CHECK-LABEL: define {{.*}} @"??0DerivedClass@test@@QEAA@XZ" | |
| // Layout of DerivedClass: | |
| // offset 0: vbptr for Base1If (8 bytes) | |
| // offset 8: vbptr for Base2If (8 bytes) | |
| // offset 16+: virtual base Interface | |
| // CHECK: call {{.*}} @"??0BaseIf@test@@QEAA@XZ" | |
| // EmitNullBaseClassInitialization now correctly calculates memory regions | |
| // around the vbptrs without hitting negative size assertion | |
| // No memset is generated since there are no gaps to zero | |
| // CHECK-NOT: call void @llvm.memset | |
| // CHECK: ret | |
| DerivedClass() | |
| : BaseIf() | |
| { } | |
| }; | |
| // Instantiate to trigger code generation | |
| DerivedClass d; | |
| // Test 4c: Non-template version with three vbptrs | |
| class TriConcreteClass : public TriDiamond { | |
| public: | |
| // CHECK-LABEL: define {{.*}} @"??0TriConcreteClass@test@@QEAA@XZ" | |
| // Layout of TriConcreteClass: | |
| // offset 0: vbptr for Left (8 bytes) | |
| // offset 8: vbptr for Middle (8 bytes) | |
| // offset 16: vbptr for Right (8 bytes) | |
| // offset 24+: virtual base Base | |
| // CHECK: call {{.*}} @"??0TriDiamond@test@@QEAA@XZ" | |
| // No memset is generated since there are no gaps to zero | |
| // CHECK-NOT: call void @llvm.memset | |
| // CHECK: ret | |
| TriConcreteClass() : TriDiamond() {} | |
| }; | |
| // Instantiate to trigger code generation | |
| TriConcreteClass tc; | |
| } // namespace test |