[llvm] [Support] Use block-scope statics for cl::opt registration globals (PR #215216)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 10 01:35:14 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-support

Author: Sylvestre Ledru (sylvestre)

<details>
<summary>Changes</summary>

Every static cl::opt constructor references GlobalParser and the TopLevelSubCommand/AllSubCommands ManagedStatics, namespace-scope globals in another translation unit. Although these are constant- initialized in practice, Coverity's GLOBAL_INIT_ORDER checker cannot prove it and reports one 'Initialization or destruction ordering is unspecified' finding per option -- 15,356 of the 34,356 outstanding defects (44.7%) in the LLVM Coverity project.

Move the three registries into block-scope statics behind accessor functions. Block-scope statics are initialized on first use, so the cross-TU initialization-order hazard pattern disappears while the ManagedStatic semantics (lazy construction, destruction via llvm_shutdown) are preserved unchanged.

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


1 Files Affected:

- (modified) llvm/lib/Support/CommandLine.cpp (+50-40) 


``````````diff
diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index a9102fe30efe3..03ef86bb225d3 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -427,7 +427,15 @@ class CommandLineParser {
 
 } // namespace
 
-static ManagedStatic<CommandLineParser> GlobalParser;
+// The global parser is kept as a block-scope static so that option
+// constructors running during dynamic initialization of other translation
+// units never reference a namespace-scope global whose initialization order
+// is unspecified. The ManagedStatic keeps construction lazy and destruction
+// tied to llvm_shutdown().
+static CommandLineParser &globalParser() {
+  static ManagedStatic<CommandLineParser> GlobalParser;
+  return *GlobalParser;
+}
 
 template <typename T, T TrueVal, T FalseVal>
 static bool parseBool(Option &O, StringRef ArgName, StringRef Arg, T &Value) {
@@ -446,11 +454,11 @@ static bool parseBool(Option &O, StringRef ArgName, StringRef Arg, T &Value) {
 }
 
 void cl::AddLiteralOption(Option &O, StringRef Name) {
-  GlobalParser->addLiteralOption(O, Name);
+  globalParser().addLiteralOption(O, Name);
 }
 
 extrahelp::extrahelp(StringRef Help) : morehelp(Help) {
-  GlobalParser->MoreHelp.push_back(Help);
+  globalParser().MoreHelp.push_back(Help);
 }
 
 Option::Option(NumOccurrencesFlag OccurrencesFlag, OptionHidden Hidden)
@@ -461,15 +469,15 @@ Option::Option(NumOccurrencesFlag OccurrencesFlag, OptionHidden Hidden)
 }
 
 void Option::addArgument() {
-  GlobalParser->addOption(this);
+  globalParser().addOption(this);
   FullyInitialized = true;
 }
 
-void Option::removeArgument() { GlobalParser->removeOption(this); }
+void Option::removeArgument() { globalParser().removeOption(this); }
 
 void Option::setArgStr(StringRef S) {
   if (FullyInitialized)
-    GlobalParser->updateArgStr(this, S);
+    globalParser().updateArgStr(this, S);
   assert(!S.starts_with("-") && "Option can't start with '-");
   ArgStr = S;
   if (ArgStr.size() == 1)
@@ -495,29 +503,31 @@ void Option::reset() {
 }
 
 void OptionCategory::registerCategory() {
-  GlobalParser->registerCategory(this);
+  globalParser().registerCategory(this);
 }
 
-// A special subcommand representing no subcommand. It is particularly important
-// that this ManagedStatic uses constant initailization and not dynamic
-// initialization because it is referenced from cl::opt constructors, which run
-// dynamically in an arbitrary order.
-LLVM_REQUIRE_CONSTANT_INITIALIZATION
-static ManagedStatic<SubCommand> TopLevelSubCommand;
+// A special subcommand representing no subcommand. It is kept as a
+// block-scope static because it is referenced from cl::opt constructors,
+// which run dynamically in an arbitrary order across translation units;
+// block-scope statics are initialized on first use and therefore have no
+// initialization-order hazard.
+SubCommand &SubCommand::getTopLevel() {
+  static ManagedStatic<SubCommand> TopLevelSubCommand;
+  return *TopLevelSubCommand;
+}
 
 // A special subcommand that can be used to put an option into all subcommands.
-static ManagedStatic<SubCommand> AllSubCommands;
-
-SubCommand &SubCommand::getTopLevel() { return *TopLevelSubCommand; }
-
-SubCommand &SubCommand::getAll() { return *AllSubCommands; }
+SubCommand &SubCommand::getAll() {
+  static ManagedStatic<SubCommand> AllSubCommands;
+  return *AllSubCommands;
+}
 
 void SubCommand::registerSubCommand() {
-  GlobalParser->registerSubCommand(this);
+  globalParser().registerSubCommand(this);
 }
 
 void SubCommand::unregisterSubCommand() {
-  GlobalParser->unregisterSubCommand(this);
+  globalParser().unregisterSubCommand(this);
 }
 
 void SubCommand::reset() {
@@ -529,7 +539,7 @@ void SubCommand::reset() {
 }
 
 SubCommand::operator bool() const {
-  return (GlobalParser->getActiveSubCommand() == this);
+  return (globalParser().getActiveSubCommand() == this);
 }
 
 //===----------------------------------------------------------------------===//
@@ -1495,7 +1505,7 @@ bool cl::ParseCommandLineOptions(int argc, const char *const *argv,
   int NewArgc = static_cast<int>(NewArgv.size());
 
   // Parse all options.
-  return GlobalParser->ParseCommandLineOptions(
+  return globalParser().ParseCommandLineOptions(
       NewArgc, &NewArgv[0], Overview, Errs, VFS, LongOptionsUseDoubleDash);
 }
 
@@ -1573,7 +1583,7 @@ bool CommandLineParser::ParseCommandLineOptions(
     if (ChosenSubCommand != &SubCommand::getTopLevel())
       FirstArg = 2;
   }
-  GlobalParser->ActiveSubCommand = ChosenSubCommand;
+  globalParser().ActiveSubCommand = ChosenSubCommand;
 
   assert(ChosenSubCommand);
   auto &ConsumeAfterOpt = ChosenSubCommand->ConsumeAfterOpt;
@@ -1891,7 +1901,7 @@ bool Option::error(const Twine &Message, StringRef ArgName, raw_ostream &Errs) {
   if (ArgName.empty())
     Errs << HelpStr; // Be nice for positional arguments
   else
-    Errs << GlobalParser->ProgramName << ": for the " << PrintArg(ArgName, 0);
+    Errs << globalParser().ProgramName << ": for the " << PrintArg(ArgName, 0);
 
   Errs << " option: " << Message << "\n";
   return true;
@@ -2446,7 +2456,7 @@ class HelpPrinter {
   }
 
   void printHelp() {
-    SubCommand *Sub = GlobalParser->getActiveSubCommand();
+    SubCommand *Sub = globalParser().getActiveSubCommand();
     auto &OptionsMap = Sub->OptionsMap;
     auto &PositionalOpts = Sub->PositionalOpts;
     auto &ConsumeAfterOpt = Sub->ConsumeAfterOpt;
@@ -2455,13 +2465,13 @@ class HelpPrinter {
     sortOpts(OptionsMap, Opts, ShowHidden);
 
     StrSubCommandPairVector Subs;
-    sortSubCommands(GlobalParser->RegisteredSubCommands, Subs);
+    sortSubCommands(globalParser().RegisteredSubCommands, Subs);
 
-    if (!GlobalParser->ProgramOverview.empty())
-      outs() << "OVERVIEW: " << GlobalParser->ProgramOverview << "\n";
+    if (!globalParser().ProgramOverview.empty())
+      outs() << "OVERVIEW: " << globalParser().ProgramOverview << "\n";
 
     if (Sub == &SubCommand::getTopLevel()) {
-      outs() << "USAGE: " << GlobalParser->ProgramName;
+      outs() << "USAGE: " << globalParser().ProgramName;
       if (!Subs.empty())
         outs() << " [subcommand]";
       outs() << " [options]";
@@ -2470,7 +2480,7 @@ class HelpPrinter {
         outs() << "SUBCOMMAND '" << Sub->getName()
                << "': " << Sub->getDescription() << "\n\n";
       }
-      outs() << "USAGE: " << GlobalParser->ProgramName << " " << Sub->getName()
+      outs() << "USAGE: " << globalParser().ProgramName << " " << Sub->getName()
              << " [options]";
     }
 
@@ -2494,7 +2504,7 @@ class HelpPrinter {
       outs() << "SUBCOMMANDS:\n\n";
       printSubCommands(Subs, MaxSubLen);
       outs() << "\n";
-      outs() << "  Type \"" << GlobalParser->ProgramName
+      outs() << "  Type \"" << globalParser().ProgramName
              << " <subcommand> --help\" to get more help on a specific "
                 "subcommand";
     }
@@ -2510,9 +2520,9 @@ class HelpPrinter {
     printOptions(Opts, MaxArgLen);
 
     // Print any extra help the user has declared.
-    for (const auto &I : GlobalParser->MoreHelp)
+    for (const auto &I : globalParser().MoreHelp)
       outs() << I;
-    GlobalParser->MoreHelp.clear();
+    globalParser().MoreHelp.clear();
   }
 };
 
@@ -2540,7 +2550,7 @@ class CategorizedHelpPrinter : public HelpPrinter {
     // Collect registered option categories into vector in preparation for
     // sorting.
     llvm::append_range(SortedCategories,
-                       GlobalParser->RegisteredOptionCategories);
+                       globalParser().RegisteredOptionCategories);
 
     // Sort the different option categories alphabetically.
     assert(SortedCategories.size() > 0 && "No option categories registered!");
@@ -2788,7 +2798,7 @@ void HelpPrinterWrapper::operator=(bool Value) {
   // Decide which printer to invoke. If more than one option category is
   // registered then it is useful to show the categorized help instead of
   // uncategorized help.
-  if (GlobalParser->RegisteredOptionCategories.size() > 1) {
+  if (globalParser().RegisteredOptionCategories.size() > 1) {
     // unhide --help-list option so user can have uncategorized output if they
     // want it.
     CommonOptions->HLOp.setHiddenFlag(NotHidden);
@@ -2800,7 +2810,7 @@ void HelpPrinterWrapper::operator=(bool Value) {
 }
 
 // Print the value of each option.
-void cl::PrintOptionValues() { GlobalParser->printOptionValues(); }
+void cl::PrintOptionValues() { globalParser().printOptionValues(); }
 
 void CommandLineParser::printOptionValues() {
   if (!CommonOptions->PrintOptions && !CommonOptions->PrintAllOptions)
@@ -2894,7 +2904,7 @@ void cl::AddExtraVersionPrinter(VersionPrinterTy func) {
 
 OptionsMapTy &cl::getRegisteredOptions(SubCommand &Sub) {
   initCommonOptions();
-  auto &Subs = GlobalParser->RegisteredSubCommands;
+  auto &Subs = globalParser().RegisteredSubCommands;
   (void)Subs;
   assert(Subs.contains(&Sub));
   return Sub.OptionsMap;
@@ -2902,7 +2912,7 @@ OptionsMapTy &cl::getRegisteredOptions(SubCommand &Sub) {
 
 iterator_range<SmallPtrSet<SubCommand *, 4>::iterator>
 cl::getRegisteredSubcommands() {
-  return GlobalParser->getRegisteredSubcommands();
+  return globalParser().getRegisteredSubcommands();
 }
 
 void cl::HideUnrelatedOptions(cl::OptionCategory &Category, SubCommand &Sub) {
@@ -2933,9 +2943,9 @@ void cl::HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *> Categories,
   }
 }
 
-void cl::ResetCommandLineParser() { GlobalParser->reset(); }
+void cl::ResetCommandLineParser() { globalParser().reset(); }
 void cl::ResetAllOptionOccurrences() {
-  GlobalParser->ResetAllOptionOccurrences();
+  globalParser().ResetAllOptionOccurrences();
 }
 
 void LLVMParseCommandLineOptions(int argc, const char *const *argv,

``````````

</details>


https://github.com/llvm/llvm-project/pull/215216


More information about the llvm-commits mailing list