From: Erik Pilkington Date: Tue, 21 Aug 2018 17:24:06 +0000 (+0000) Subject: Add a new flag and attributes to control static destructor registration X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=de7be6a701dab1c0050cc7c42cfa3049da515658;p=clang Add a new flag and attributes to control static destructor registration This commit adds the flag -fno-c++-static-destructors and the attributes [[clang::no_destroy]] and [[clang::always_destroy]]. no_destroy specifies that a specific static or thread duration variable shouldn't have it's destructor registered, and is the default in -fno-c++-static-destructors mode. always_destroy is the opposite, and is the default in -fc++-static-destructors mode. A variable whose destructor is disabled (either because of -fno-c++-static-destructors or [[clang::no_destroy]]) doesn't count as a use of the destructor, so we don't do any access checking or mark it referenced. We also don't emit -Wexit-time-destructors for these variables. rdar://21734598 Differential revision: https://reviews.llvm.org/D50994 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@340306 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 0c910e0ffb..35978c155d 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1469,6 +1469,9 @@ public: // has no definition within this source file. bool isKnownToBeDefined() const; + /// Do we need to emit an exit-time destructor for this variable? + bool isNoDestroy(const ASTContext &) const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K >= firstVar && K <= lastVar; } diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 6d3c271c5b..e4377f2b7a 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -3009,3 +3009,15 @@ def Reinitializes : InheritableAttr { let Subjects = SubjectList<[NonStaticNonConstCXXMethod], ErrorDiag>; let Documentation = [ReinitializesDocs]; } + +def NoDestroy : InheritableAttr { + let Spellings = [Clang<"no_destroy", 0>]; + let Subjects = SubjectList<[Var]>; + let Documentation = [NoDestroyDocs]; +} + +def AlwaysDestroy : InheritableAttr { + let Spellings = [Clang<"always_destroy", 0>]; + let Subjects = SubjectList<[Var]>; + let Documentation = [AlwaysDestroyDocs]; +} diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 27334b535a..e9598df8b5 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -3486,3 +3486,22 @@ a container class: }; }]; } + +def AlwaysDestroyDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``always_destroy`` attribute specifies that a variable with static or thread +storage duration should have its exit-time destructor run. This attribute is the +default unless clang was invoked with -fno-c++-static-destructors. + }]; +} + +def NoDestroyDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``no_destroy`` attribute specifies that a variable with static or thread +storage duration shouldn't have its exit-time destructor run. Annotating every +static and thread duration variable with this attribute is equivalent to +invoking clang with -fno-c++-static-destructors. + }]; +} diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 94cf03e19e..2f1bd94614 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1809,6 +1809,10 @@ def err_destructor_expr_type_mismatch : Error< def note_destructor_type_here : Note< "type %0 is declared here">; +def err_destroy_attr_on_non_static_var : Error< + "%select{no_destroy|always_destroy}0 attribute can only be applied to a" + " variable with static or thread storage duration">; + def err_destructor_template : Error< "destructor cannot be declared as a template">; diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def index fc38af5b03..8c2865b3e4 100644 --- a/include/clang/Basic/LangOptions.def +++ b/include/clang/Basic/LangOptions.def @@ -308,6 +308,8 @@ LANGOPT(FixedPoint, 1, 0, "fixed point types") LANGOPT(PaddingOnUnsignedFixedPoint, 1, 0, "unsigned fixed point types having one extra padding bit") +LANGOPT(RegisterStaticDestructors, 1, 1, "Register C++ static destructors") + #undef LANGOPT #undef COMPATIBLE_LANGOPT #undef BENIGN_LANGOPT diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 2dbf7c52eb..6e45f2aef6 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -898,6 +898,13 @@ def ffixed_point : Flag<["-"], "ffixed-point">, Group, Flags<[CC1Option]>, HelpText<"Enable fixed point types">; def fno_fixed_point : Flag<["-"], "fno-fixed-point">, Group, HelpText<"Disable fixed point types">; +def fcxx_static_destructors : Flag<["-"], "fc++-static-destructors">, + Group, + HelpText<"Enable C++ static destructor registration (the default)">; +def fno_cxx_static_destructors : Flag<["-"], "fno-c++-static-destructors">, + Group, + Flags<[CC1Option]>, + HelpText<"Disable C++ static destructor registration">; // Begin sanitizer flags. These should all be core options exposed in all driver // modes. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 8ce960ff5f..b6d35f4260 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2449,6 +2449,12 @@ bool VarDecl::isKnownToBeDefined() const { return hasDefinition(); } +bool VarDecl::isNoDestroy(const ASTContext &Ctx) const { + return hasGlobalStorage() && (hasAttr() || + (!Ctx.getLangOpts().RegisterStaticDestructors && + !hasAttr())); +} + MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const { if (isStaticDataMember()) // FIXME: Remove ? diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 5b430741ed..b2a8f5a945 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -2342,6 +2342,9 @@ void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, llvm::Constant *dtor, llvm::Constant *addr) { + if (D.isNoDestroy(CGM.getContext())) + return; + // Use __cxa_atexit if available. if (CGM.getCodeGenOpts().CXAAtExit) return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr, D.getTLSKind()); diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 059adb78ca..968c171c71 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -2240,6 +2240,9 @@ static void emitGlobalDtorWithTLRegDtor(CodeGenFunction &CGF, const VarDecl &VD, void MicrosoftCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, llvm::Constant *Dtor, llvm::Constant *Addr) { + if (D.isNoDestroy(CGM.getContext())) + return; + if (D.getTLSKind()) return emitGlobalDtorWithTLRegDtor(CGF, D, Dtor, Addr); diff --git a/lib/Driver/ToolChains/Clang.cpp b/lib/Driver/ToolChains/Clang.cpp index 68ad03d7ef..135d40fc70 100644 --- a/lib/Driver/ToolChains/Clang.cpp +++ b/lib/Driver/ToolChains/Clang.cpp @@ -4832,6 +4832,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_complete_member_pointers, false)) CmdArgs.push_back("-fcomplete-member-pointers"); + if (!Args.hasFlag(options::OPT_fcxx_static_destructors, + options::OPT_fno_cxx_static_destructors, true)) + CmdArgs.push_back("-fno-c++-static-destructors"); + if (Arg *A = Args.getLastArg(options::OPT_moutline, options::OPT_mno_outline)) { if (A->getOption().matches(options::OPT_moutline)) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index ded2d42199..74476bbe5e 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2772,6 +2772,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, // -fallow-editor-placeholders Opts.AllowEditorPlaceholders = Args.hasArg(OPT_fallow_editor_placeholders); + Opts.RegisterStaticDestructors = !Args.hasArg(OPT_fno_cxx_static_destructors); + if (Arg *A = Args.getLastArg(OPT_fclang_abi_compat_EQ)) { Opts.setClangABICompat(LangOptions::ClangABI::Latest); diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 39d7019b25..27eb533360 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -5917,6 +5917,20 @@ static void handleOpenCLAccessAttr(Sema &S, Decl *D, const ParsedAttr &AL) { AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); } +static void handleDestroyAttr(Sema &S, Decl *D, const ParsedAttr &A) { + if (!isa(D) || !cast(D)->hasGlobalStorage()) { + S.Diag(D->getLocation(), diag::err_destroy_attr_on_non_static_var) + << (A.getKind() == ParsedAttr::AT_AlwaysDestroy); + return; + } + + if (A.getKind() == ParsedAttr::AT_AlwaysDestroy) { + handleSimpleAttributeWithExclusions(S, D, A); + } else { + handleSimpleAttributeWithExclusions(S, D, A); + } +} + //===----------------------------------------------------------------------===// // Top Level Sema Entry Points //===----------------------------------------------------------------------===// @@ -6587,6 +6601,11 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_Reinitializes: handleSimpleAttribute(S, D, AL); break; + + case ParsedAttr::AT_AlwaysDestroy: + case ParsedAttr::AT_NoDestroy: + handleDestroyAttr(S, D, AL); + break; } } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 3d8da1f797..61a7730d9b 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -12910,6 +12910,9 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) { if (ClassDecl->hasIrrelevantDestructor()) return; if (ClassDecl->isDependentContext()) return; + if (VD->isNoDestroy(getASTContext())) + return; + CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl); MarkFunctionReferenced(VD->getLocation(), Destructor); CheckDestructorAccess(VD->getLocation(), Destructor, diff --git a/test/CodeGenCXX/always_destroy.cpp b/test/CodeGenCXX/always_destroy.cpp new file mode 100644 index 0000000000..e84c4cf02c --- /dev/null +++ b/test/CodeGenCXX/always_destroy.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 %s -fno-c++-static-destructors -emit-llvm -triple x86_64-apple-macosx10.13.0 -o - | FileCheck %s + +struct NonTrivial { + ~NonTrivial(); +}; + +// CHECK-NOT: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev +NonTrivial nt1; +// CHECK-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev +thread_local NonTrivial nt2; + +struct NonTrivial2 { + ~NonTrivial2(); +}; + +// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev +[[clang::always_destroy]] NonTrivial2 nt21; +// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev +[[clang::always_destroy]] thread_local NonTrivial2 nt22; + +void f() { + // CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev + [[clang::always_destroy]] static NonTrivial2 nt21; + // CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev + [[clang::always_destroy]] thread_local NonTrivial2 nt22; +} + +// CHECK-NOT: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev +[[clang::no_destroy]] NonTrivial nt3; +// CHECK-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev +[[clang::no_destroy]] thread_local NonTrivial nt4; diff --git a/test/CodeGenCXX/no_destroy.cpp b/test/CodeGenCXX/no_destroy.cpp new file mode 100644 index 0000000000..93e6ce17bb --- /dev/null +++ b/test/CodeGenCXX/no_destroy.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 %s -emit-llvm -triple x86_64-apple-macosx10.13.0 -o - | FileCheck %s + +struct NonTrivial { + ~NonTrivial(); +}; + +// CHECK-NOT: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev +[[clang::no_destroy]] NonTrivial nt1; +// CHECK-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev +[[clang::no_destroy]] thread_local NonTrivial nt2; + +struct NonTrivial2 { + ~NonTrivial2(); +}; + +// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev +NonTrivial2 nt21; +// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev +thread_local NonTrivial2 nt22; + +void f() { + // CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev + static NonTrivial2 nt21; + // CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev + thread_local NonTrivial2 nt22; +} + +// CHECK: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev +[[clang::always_destroy]] NonTrivial nt3; +// CHECK: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev +[[clang::always_destroy]] thread_local NonTrivial nt4; diff --git a/test/Misc/pragma-attribute-supported-attributes-list.test b/test/Misc/pragma-attribute-supported-attributes-list.test index 4a3846be8d..d2c1fe6d27 100644 --- a/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,7 +2,7 @@ // The number of supported attributes should never go down! -// CHECK: #pragma clang attribute supports 72 attributes: +// CHECK: #pragma clang attribute supports 74 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -11,6 +11,7 @@ // CHECK-NEXT: AbiTag (SubjectMatchRule_record_not_is_union, SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_namespace) // CHECK-NEXT: AlignValue (SubjectMatchRule_variable, SubjectMatchRule_type_alias) // CHECK-NEXT: AllocSize (SubjectMatchRule_function) +// CHECK-NEXT: AlwaysDestroy (SubjectMatchRule_variable) // CHECK-NEXT: Annotate () // CHECK-NEXT: AnyX86NoCfCheck (SubjectMatchRule_hasType_functionType) // CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function) @@ -38,6 +39,7 @@ // CHECK-NEXT: MipsLongCall (SubjectMatchRule_function) // CHECK-NEXT: MipsShortCall (SubjectMatchRule_function) // CHECK-NEXT: NoDebug (SubjectMatchRule_hasType_functionType, SubjectMatchRule_objc_method, SubjectMatchRule_variable_not_is_parameter) +// CHECK-NEXT: NoDestroy (SubjectMatchRule_variable) // CHECK-NEXT: NoDuplicate (SubjectMatchRule_function) // CHECK-NEXT: NoEscape (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: NoMicroMips (SubjectMatchRule_function) diff --git a/test/SemaCXX/no_destroy.cpp b/test/SemaCXX/no_destroy.cpp new file mode 100644 index 0000000000..bdb8077cb5 --- /dev/null +++ b/test/SemaCXX/no_destroy.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -DNO_DTORS -fno-c++-static-destructors -verify %s +// RUN: %clang_cc1 -verify %s + +struct SecretDestructor { +#ifndef NO_DTORS + // expected-note@+2 4 {{private}} +#endif +private: ~SecretDestructor(); // expected-note 2 {{private}} +}; + +SecretDestructor sd1; +thread_local SecretDestructor sd2; +void locals() { + static SecretDestructor sd3; + thread_local SecretDestructor sd4; +} + +#ifndef NO_DTORS +// SecretDestructor sd1; // expected-error@-8 {{private}} +// thread_local SecretDestructor sd2; // expected-error@-8 {{private}} +// void locals() { +// static SecretDestructor sd3; // expected-error@-8 {{private}} +// thread_local SecretDestructor sd4; // expected-error@-8 {{private}} +// } +#endif + +[[clang::always_destroy]] SecretDestructor sd6; // expected-error{{private}} +[[clang::always_destroy]] thread_local SecretDestructor sd7; // expected-error{{private}} + +[[clang::no_destroy]] SecretDestructor sd8; + +int main() { + [[clang::no_destroy]] int p; // expected-error{{no_destroy attribute can only be applied to a variable with static or thread storage duration}} + [[clang::always_destroy]] int p2; // expected-error{{always_destroy attribute can only be applied to a variable with static or thread storage duration}} + [[clang::no_destroy]] static int p3; + [[clang::always_destroy]] static int p4; +} + +[[clang::always_destroy]] [[clang::no_destroy]] int p; // expected-error{{'no_destroy' and 'always_destroy' attributes are not compatible}} // expected-note{{here}} +[[clang::no_destroy]] [[clang::always_destroy]] int p2; // expected-error{{'always_destroy' and 'no_destroy' attributes are not compatible}} // expected-note{{here}} diff --git a/test/SemaCXX/warn-exit-time-destructors.cpp b/test/SemaCXX/warn-exit-time-destructors.cpp index 124576aa95..17b3a94063 100644 --- a/test/SemaCXX/warn-exit-time-destructors.cpp +++ b/test/SemaCXX/warn-exit-time-destructors.cpp @@ -43,3 +43,8 @@ namespace test3 { }; E e; } + +namespace test4 { +struct A { ~A(); }; +[[clang::no_destroy]] A a; // no warning +}