forked from OSchip/llvm-project
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 llvm-svn: 340306
This commit is contained in:
parent
7d1790868f
commit
5a559e64a9
clang
include/clang
lib
AST
CodeGen
Driver/ToolChains
Frontend
Sema
test
|
@ -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; }
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -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">;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -898,6 +898,13 @@ def ffixed_point : Flag<["-"], "ffixed-point">, Group<f_Group>,
|
|||
Flags<[CC1Option]>, HelpText<"Enable fixed point types">;
|
||||
def fno_fixed_point : Flag<["-"], "fno-fixed-point">, Group<f_Group>,
|
||||
HelpText<"Disable fixed point types">;
|
||||
def fcxx_static_destructors : Flag<["-"], "fc++-static-destructors">,
|
||||
Group<f_Group>,
|
||||
HelpText<"Enable C++ static destructor registration (the default)">;
|
||||
def fno_cxx_static_destructors : Flag<["-"], "fno-c++-static-destructors">,
|
||||
Group<f_Group>,
|
||||
Flags<[CC1Option]>,
|
||||
HelpText<"Disable C++ static destructor registration">;
|
||||
|
||||
// Begin sanitizer flags. These should all be core options exposed in all driver
|
||||
// modes.
|
||||
|
|
|
@ -2449,6 +2449,12 @@ bool VarDecl::isKnownToBeDefined() const {
|
|||
return hasDefinition();
|
||||
}
|
||||
|
||||
bool VarDecl::isNoDestroy(const ASTContext &Ctx) const {
|
||||
return hasGlobalStorage() && (hasAttr<NoDestroyAttr>() ||
|
||||
(!Ctx.getLangOpts().RegisterStaticDestructors &&
|
||||
!hasAttr<AlwaysDestroyAttr>()));
|
||||
}
|
||||
|
||||
MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const {
|
||||
if (isStaticDataMember())
|
||||
// FIXME: Remove ?
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<VarDecl>(D) || !cast<VarDecl>(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<AlwaysDestroyAttr, NoDestroyAttr>(S, D, A);
|
||||
} else {
|
||||
handleSimpleAttributeWithExclusions<NoDestroyAttr, AlwaysDestroyAttr>(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<ReinitializesAttr>(S, D, AL);
|
||||
break;
|
||||
|
||||
case ParsedAttr::AT_AlwaysDestroy:
|
||||
case ParsedAttr::AT_NoDestroy:
|
||||
handleDestroyAttr(S, D, AL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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)
|
||||
|
|
|
@ -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}}
|
|
@ -43,3 +43,8 @@ namespace test3 {
|
|||
};
|
||||
E e;
|
||||
}
|
||||
|
||||
namespace test4 {
|
||||
struct A { ~A(); };
|
||||
[[clang::no_destroy]] A a; // no warning
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue