Skip to content

[clang] Add support for omitting only global destructors #104899

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 26, 2024

Conversation

smeenai
Copy link
Collaborator

@smeenai smeenai commented Aug 20, 2024

For mobile applications, it's common for global destructors to never be
called (because the applications have their own lifecycle independent of
the standard C runtime), but threads are created and destroyed as normal
and so thread-local destructors are still called. -fno-static-c++-destructors
omits unnecessary global destructors, which is useful for code size, but
it also omits thread-local destructors, which is unsuitable. Add a
ternary -fc++-static-destructors={all,none,thread-local} option
instead to allow omitting only global destructors.

For mobile applications, it's common for global destructors to never be
called (because the applications have their own lifecycle independent of
the standard C runtime), but threads are created and destroyed as normal
and so thread-local destructors are still called. -fno-static-c++-destructors
omits unnecessary global destructors, which is useful for code size, but
it also omits thread-local destructors, which is unsuitable. Add a
ternary `-fc++-static-destructors={all,none,thread-local}` option
instead to allow omitting only global destructors.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Aug 20, 2024
@llvmbot
Copy link
Member

llvmbot commented Aug 20, 2024

@llvm/pr-subscribers-clang-driver

Author: Shoaib Meenai (smeenai)

Changes

For mobile applications, it's common for global destructors to never be
called (because the applications have their own lifecycle independent of
the standard C runtime), but threads are created and destroyed as normal
and so thread-local destructors are still called. -fno-static-c++-destructors
omits unnecessary global destructors, which is useful for code size, but
it also omits thread-local destructors, which is unsuitable. Add a
ternary -fc++-static-destructors={all,none,thread-local} option
instead to allow omitting only global destructors.


Full diff: https://github.com/llvm/llvm-project/pull/104899.diff

9 Files Affected:

  • (modified) clang/include/clang/Basic/LangOptions.def (+3-1)
  • (modified) clang/include/clang/Basic/LangOptions.h (+10)
  • (modified) clang/include/clang/Driver/Options.td (+12-5)
  • (modified) clang/lib/AST/Decl.cpp (+11-3)
  • (modified) clang/lib/Driver/ToolChains/Clang.cpp (+3-2)
  • (modified) clang/test/CodeGenCXX/always_destroy.cpp (+6-2)
  • (modified) clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp (+2-1)
  • (added) clang/test/Driver/cxx-static-destructors.cpp (+9)
  • (modified) clang/test/SemaCXX/no_destroy.cpp (+10-20)
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index d454a7ff2f8cf4..a57da8addb69be 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -465,7 +465,9 @@ 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")
+ENUM_LANGOPT(RegisterStaticDestructors, RegisterStaticDestructorsKind, 2,
+             RegisterStaticDestructorsKind::All,
+             "Register C++ static destructors")
 
 LANGOPT(RegCall4, 1, 0, "Set __regcall4 as a default calling convention to respect __regcall ABI v.4")
 
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 91f1c2f2e6239e..88fbf26e2eb79b 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -441,6 +441,16 @@ class LangOptionsBase {
     CX_None
   };
 
+  /// Controls which variables have static destructors registered.
+  enum class RegisterStaticDestructorsKind {
+    /// Register static destructors for all variables.
+    All,
+    /// Register static destructors only for thread-local variables.
+    ThreadLocal,
+    /// Don't register static destructors for any variables.
+    None,
+  };
+
   // Define simple language options (with no accessors).
 #define LANGOPT(Name, Bits, Default, Description) unsigned Name : Bits;
 #define ENUM_LANGOPT(Name, Type, Bits, Default, Description)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index c66e035a259b3f..0d8cb1d18fb24d 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2300,11 +2300,18 @@ defm fixed_point : BoolFOption<"fixed-point",
   PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
   NegFlag<SetFalse, [], [ClangOption], "Disable">,
   BothFlags<[], [ClangOption], " fixed point types">>;
-defm cxx_static_destructors : BoolFOption<"c++-static-destructors",
-  LangOpts<"RegisterStaticDestructors">, DefaultTrue,
-  NegFlag<SetFalse, [], [ClangOption, CC1Option],
-          "Disable C++ static destructor registration">,
-  PosFlag<SetTrue>>;
+def cxx_static_destructors_EQ : Joined<["-"], "fc++-static-destructors=">, Group<f_Group>,
+  HelpText<"Controls which variables C++ static destructors are registered for">,
+  Values<"all,thread-local,none">,
+  NormalizedValues<["All", "ThreadLocal", "None"]>,
+  NormalizedValuesScope<"LangOptions::RegisterStaticDestructorsKind">,
+  MarshallingInfoEnum<LangOpts<"RegisterStaticDestructors">, "All">,
+  Visibility<[ClangOption, CC1Option]>;
+def cxx_static_destructors : Flag<["-"], "fc++-static-destructors">, Group<f_Group>,
+  Alias<cxx_static_destructors_EQ>, AliasArgs<["all"]>;
+def no_cxx_static_destructors : Flag<["-"], "fno-c++-static-destructors">, Group<f_Group>,
+  Alias<cxx_static_destructors_EQ>, AliasArgs<["none"]>,
+  HelpText<"Disable C++ static destructor registration">;
 def fsymbol_partition_EQ : Joined<["-"], "fsymbol-partition=">, Group<f_Group>,
   Visibility<[ClangOption, CC1Option]>,
   MarshallingInfoString<CodeGenOpts<"SymbolPartition">>;
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 90caf81757ac96..1a07125815832e 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2799,9 +2799,17 @@ bool VarDecl::isKnownToBeDefined() const {
 }
 
 bool VarDecl::isNoDestroy(const ASTContext &Ctx) const {
-  return hasGlobalStorage() && (hasAttr<NoDestroyAttr>() ||
-                                (!Ctx.getLangOpts().RegisterStaticDestructors &&
-                                 !hasAttr<AlwaysDestroyAttr>()));
+  if (!hasGlobalStorage())
+    return false;
+  if (hasAttr<NoDestroyAttr>())
+    return true;
+  if (hasAttr<AlwaysDestroyAttr>())
+    return false;
+
+  using RSDKind = LangOptions::RegisterStaticDestructorsKind;
+  RSDKind K = Ctx.getLangOpts().getRegisterStaticDestructors();
+  return K == RSDKind::None ||
+         (K == RSDKind::ThreadLocal && getTLSKind() == TLS_None);
 }
 
 QualType::DestructionKind
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index a5a87db97e96b4..68ac374e036b1f 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7967,8 +7967,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
                     options::OPT_fno_keep_persistent_storage_variables);
   Args.addOptInFlag(CmdArgs, options::OPT_fcomplete_member_pointers,
                     options::OPT_fno_complete_member_pointers);
-  Args.addOptOutFlag(CmdArgs, options::OPT_fcxx_static_destructors,
-                     options::OPT_fno_cxx_static_destructors);
+  if (Arg *A = Args.getLastArg(options::OPT_cxx_static_destructors_EQ))
+    CmdArgs.push_back(
+        Args.MakeArgString(Twine("-fc++-static-destructors=") + A->getValue()));
 
   addMachineOutlinerArgs(D, Args, CmdArgs, Triple, /*IsLTO=*/false);
 
diff --git a/clang/test/CodeGenCXX/always_destroy.cpp b/clang/test/CodeGenCXX/always_destroy.cpp
index e84c4cf02c52f5..ca8a8e0cabacb3 100644
--- a/clang/test/CodeGenCXX/always_destroy.cpp
+++ b/clang/test/CodeGenCXX/always_destroy.cpp
@@ -1,4 +1,7 @@
-// RUN: %clang_cc1 %s -fno-c++-static-destructors -emit-llvm -triple x86_64-apple-macosx10.13.0 -o - | FileCheck %s
+// RUN: %clang_cc1 %s -fc++-static-destructors=none -emit-llvm -triple x86_64-apple-macosx10.13.0 -o - | \
+// RUN:   FileCheck --check-prefixes=CHECK,NO-DTORS %s
+// RUN: %clang_cc1 %s -fc++-static-destructors=thread-local -emit-llvm -triple x86_64-apple-macosx10.13.0 -o - | \
+// RUN:   FileCheck --check-prefixes=CHECK,THREAD-LOCAL-DTORS %s
 
 struct NonTrivial {
   ~NonTrivial();
@@ -6,7 +9,8 @@ struct NonTrivial {
 
 // CHECK-NOT: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev
 NonTrivial nt1;
-// CHECK-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev
+// NO-DTORS-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev
+// THREAD-LOCAL-DTORS: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev
 thread_local NonTrivial nt2;
 
 struct NonTrivial2 {
diff --git a/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp b/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp
index b03791e5135df5..053043adb61c17 100644
--- a/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp
+++ b/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -std=c++2a -emit-llvm -O0 -triple x86_64-unknown-linux-gnu -DNOATTR  %s -o - | FileCheck %s
 // RUN: %clang_cc1 -std=c++2a -emit-llvm -O0 -triple x86_64-unknown-linux-gnu %s -o - | FileCheck %s --check-prefix=CHECK-ATTR
-// RUN: %clang_cc1 -std=c++2a -emit-llvm -O0 -triple x86_64-unknown-linux-gnu -DNOATTR -fno-c++-static-destructors %s -o - | FileCheck %s --check-prefix=CHECK-FLAG
+// RUN: %clang_cc1 -std=c++2a -emit-llvm -O0 -triple x86_64-unknown-linux-gnu -DNOATTR -fc++-static-destructors=none %s -o - | FileCheck %s --check-prefix=CHECK-FLAG
+// RUN: %clang_cc1 -std=c++2a -emit-llvm -O0 -triple x86_64-unknown-linux-gnu -DNOATTR -fc++-static-destructors=thread-local %s -o - | FileCheck %s --check-prefix=CHECK-FLAG
 
 // Regression test for D54344. Class with no user-defined destructor
 // that has an inherited member that has a non-trivial destructor
diff --git a/clang/test/Driver/cxx-static-destructors.cpp b/clang/test/Driver/cxx-static-destructors.cpp
new file mode 100644
index 00000000000000..71da4d28c25142
--- /dev/null
+++ b/clang/test/Driver/cxx-static-destructors.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang -### -c -fc++-static-destructors=all %s 2>&1 | FileCheck --check-prefix ALL %s
+// RUN: %clang -### -c -fc++-static-destructors %s 2>&1 | FileCheck --check-prefix ALL %s
+// RUN: %clang -### -c -fc++-static-destructors=none %s 2>&1 | FileCheck --check-prefix NONE %s
+// RUN: %clang -### -c -fno-c++-static-destructors %s 2>&1 | FileCheck --check-prefix NONE %s
+// RUN: %clang -### -c -fc++-static-destructors=thread-local %s 2>&1 | FileCheck --check-prefix THREAD-LOCAL %s
+
+// ALL: -fc++-static-destructors=all
+// NONE: -fc++-static-destructors=none
+// THREAD-LOCAL: -fc++-static-destructors=thread-local
diff --git a/clang/test/SemaCXX/no_destroy.cpp b/clang/test/SemaCXX/no_destroy.cpp
index 5872bcf4b439e2..d39bcaeff860a1 100644
--- a/clang/test/SemaCXX/no_destroy.cpp
+++ b/clang/test/SemaCXX/no_destroy.cpp
@@ -1,31 +1,21 @@
-// RUN: %clang_cc1 -DNO_DTORS -DNO_EXCEPTIONS -fno-c++-static-destructors -verify %s
-// RUN: %clang_cc1 -DNO_EXCEPTIONS -verify %s
-// RUN: %clang_cc1 -DNO_DTORS -fexceptions -fno-c++-static-destructors -verify %s
-// RUN: %clang_cc1 -fexceptions -verify %s
+// RUN: %clang_cc1 -fc++-static-destructors=none -verify %s
+// RUN: %clang_cc1 -fc++-static-destructors=thread-local -verify=expected,thread-local-dtors %s
+// RUN: %clang_cc1 -verify=expected,thread-local-dtors,all-dtors %s
+// RUN: %clang_cc1 -fexceptions -fc++-static-destructors=none -verify %s
+// RUN: %clang_cc1 -fexceptions -fc++-static-destructors=thread-local -verify=expected,thread-local-dtors %s
+// RUN: %clang_cc1 -fexceptions -verify=expected,thread-local-dtors,all-dtors %s
 
 struct SecretDestructor {
-#ifndef NO_DTORS
-  // expected-note@+2 4 {{private}}
-#endif
 private: ~SecretDestructor(); // expected-note + {{private}}
 };
 
-SecretDestructor sd1;
-thread_local SecretDestructor sd2;
+SecretDestructor sd1; // all-dtors-error{{private}}
+thread_local SecretDestructor sd2; // thread-local-dtors-error{{private}}
 void locals() {
-  static SecretDestructor sd3;
-  thread_local SecretDestructor sd4;
+  static SecretDestructor sd3; // all-dtors-error{{private}}
+  thread_local SecretDestructor sd4; // thread-local-dtors-error{{private}}
 }
 
-#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}}
 

@llvmbot
Copy link
Member

llvmbot commented Aug 20, 2024

@llvm/pr-subscribers-clang

Author: Shoaib Meenai (smeenai)

Changes

For mobile applications, it's common for global destructors to never be
called (because the applications have their own lifecycle independent of
the standard C runtime), but threads are created and destroyed as normal
and so thread-local destructors are still called. -fno-static-c++-destructors
omits unnecessary global destructors, which is useful for code size, but
it also omits thread-local destructors, which is unsuitable. Add a
ternary -fc++-static-destructors={all,none,thread-local} option
instead to allow omitting only global destructors.


Full diff: https://github.com/llvm/llvm-project/pull/104899.diff

9 Files Affected:

  • (modified) clang/include/clang/Basic/LangOptions.def (+3-1)
  • (modified) clang/include/clang/Basic/LangOptions.h (+10)
  • (modified) clang/include/clang/Driver/Options.td (+12-5)
  • (modified) clang/lib/AST/Decl.cpp (+11-3)
  • (modified) clang/lib/Driver/ToolChains/Clang.cpp (+3-2)
  • (modified) clang/test/CodeGenCXX/always_destroy.cpp (+6-2)
  • (modified) clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp (+2-1)
  • (added) clang/test/Driver/cxx-static-destructors.cpp (+9)
  • (modified) clang/test/SemaCXX/no_destroy.cpp (+10-20)
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index d454a7ff2f8cf4..a57da8addb69be 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -465,7 +465,9 @@ 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")
+ENUM_LANGOPT(RegisterStaticDestructors, RegisterStaticDestructorsKind, 2,
+             RegisterStaticDestructorsKind::All,
+             "Register C++ static destructors")
 
 LANGOPT(RegCall4, 1, 0, "Set __regcall4 as a default calling convention to respect __regcall ABI v.4")
 
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 91f1c2f2e6239e..88fbf26e2eb79b 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -441,6 +441,16 @@ class LangOptionsBase {
     CX_None
   };
 
+  /// Controls which variables have static destructors registered.
+  enum class RegisterStaticDestructorsKind {
+    /// Register static destructors for all variables.
+    All,
+    /// Register static destructors only for thread-local variables.
+    ThreadLocal,
+    /// Don't register static destructors for any variables.
+    None,
+  };
+
   // Define simple language options (with no accessors).
 #define LANGOPT(Name, Bits, Default, Description) unsigned Name : Bits;
 #define ENUM_LANGOPT(Name, Type, Bits, Default, Description)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index c66e035a259b3f..0d8cb1d18fb24d 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2300,11 +2300,18 @@ defm fixed_point : BoolFOption<"fixed-point",
   PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
   NegFlag<SetFalse, [], [ClangOption], "Disable">,
   BothFlags<[], [ClangOption], " fixed point types">>;
-defm cxx_static_destructors : BoolFOption<"c++-static-destructors",
-  LangOpts<"RegisterStaticDestructors">, DefaultTrue,
-  NegFlag<SetFalse, [], [ClangOption, CC1Option],
-          "Disable C++ static destructor registration">,
-  PosFlag<SetTrue>>;
+def cxx_static_destructors_EQ : Joined<["-"], "fc++-static-destructors=">, Group<f_Group>,
+  HelpText<"Controls which variables C++ static destructors are registered for">,
+  Values<"all,thread-local,none">,
+  NormalizedValues<["All", "ThreadLocal", "None"]>,
+  NormalizedValuesScope<"LangOptions::RegisterStaticDestructorsKind">,
+  MarshallingInfoEnum<LangOpts<"RegisterStaticDestructors">, "All">,
+  Visibility<[ClangOption, CC1Option]>;
+def cxx_static_destructors : Flag<["-"], "fc++-static-destructors">, Group<f_Group>,
+  Alias<cxx_static_destructors_EQ>, AliasArgs<["all"]>;
+def no_cxx_static_destructors : Flag<["-"], "fno-c++-static-destructors">, Group<f_Group>,
+  Alias<cxx_static_destructors_EQ>, AliasArgs<["none"]>,
+  HelpText<"Disable C++ static destructor registration">;
 def fsymbol_partition_EQ : Joined<["-"], "fsymbol-partition=">, Group<f_Group>,
   Visibility<[ClangOption, CC1Option]>,
   MarshallingInfoString<CodeGenOpts<"SymbolPartition">>;
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 90caf81757ac96..1a07125815832e 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2799,9 +2799,17 @@ bool VarDecl::isKnownToBeDefined() const {
 }
 
 bool VarDecl::isNoDestroy(const ASTContext &Ctx) const {
-  return hasGlobalStorage() && (hasAttr<NoDestroyAttr>() ||
-                                (!Ctx.getLangOpts().RegisterStaticDestructors &&
-                                 !hasAttr<AlwaysDestroyAttr>()));
+  if (!hasGlobalStorage())
+    return false;
+  if (hasAttr<NoDestroyAttr>())
+    return true;
+  if (hasAttr<AlwaysDestroyAttr>())
+    return false;
+
+  using RSDKind = LangOptions::RegisterStaticDestructorsKind;
+  RSDKind K = Ctx.getLangOpts().getRegisterStaticDestructors();
+  return K == RSDKind::None ||
+         (K == RSDKind::ThreadLocal && getTLSKind() == TLS_None);
 }
 
 QualType::DestructionKind
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index a5a87db97e96b4..68ac374e036b1f 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7967,8 +7967,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
                     options::OPT_fno_keep_persistent_storage_variables);
   Args.addOptInFlag(CmdArgs, options::OPT_fcomplete_member_pointers,
                     options::OPT_fno_complete_member_pointers);
-  Args.addOptOutFlag(CmdArgs, options::OPT_fcxx_static_destructors,
-                     options::OPT_fno_cxx_static_destructors);
+  if (Arg *A = Args.getLastArg(options::OPT_cxx_static_destructors_EQ))
+    CmdArgs.push_back(
+        Args.MakeArgString(Twine("-fc++-static-destructors=") + A->getValue()));
 
   addMachineOutlinerArgs(D, Args, CmdArgs, Triple, /*IsLTO=*/false);
 
diff --git a/clang/test/CodeGenCXX/always_destroy.cpp b/clang/test/CodeGenCXX/always_destroy.cpp
index e84c4cf02c52f5..ca8a8e0cabacb3 100644
--- a/clang/test/CodeGenCXX/always_destroy.cpp
+++ b/clang/test/CodeGenCXX/always_destroy.cpp
@@ -1,4 +1,7 @@
-// RUN: %clang_cc1 %s -fno-c++-static-destructors -emit-llvm -triple x86_64-apple-macosx10.13.0 -o - | FileCheck %s
+// RUN: %clang_cc1 %s -fc++-static-destructors=none -emit-llvm -triple x86_64-apple-macosx10.13.0 -o - | \
+// RUN:   FileCheck --check-prefixes=CHECK,NO-DTORS %s
+// RUN: %clang_cc1 %s -fc++-static-destructors=thread-local -emit-llvm -triple x86_64-apple-macosx10.13.0 -o - | \
+// RUN:   FileCheck --check-prefixes=CHECK,THREAD-LOCAL-DTORS %s
 
 struct NonTrivial {
   ~NonTrivial();
@@ -6,7 +9,8 @@ struct NonTrivial {
 
 // CHECK-NOT: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev
 NonTrivial nt1;
-// CHECK-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev
+// NO-DTORS-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev
+// THREAD-LOCAL-DTORS: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev
 thread_local NonTrivial nt2;
 
 struct NonTrivial2 {
diff --git a/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp b/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp
index b03791e5135df5..053043adb61c17 100644
--- a/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp
+++ b/clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -std=c++2a -emit-llvm -O0 -triple x86_64-unknown-linux-gnu -DNOATTR  %s -o - | FileCheck %s
 // RUN: %clang_cc1 -std=c++2a -emit-llvm -O0 -triple x86_64-unknown-linux-gnu %s -o - | FileCheck %s --check-prefix=CHECK-ATTR
-// RUN: %clang_cc1 -std=c++2a -emit-llvm -O0 -triple x86_64-unknown-linux-gnu -DNOATTR -fno-c++-static-destructors %s -o - | FileCheck %s --check-prefix=CHECK-FLAG
+// RUN: %clang_cc1 -std=c++2a -emit-llvm -O0 -triple x86_64-unknown-linux-gnu -DNOATTR -fc++-static-destructors=none %s -o - | FileCheck %s --check-prefix=CHECK-FLAG
+// RUN: %clang_cc1 -std=c++2a -emit-llvm -O0 -triple x86_64-unknown-linux-gnu -DNOATTR -fc++-static-destructors=thread-local %s -o - | FileCheck %s --check-prefix=CHECK-FLAG
 
 // Regression test for D54344. Class with no user-defined destructor
 // that has an inherited member that has a non-trivial destructor
diff --git a/clang/test/Driver/cxx-static-destructors.cpp b/clang/test/Driver/cxx-static-destructors.cpp
new file mode 100644
index 00000000000000..71da4d28c25142
--- /dev/null
+++ b/clang/test/Driver/cxx-static-destructors.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang -### -c -fc++-static-destructors=all %s 2>&1 | FileCheck --check-prefix ALL %s
+// RUN: %clang -### -c -fc++-static-destructors %s 2>&1 | FileCheck --check-prefix ALL %s
+// RUN: %clang -### -c -fc++-static-destructors=none %s 2>&1 | FileCheck --check-prefix NONE %s
+// RUN: %clang -### -c -fno-c++-static-destructors %s 2>&1 | FileCheck --check-prefix NONE %s
+// RUN: %clang -### -c -fc++-static-destructors=thread-local %s 2>&1 | FileCheck --check-prefix THREAD-LOCAL %s
+
+// ALL: -fc++-static-destructors=all
+// NONE: -fc++-static-destructors=none
+// THREAD-LOCAL: -fc++-static-destructors=thread-local
diff --git a/clang/test/SemaCXX/no_destroy.cpp b/clang/test/SemaCXX/no_destroy.cpp
index 5872bcf4b439e2..d39bcaeff860a1 100644
--- a/clang/test/SemaCXX/no_destroy.cpp
+++ b/clang/test/SemaCXX/no_destroy.cpp
@@ -1,31 +1,21 @@
-// RUN: %clang_cc1 -DNO_DTORS -DNO_EXCEPTIONS -fno-c++-static-destructors -verify %s
-// RUN: %clang_cc1 -DNO_EXCEPTIONS -verify %s
-// RUN: %clang_cc1 -DNO_DTORS -fexceptions -fno-c++-static-destructors -verify %s
-// RUN: %clang_cc1 -fexceptions -verify %s
+// RUN: %clang_cc1 -fc++-static-destructors=none -verify %s
+// RUN: %clang_cc1 -fc++-static-destructors=thread-local -verify=expected,thread-local-dtors %s
+// RUN: %clang_cc1 -verify=expected,thread-local-dtors,all-dtors %s
+// RUN: %clang_cc1 -fexceptions -fc++-static-destructors=none -verify %s
+// RUN: %clang_cc1 -fexceptions -fc++-static-destructors=thread-local -verify=expected,thread-local-dtors %s
+// RUN: %clang_cc1 -fexceptions -verify=expected,thread-local-dtors,all-dtors %s
 
 struct SecretDestructor {
-#ifndef NO_DTORS
-  // expected-note@+2 4 {{private}}
-#endif
 private: ~SecretDestructor(); // expected-note + {{private}}
 };
 
-SecretDestructor sd1;
-thread_local SecretDestructor sd2;
+SecretDestructor sd1; // all-dtors-error{{private}}
+thread_local SecretDestructor sd2; // thread-local-dtors-error{{private}}
 void locals() {
-  static SecretDestructor sd3;
-  thread_local SecretDestructor sd4;
+  static SecretDestructor sd3; // all-dtors-error{{private}}
+  thread_local SecretDestructor sd4; // thread-local-dtors-error{{private}}
 }
 
-#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}}
 

Copy link
Member

@MaskRay MaskRay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

(I'm starting a 3-week vacation this Friday and will have limited availability.)

Args.addOptOutFlag(CmdArgs, options::OPT_fcxx_static_destructors,
options::OPT_fno_cxx_static_destructors);
if (Arg *A = Args.getLastArg(options::OPT_cxx_static_destructors_EQ))
CmdArgs.push_back(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A->render(Args, CmdArgs);

@smeenai smeenai merged commit 7945435 into llvm:main Aug 26, 2024
9 checks passed
@smeenai smeenai deleted the no-global-dtors branch August 26, 2024 20:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants