diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/VTableBuilder.cpp | 18 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGClass.cpp | 9 | ||||
-rw-r--r-- | clang/lib/CodeGen/MicrosoftCXXABI.cpp | 83 |
3 files changed, 88 insertions, 22 deletions
diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp index 045b7f1a42d..2da089186c9 100644 --- a/clang/lib/AST/VTableBuilder.cpp +++ b/clang/lib/AST/VTableBuilder.cpp @@ -2668,11 +2668,6 @@ CharUnits VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD, BaseSubobject Base, FinalOverriders::OverriderInfo Overrider) { - // Complete object virtual destructors are always emitted in the most derived - // class, thus don't have this offset. - if (isa<CXXDestructorDecl>(MD)) - return CharUnits(); - InitialOverriddenDefinitionCollector Collector; visitAllOverriddenMethods(MD, Collector); @@ -2690,6 +2685,7 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD, I != E; ++I) { const CXXBasePath &Path = (*I); CharUnits ThisOffset = Base.getBaseOffset(); + bool SeenVBase = false; // For each path from the overrider to the parents of the overridden methods, // traverse the path, calculating the this offset in the most derived class. @@ -2701,6 +2697,7 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD, const ASTRecordLayout &Layout = Context.getASTRecordLayout(PrevRD); if (Element.Base->isVirtual()) { + SeenVBase = true; if (Overrider.Method->getParent() == PrevRD) { // This one's interesting. If the final overrider is in a vbase B of the // most derived class and it overrides a method of the B's own vbase A, @@ -2713,11 +2710,22 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD, } else { ThisOffset = MostDerivedClassLayout.getVBaseClassOffset(CurRD); } + + // A virtual destructor of a virtual base takes the address of the + // virtual base subobject as the "this" argument. + if (isa<CXXDestructorDecl>(MD)) + break; } else { ThisOffset += Layout.getBaseClassOffset(CurRD); } } + // If a "Base" class has at least one non-virtual base with a virtual + // destructor, the "Base" virtual destructor will take the address of the + // "Base" subobject as the "this" argument. + if (!SeenVBase && isa<CXXDestructorDecl>(MD)) + return Base.getBaseOffset(); + if (Ret > ThisOffset || First) { First = false; Ret = ThisOffset; diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 91f42e2f579..d6907840c94 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -1829,8 +1829,8 @@ void CodeGenFunction::EmitCXXDestructorCall(const CXXDestructorDecl *DD, bool ForVirtualBase, bool Delegating, llvm::Value *This) { - llvm::Value *VTT = GetVTTParameter(GlobalDecl(DD, Type), - ForVirtualBase, Delegating); + GlobalDecl GD(DD, Type); + llvm::Value *VTT = GetVTTParameter(GD, ForVirtualBase, Delegating); llvm::Value *Callee = 0; if (getLangOpts().AppleKext) Callee = BuildAppleKextVirtualDestructorCall(DD, Type, @@ -1838,7 +1838,10 @@ void CodeGenFunction::EmitCXXDestructorCall(const CXXDestructorDecl *DD, if (!Callee) Callee = CGM.GetAddrOfCXXDestructor(DD, Type); - + + if (DD->isVirtual()) + This = CGM.getCXXABI().adjustThisArgumentForVirtualCall(*this, GD, This); + // FIXME: Provide a source location here. EmitCXXMemberCall(DD, SourceLocation(), Callee, ReturnValueSlot(), This, VTT, getContext().getPointerType(getContext().VoidPtrTy), diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 9d59160cec8..c1ea60d09a4 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -563,21 +563,65 @@ llvm::Value *MicrosoftCXXABI::adjustThisArgumentForVirtualCall( CodeGenFunction &CGF, GlobalDecl GD, llvm::Value *This) { GD = GD.getCanonicalDecl(); const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()); - if (isa<CXXDestructorDecl>(MD)) - return This; - + // FIXME: consider splitting the vdtor vs regular method code into two + // functions. + + GlobalDecl LookupGD = GD; + if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) { + // Complete dtors take a pointer to the complete object, + // thus don't need adjustment. + if (GD.getDtorType() == Dtor_Complete) + return This; + + // There's only Dtor_Deleting in vftable but it shares the this adjustment + // with the base one, so look up the deleting one instead. + LookupGD = GlobalDecl(DD, Dtor_Deleting); + } MicrosoftVFTableContext::MethodVFTableLocation ML = - CGM.getVFTableContext().getMethodVFTableLocation(GD); + CGM.getVFTableContext().getMethodVFTableLocation(LookupGD); unsigned AS = cast<llvm::PointerType>(This->getType())->getAddressSpace(); llvm::Type *charPtrTy = CGF.Int8Ty->getPointerTo(AS); + CharUnits StaticOffset = ML.VFTableOffset; if (ML.VBase) { - This = CGF.Builder.CreateBitCast(This, charPtrTy); - llvm::Value *VBaseOffset = CGM.getCXXABI() - .GetVirtualBaseClassOffset(CGF, This, MD->getParent(), ML.VBase); - This = CGF.Builder.CreateInBoundsGEP(This, VBaseOffset); + bool AvoidVirtualOffset = false; + if (isa<CXXDestructorDecl>(MD) && GD.getDtorType() == Dtor_Base) { + // A base destructor can only be called from a complete destructor of the + // same record type or another destructor of a more derived type. + const CXXRecordDecl *CurRD = + cast<CXXDestructorDecl>(CGF.CurGD.getDecl())->getParent(); + + if (MD->getParent() == CurRD) { + assert(CGF.CurGD.getDtorType() == Dtor_Complete); + // We're calling the main base dtor from a complete dtor, so we know the + // "this" offset statically. + AvoidVirtualOffset = true; + } else { + // Let's see if we try to call a destructor of a non-virtual base. + for (CXXRecordDecl::base_class_const_iterator I = CurRD->bases_begin(), + E = CurRD->bases_end(); I != E; ++I) { + if (I->getType()->getAsCXXRecordDecl() != MD->getParent()) + continue; + // If we call a base destructor for a non-virtual base, we statically + // know where it expects the vfptr and "this" to be. + AvoidVirtualOffset = true; + break; + } + } + } + + if (AvoidVirtualOffset) { + const ASTRecordLayout &Layout = + CGF.getContext().getASTRecordLayout(MD->getParent()); + // This reflects the logic from VFTableBuilder::ComputeThisOffset(). + StaticOffset += Layout.getVBaseClassOffset(ML.VBase); + } else { + This = CGF.Builder.CreateBitCast(This, charPtrTy); + llvm::Value *VBaseOffset = CGM.getCXXABI() + .GetVirtualBaseClassOffset(CGF, This, MD->getParent(), ML.VBase); + This = CGF.Builder.CreateInBoundsGEP(This, VBaseOffset); + } } - CharUnits StaticOffset = ML.VFTableOffset; if (!StaticOffset.isZero()) { assert(StaticOffset.isPositive()); This = CGF.Builder.CreateBitCast(This, charPtrTy); @@ -625,8 +669,18 @@ llvm::Value *MicrosoftCXXABI::adjustThisParameterInVirtualFunctionPrologue( CodeGenFunction &CGF, GlobalDecl GD, llvm::Value *This) { GD = GD.getCanonicalDecl(); const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()); - if (isa<CXXDestructorDecl>(MD)) - return This; + + GlobalDecl LookupGD = GD; + if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) { + // Complete destructors take a pointer to the complete object as a + // parameter, thus don't need this adjustment. + if (GD.getDtorType() == Dtor_Complete) + return This; + + // There's no Dtor_Base in vftable but it shares the this adjustment with + // the deleting one, so look it up instead. + LookupGD = GlobalDecl(DD, Dtor_Deleting); + } // In this ABI, every virtual function takes a pointer to one of the // subobjects that first defines it as the 'this' parameter, rather than a @@ -635,7 +689,7 @@ llvm::Value *MicrosoftCXXABI::adjustThisParameterInVirtualFunctionPrologue( // See comments in the MicrosoftVFTableContext implementation for the details. MicrosoftVFTableContext::MethodVFTableLocation ML = - CGM.getVFTableContext().getMethodVFTableLocation(GD); + CGM.getVFTableContext().getMethodVFTableLocation(LookupGD); CharUnits Adjustment = ML.VFTableOffset; if (ML.VBase) { const ASTRecordLayout &DerivedLayout = @@ -850,17 +904,18 @@ void MicrosoftCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF, // We have only one destructor in the vftable but can get both behaviors // by passing an implicit int parameter. + GlobalDecl GD(Dtor, Dtor_Deleting); const CGFunctionInfo *FInfo = &CGM.getTypes().arrangeCXXDestructor(Dtor, Dtor_Deleting); llvm::Type *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo); - llvm::Value *Callee = - getVirtualFunctionPointer(CGF, GlobalDecl(Dtor, Dtor_Deleting), This, Ty); + llvm::Value *Callee = getVirtualFunctionPointer(CGF, GD, This, Ty); ASTContext &Context = CGF.getContext(); llvm::Value *ImplicitParam = llvm::ConstantInt::get(llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()), DtorType == Dtor_Deleting); + This = adjustThisArgumentForVirtualCall(CGF, GD, This); CGF.EmitCXXMemberCall(Dtor, CallLoc, Callee, ReturnValueSlot(), This, ImplicitParam, Context.IntTy, 0, 0); } |