Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -20,6 +20,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/SideEffectFinder.h" #include "clang/AST/StmtCXX.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/CodeGen/CGFunctionInfo.h" @@ -1351,12 +1352,52 @@ return HasTrivialDestructorBody(Context, FieldClassDecl, FieldClassDecl); } +namespace { + /// \brief Detect if any sub-Expr is possibly sensitive to dynamic types. + class DynamicTypeSensitivityDetector : + public ConstEvaluatedExprVisitor + { + typedef ConstEvaluatedExprVisitor Inherited; + bool Sensitive; + + public: + explicit DynamicTypeSensitivityDetector(const ASTContext &Context) + : Inherited(Context), Sensitive(false) { } + + bool isSensitive() const { return Sensitive; } + + void VisitCallExpr(const Expr *E) { + Sensitive = true; + } + void VisitCXXTypeidExpr(const Expr *E) { + Sensitive = true; + } + void VisitExpr(const Expr *E) { + if (E->getStmtClass() == Stmt::CXXTypeidExprClass || + E->getStmtClass() == Stmt::CXXDynamicCastExprClass) { + Sensitive = true; + } + if (!Sensitive) + Inherited::VisitExpr(E); + } + }; +} + /// CanSkipVTablePointerInitialization - Check whether we need to initialize /// any vtable pointers before calling this destructor. static bool CanSkipVTablePointerInitialization(ASTContext &Context, const CXXDestructorDecl *Dtor) { - if (!Dtor->hasTrivialBody()) - return false; + // We only need the vtable ptr for the body if it is sensitive to the + // dynamic type of '*this' and potentially has side-effects. + const Stmt *Body = Dtor->getBody(); + DynamicTypeSensitivityDetector Detector(Context); + Detector.Visit(Body); + if (Detector.isSensitive()) { + SideEffectFinder Finder(Context, true); + Finder.Visit(Body); + if (Finder.hasSideEffects()) + return false; + } // Check the fields. const CXXRecordDecl *ClassDecl = Dtor->getParent(); Index: test/CodeGenCXX/destructors.cpp =================================================================== --- test/CodeGenCXX/destructors.cpp +++ test/CodeGenCXX/destructors.cpp @@ -1,10 +1,13 @@ -// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -emit-llvm -o - -mconstructor-aliases -fcxx-exceptions -fexceptions -O1 -disable-llvm-optzns > %t +// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -emit-llvm -o - -mconstructor-aliases -fcxx-exceptions -fexceptions -O1 -disable-llvm-optzns > %t +// RUN: FileCheck --check-prefix=CHECK0 --input-file=%t %s // RUN: FileCheck --check-prefix=CHECK1 --input-file=%t %s // RUN: FileCheck --check-prefix=CHECK2 --input-file=%t %s // RUN: FileCheck --check-prefix=CHECK3 --input-file=%t %s // RUN: FileCheck --check-prefix=CHECK4 --input-file=%t %s // RUN: FileCheck --check-prefix=CHECK5 --input-file=%t %s +#include // so we can use 'typeid' + struct A { int a; @@ -16,7 +19,14 @@ ~B(); }; +// CHECK0-LABEL: @_ZN1BD1Ev = alias void (%struct.B*)* @_ZN1BD2Ev +// CHECK0-LABEL: @_ZN1CD1Ev = alias void (%struct.C*)* @_ZN1CD2Ev +// CHECK0-LABEL: @_ZN2TBD1Ev = alias void (%struct.TB*)* @_ZN2TBD2Ev +// CHECK0-LABEL: @_ZN2TDD1Ev = alias void (%struct.TD*)* @_ZN2TDD2Ev + B::~B() { } +// CHECK0-LABEL: define void @_ZN1BD2Ev(%struct.B* %this) +// CHECK0: call void @_ZN1AD2Ev // Field with non-trivial destructor struct C { @@ -26,6 +36,100 @@ }; C::~C() { } +// CHECK0-LABEL: define void @_ZN1CD2Ev(%struct.C* %this) +// CHECK0: call void @_ZN1AD1Ev + +struct TB { + virtual void f(); + ~TB(); + int x; +}; +TB *tb; +// None of these statements need the dynamic type to be correct +// and has side-effects (or are even important to evaluate). +TB::~TB() { ; {{}} (void)0; ({int y = 42; y; }); x; typeid(this); dynamic_cast(tb); } +// CHECK0-LABEL: define void @_ZN2TBD2Ev(%struct.TB* %this) +// No need to store vptr. +// CHECK0-NOT: @_ZTV +// CHECK0: ret +struct TD : TB { + ~TD(); +}; +TD::~TD() { ; {{}} (void)0; ({int y = 42; y; }); x; typeid(this); dynamic_cast(tb); } +// CHECK0-LABEL: define void @_ZN2TDD2Ev(%struct.TD* %this) +// No need to store vptr. +// CHECK0-NOT: @_ZTV +// CHECK0: call void @_ZN2TBD2Ev +// CHECK0: ret + +// Check cases where we must store the vptr. +extern void g(); +struct NTD : TB { + ~NTD(); +}; +NTD::~NTD() { g(); } +// CHECK0-LABEL: define void @_ZN3NTDD2Ev(%struct.NTD* %this) +// Because we call 'g()' must to store the vptr (our +// base doesn't need to, but that's irrelevant). +// CHECK0: store {{.*}} @_ZTV3NTD +// CHECK0: void @_Z1gv +// CHECK0: call void @_ZN2TBD2Ev +// CHECK0: ret + +struct NTB { + virtual void f(); + ~NTB(); + int x; +}; +NTB::~NTB() { g(); } +// CHECK0-LABEL: define void @_ZN3NTBD2Ev(%struct.NTB* %this) +// Because we call 'g()' must to store the vptr. +// CHECK0: store {{.*}} @_ZTV3NTB +// CHECK0: void @_Z1gv +// CHECK0: ret + +struct TD2 : NTB { + virtual void f(); + ~TD2(); +}; +NTB *ntb; +TD2::~TD2() { ; {{}} (void)0; ({int y = 42; y; }); x; /*@@@typeid(this); dynamic_cast(ntb);*/ } +// CHECK0-LABEL: define void @_ZN3TD2D2Ev(%struct.TD2* %this) +// We don't need to store the vptr (our base does need to, +// but that's irrelevant). +// CHECK0-NOT: @_ZTV +// CHECK0: call void @_ZN3NTBD2Ev +// CHECK0: ret + +// More cases we must store the vptr. +struct NT2 { + virtual void f(); + ~NT2(); +}; +NT2* nt2; +NT2::~NT2() { nt2->f(); } +// CHECK0-LABEL: define void @_ZN3NT2D2Ev(%struct.NT2* %this) +// CHECK0: store {{.*}} @_ZTV3NT2 +// CHECK0: ret + +struct NT3 { + virtual void f(); + ~NT3(); +}; +const std::type_info *ti; +NT3::~NT3() { ti = &typeid(this); } +// CHECK0-LABEL: define void @_ZN3NT3D2Ev(%struct.NT3* %this) +// CHECK0: store {{.*}} @_ZTV3NT3 +// CHECK0: ret + +struct NT4 : TB { + virtual void f(); + ~NT4(); +}; +NT4::~NT4() { dynamic_cast(*tb); } +// CHECK0-LABEL: define void @_ZN3NT4D2Ev(%struct.NT4* %this) +// CHECK0: store {{.*}} @_ZTV3NT4 +// CHECK0: ret namespace PR7526 { extern void foo();