When emitting complete destructors for classes with virtual bases, compute

the offset to the virtual bases statically inside of relying on the virtual
base offsets in the object's vtable(s).  This is both more efficient and
sound against the destructor's manipulation of the vtables.

Also extract a few helper routines.

Oh and we seem to pass all tests with an optimized clang now.

llvm-svn: 96327
This commit is contained in:
John McCall 2010-02-16 04:15:37 +00:00
parent 1e3715acc3
commit 6ce747220a
2 changed files with 64 additions and 36 deletions

View File

@ -19,11 +19,11 @@ using namespace clang;
using namespace CodeGen; using namespace CodeGen;
static uint64_t static uint64_t
ComputeNonVirtualBaseClassOffset(ASTContext &Context, CXXBasePaths &Paths, ComputeNonVirtualBaseClassOffset(ASTContext &Context,
const CXXBasePath &Path,
unsigned Start) { unsigned Start) {
uint64_t Offset = 0; uint64_t Offset = 0;
const CXXBasePath &Path = Paths.front();
for (unsigned i = Start, e = Path.size(); i != e; ++i) { for (unsigned i = Start, e = Path.size(); i != e; ++i) {
const CXXBasePathElement& Element = Path[i]; const CXXBasePathElement& Element = Path[i];
@ -57,7 +57,8 @@ CodeGenModule::GetNonVirtualBaseClassOffset(const CXXRecordDecl *Class,
return 0; return 0;
} }
uint64_t Offset = ComputeNonVirtualBaseClassOffset(getContext(), Paths, 0); uint64_t Offset = ComputeNonVirtualBaseClassOffset(getContext(),
Paths.front(), 0);
if (!Offset) if (!Offset)
return 0; return 0;
@ -99,10 +100,46 @@ CodeGenModule::ComputeThunkAdjustment(const CXXRecordDecl *ClassDecl,
getVtableInfo().getVirtualBaseOffsetIndex(ClassDecl, BaseClassDecl); getVtableInfo().getVirtualBaseOffsetIndex(ClassDecl, BaseClassDecl);
uint64_t Offset = uint64_t Offset =
ComputeNonVirtualBaseClassOffset(getContext(), Paths, Start); ComputeNonVirtualBaseClassOffset(getContext(), Paths.front(), Start);
return ThunkAdjustment(Offset, VirtualOffset); return ThunkAdjustment(Offset, VirtualOffset);
} }
/// Gets the address of a virtual base class within a complete object.
/// This should only be used for (1) non-virtual bases or (2) virtual bases
/// when the type is known to be complete (e.g. in complete destructors).
///
/// The object pointed to by 'This' is assumed to be non-null.
llvm::Value *
CodeGenFunction::GetAddressOfBaseOfCompleteClass(llvm::Value *This,
bool isBaseVirtual,
const CXXRecordDecl *Derived,
const CXXRecordDecl *Base) {
// 'this' must be a pointer (in some address space) to Derived.
assert(This->getType()->isPointerTy() &&
cast<llvm::PointerType>(This->getType())->getElementType()
== ConvertType(Derived));
// Compute the offset of the virtual base.
uint64_t Offset;
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Derived);
if (isBaseVirtual)
Offset = Layout.getVBaseClassOffset(Base);
else
Offset = Layout.getBaseClassOffset(Base);
// Shift and cast down to the base type.
// TODO: for complete types, this should be possible with a GEP.
llvm::Value *V = This;
if (Offset) {
const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(getLLVMContext());
V = Builder.CreateBitCast(V, Int8PtrTy);
V = Builder.CreateConstInBoundsGEP1_64(V, Offset / 8);
}
V = Builder.CreateBitCast(V, ConvertType(Base)->getPointerTo());
return V;
}
llvm::Value * llvm::Value *
CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value, CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value,
const CXXRecordDecl *Class, const CXXRecordDecl *Class,
@ -110,7 +147,7 @@ CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value,
bool NullCheckValue) { bool NullCheckValue) {
QualType BTy = QualType BTy =
getContext().getCanonicalType( getContext().getCanonicalType(
getContext().getTypeDeclType(const_cast<CXXRecordDecl*>(BaseClass))); getContext().getTypeDeclType(BaseClass));
const llvm::Type *BasePtrTy = llvm::PointerType::getUnqual(ConvertType(BTy)); const llvm::Type *BasePtrTy = llvm::PointerType::getUnqual(ConvertType(BTy));
if (Class == BaseClass) { if (Class == BaseClass) {
@ -141,7 +178,7 @@ CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value,
} }
uint64_t Offset = uint64_t Offset =
ComputeNonVirtualBaseClassOffset(getContext(), Paths, Start); ComputeNonVirtualBaseClassOffset(getContext(), Paths.front(), Start);
if (!Offset && !VBase) { if (!Offset && !VBase) {
// Just cast back. // Just cast back.
@ -790,35 +827,18 @@ static void EmitBaseInitializer(CodeGenFunction &CGF,
if (CtorType == Ctor_Base && isBaseVirtual) if (CtorType == Ctor_Base && isBaseVirtual)
return; return;
// Compute the offset to the base; we do this directly instead of using // We can pretend to be a complete class because it only matters for
// GetAddressOfBaseClass because the class doesn't have a vtable pointer // virtual bases, and we only do virtual bases for complete ctors.
// at this point. llvm::Value *V = ThisPtr;
// FIXME: This could be refactored back into GetAddressOfBaseClass if it took V = CGF.GetAddressOfBaseOfCompleteClass(V, isBaseVirtual,
// an extra parameter for whether the derived class is the complete object ClassDecl, BaseClassDecl);
// class.
const ASTRecordLayout &Layout =
CGF.getContext().getASTRecordLayout(ClassDecl);
uint64_t Offset;
if (isBaseVirtual)
Offset = Layout.getVBaseClassOffset(BaseClassDecl);
else
Offset = Layout.getBaseClassOffset(BaseClassDecl);
const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext());
const llvm::Type *BaseClassType = CGF.ConvertType(QualType(BaseType, 0));
llvm::Value *V = CGF.Builder.CreateBitCast(ThisPtr, Int8PtrTy);
V = CGF.Builder.CreateConstInBoundsGEP1_64(V, Offset/8);
V = CGF.Builder.CreateBitCast(V, BaseClassType->getPointerTo());
CGF.EmitAggExpr(BaseInit->getInit(), V, false, false, true); CGF.EmitAggExpr(BaseInit->getInit(), V, false, false, true);
if (CGF.Exceptions && !BaseClassDecl->hasTrivialDestructor()) { if (CGF.Exceptions && !BaseClassDecl->hasTrivialDestructor()) {
// FIXME: Is this OK for C++0x delegating constructors? // FIXME: Is this OK for C++0x delegating constructors?
CodeGenFunction::EHCleanupBlock Cleanup(CGF); CodeGenFunction::EHCleanupBlock Cleanup(CGF);
llvm::Value *ThisPtr = CGF.LoadCXXThis();
llvm::Value *V = CGF.Builder.CreateBitCast(ThisPtr, Int8PtrTy);
V = CGF.Builder.CreateConstInBoundsGEP1_64(V, Offset / 8);
V = CGF.Builder.CreateBitCast(V, BaseClassType->getPointerTo());
CXXDestructorDecl *DD = BaseClassDecl->getDestructor(CGF.getContext()); CXXDestructorDecl *DD = BaseClassDecl->getDestructor(CGF.getContext());
CGF.EmitCXXDestructorCall(DD, Dtor_Base, V); CGF.EmitCXXDestructorCall(DD, Dtor_Base, V);
} }
@ -886,7 +906,6 @@ static void EmitMemberInitializer(CodeGenFunction &CGF,
/// EmitCtorPrologue - This routine generates necessary code to initialize /// EmitCtorPrologue - This routine generates necessary code to initialize
/// base classes and non-static data members belonging to this constructor. /// base classes and non-static data members belonging to this constructor.
/// FIXME: This needs to take a CXXCtorType.
void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD, void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
CXXCtorType CtorType) { CXXCtorType CtorType) {
const CXXRecordDecl *ClassDecl = CD->getParent(); const CXXRecordDecl *ClassDecl = CD->getParent();
@ -1013,15 +1032,16 @@ void CodeGenFunction::EmitDtorEpilogue(const CXXDestructorDecl *DD,
ClassDecl->vbases_rbegin(), E = ClassDecl->vbases_rend(); I != E; ++I) { ClassDecl->vbases_rbegin(), E = ClassDecl->vbases_rend(); I != E; ++I) {
const CXXBaseSpecifier &Base = *I; const CXXBaseSpecifier &Base = *I;
CXXRecordDecl *BaseClassDecl CXXRecordDecl *BaseClassDecl
= cast<CXXRecordDecl>(Base.getType()->getAs<RecordType>()->getDecl()); = cast<CXXRecordDecl>(Base.getType()->getAs<RecordType>()->getDecl());
// Ignore trivial destructors. // Ignore trivial destructors.
if (BaseClassDecl->hasTrivialDestructor()) if (BaseClassDecl->hasTrivialDestructor())
continue; continue;
const CXXDestructorDecl *D = BaseClassDecl->getDestructor(getContext()); const CXXDestructorDecl *D = BaseClassDecl->getDestructor(getContext());
llvm::Value *V = GetAddressOfBaseClass(LoadCXXThis(), llvm::Value *V = GetAddressOfBaseOfCompleteClass(LoadCXXThis(),
ClassDecl, BaseClassDecl, true,
/*NullCheckValue=*/false); ClassDecl,
BaseClassDecl);
EmitCXXDestructorCall(D, Dtor_Base, V); EmitCXXDestructorCall(D, Dtor_Base, V);
} }

View File

@ -572,6 +572,9 @@ public:
const llvm::Type *ConvertTypeForMem(QualType T); const llvm::Type *ConvertTypeForMem(QualType T);
const llvm::Type *ConvertType(QualType T); const llvm::Type *ConvertType(QualType T);
const llvm::Type *ConvertType(const TypeDecl *T) {
return ConvertType(getContext().getTypeDeclType(T));
}
/// LoadObjCSelf - Load the value of self. This function is only valid while /// LoadObjCSelf - Load the value of self. This function is only valid while
/// generating code for an Objective-C method. /// generating code for an Objective-C method.
@ -740,11 +743,16 @@ public:
/// LoadCXXVTT - Load the VTT parameter to base constructors/destructors have /// LoadCXXVTT - Load the VTT parameter to base constructors/destructors have
/// virtual bases. /// virtual bases.
llvm::Value *LoadCXXVTT(); llvm::Value *LoadCXXVTT();
/// GetAddressOfBaseOfCompleteClass - Convert the given pointer to a
/// complete class down to one of its virtual bases.
llvm::Value *GetAddressOfBaseOfCompleteClass(llvm::Value *Value,
bool IsVirtual,
const CXXRecordDecl *Derived,
const CXXRecordDecl *Base);
/// GetAddressOfBaseClass - This function will add the necessary delta to the /// GetAddressOfBaseClass - This function will add the necessary delta to the
/// load of 'this' and returns address of the base class. /// load of 'this' and returns address of the base class.
// FIXME. This currently only does a derived to non-virtual base conversion.
// Other kinds of conversions will come later.
llvm::Value *GetAddressOfBaseClass(llvm::Value *Value, llvm::Value *GetAddressOfBaseClass(llvm::Value *Value,
const CXXRecordDecl *ClassDecl, const CXXRecordDecl *ClassDecl,
const CXXRecordDecl *BaseClassDecl, const CXXRecordDecl *BaseClassDecl,