jansvoboda11 updated this revision to Diff 319526.
jansvoboda11 added a comment.

Compare once and twice generated arguments, fix re-ordering of UserEntries


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D94472/new/

https://reviews.llvm.org/D94472

Files:
  clang/include/clang/Driver/Options.td
  clang/include/clang/Frontend/CompilerInvocation.h
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  llvm/include/llvm/Option/ArgList.h
  llvm/lib/Option/ArgList.cpp

Index: llvm/lib/Option/ArgList.cpp
===================================================================
--- llvm/lib/Option/ArgList.cpp
+++ llvm/lib/Option/ArgList.cpp
@@ -90,11 +90,22 @@
 }
 
 std::vector<std::string> ArgList::getAllArgValues(OptSpecifier Id) const {
+  recordQueriedOpts(Id);
   SmallVector<const char *, 16> Values;
   AddAllArgValues(Values, Id);
   return std::vector<std::string>(Values.begin(), Values.end());
 }
 
+void ArgList::AddAllArgsExcept(ArgStringList &Output,
+                               const DenseSet<unsigned> &ExcludeIds) const {
+  for (const Arg *Arg : *this) {
+    if (!ExcludeIds.contains(Arg->getOption().getID())) {
+      Arg->claim();
+      Arg->render(*this, Output);
+    }
+  }
+}
+
 void ArgList::AddAllArgsExcept(ArgStringList &Output,
                                ArrayRef<OptSpecifier> Ids,
                                ArrayRef<OptSpecifier> ExcludeIds) const {
Index: llvm/include/llvm/Option/ArgList.h
===================================================================
--- llvm/include/llvm/Option/ArgList.h
+++ llvm/include/llvm/Option/ArgList.h
@@ -137,6 +137,16 @@
   /// The first and last index of each different OptSpecifier ID.
   DenseMap<unsigned, OptRange> OptRanges;
 
+  /// The OptSpecifiers that were queried from this argument list.
+  mutable DenseSet<unsigned> QueriedOpts;
+
+  /// Record the queried OptSpecifiers.
+  template <typename... OptSpecifiers>
+  void recordQueriedOpts(OptSpecifiers... Ids) const {
+    SmallVector<unsigned, 4> OptsSpecifiers({toOptSpecifier(Ids).getID()...});
+    QueriedOpts.insert(OptsSpecifiers.begin(), OptsSpecifiers.end());
+  }
+
   /// Get the range of indexes in which options with the specified IDs might
   /// reside, or (0, 0) if there are no such options.
   OptRange getRange(std::initializer_list<OptSpecifier> Ids) const;
@@ -203,6 +213,7 @@
   template<typename ...OptSpecifiers>
   iterator_range<filtered_iterator<sizeof...(OptSpecifiers)>>
   filtered(OptSpecifiers ...Ids) const {
+    recordQueriedOpts(Ids...);
     OptRange Range = getRange({toOptSpecifier(Ids)...});
     auto B = Args.begin() + Range.first;
     auto E = Args.begin() + Range.second;
@@ -214,6 +225,7 @@
   template<typename ...OptSpecifiers>
   iterator_range<filtered_reverse_iterator<sizeof...(OptSpecifiers)>>
   filtered_reverse(OptSpecifiers ...Ids) const {
+    recordQueriedOpts(Ids...);
     OptRange Range = getRange({toOptSpecifier(Ids)...});
     auto B = Args.rend() - Range.second;
     auto E = Args.rend() - Range.first;
@@ -308,6 +320,10 @@
       A->render(*this, Output);
   }
 
+  /// AddAllArgsExcept - Render all arguments not matching any of the excluded
+  /// ids.
+  void AddAllArgsExcept(ArgStringList &Output,
+                        const DenseSet<unsigned> &ExcludeIds) const;
   /// AddAllArgsExcept - Render all arguments matching any of the given ids
   /// and not matching any of the excluded ids.
   void AddAllArgsExcept(ArgStringList &Output, ArrayRef<OptSpecifier> Ids,
@@ -342,6 +358,12 @@
   ///
   void ClaimAllArgs() const;
 
+  /// Return the OptSpecifiers queried from this argument list.
+  const DenseSet<unsigned> &getQueriedOpts() const { return QueriedOpts; }
+
+  /// Clear the set of queried OptSpecifiers.
+  void clearQueriedOpts() const { QueriedOpts.clear(); }
+
   /// @}
   /// @name Arg Synthesis
   /// @{
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -48,6 +48,7 @@
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/CachedHashString.h"
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/FloatingPointMode.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/None.h"
@@ -204,7 +205,7 @@
                                   const char *Spelling,
                                   CompilerInvocation::StringAllocator SA,
                                   Option::OptionClass OptClass, unsigned,
-                                  Twine Value) {
+                                  const Twine &Value) {
   switch (OptClass) {
   case Option::SeparateClass:
   case Option::JoinedOrSeparateClass:
@@ -523,9 +524,94 @@
   return 0;
 }
 
-static std::string GetOptName(llvm::opt::OptSpecifier OptSpecifier) {
-  static const OptTable &OptTable = getDriverOptTable();
-  return OptTable.getOption(OptSpecifier).getPrefixedName();
+static void GenerateArg(SmallVectorImpl<const char *> &Args,
+                        llvm::opt::OptSpecifier OptSpecifier,
+                        CompilerInvocation::StringAllocator SA) {
+  Option Opt = getDriverOptTable().getOption(OptSpecifier);
+  denormalizeSimpleFlag(Args, SA(Opt.getPrefix() + Opt.getName()), SA,
+                        Option::OptionClass::FlagClass, 0);
+}
+
+static void GenerateArg(SmallVectorImpl<const char *> &Args,
+                        llvm::opt::OptSpecifier OptSpecifier,
+                        const Twine &Value,
+                        CompilerInvocation::StringAllocator SA) {
+  Option Opt = getDriverOptTable().getOption(OptSpecifier);
+  denormalizeString(Args, SA(Opt.getPrefix() + Opt.getName()), SA,
+                    Opt.getKind(), 0, Value);
+}
+
+static void DebugArgs(ArgStringList &Args, const char *Description) {
+  llvm::dbgs() << Description;
+  llvm::interleave(Args, llvm::dbgs(), " ");
+  llvm::dbgs() << "\n";
+}
+
+template <typename ParseFn, typename GenerateFn, typename SwapFn>
+static void RoundTrip(ParseFn &&Parse, GenerateFn &&Generate, SwapFn &&Swap,
+                      CompilerInvocation &Res, ArgList &OriginalArgs) {
+  bool DoRoundTrip = OriginalArgs.hasArg(OPT_round_trip_args);
+  bool DebugRoundTrip = OriginalArgs.hasArg(OPT_round_trip_args_debug);
+
+  if (!DoRoundTrip) {
+    Parse(Res, OriginalArgs);
+    return;
+  }
+
+  auto SA = [&Res](const Twine &Arg) { return Res.AllocateString(Arg); };
+
+  // Invokes the Parse functor and returns set of queried command line options.
+  auto ParseAndGetOpts = [&Parse](CompilerInvocation &Res, ArgList &Args) {
+    Args.clearQueriedOpts();
+    Parse(Res, Args);
+    return Args.getQueriedOpts();
+  };
+
+  // Generates new argument string list. All arguments except those in Tested
+  // are taken directly from Args. Arguments in Tested are generated by calling
+  // the Generate functor.
+  auto GenerateArgs = [&Generate, &SA](CompilerInvocation &Res, ArgList &Args,
+                                       llvm::DenseSet<unsigned> Tested) {
+    ArgStringList Generated;
+    Args.AddAllArgsExcept(Generated, Tested);
+    Generate(Res, Args, Generated, SA);
+    return Generated;
+  };
+
+  llvm::DenseSet<unsigned> QueriedBefore = OriginalArgs.getQueriedOpts();
+
+  // Run the first parse (of original arguments) on a dummy instance.
+  Swap(Res);
+
+  llvm::DenseSet<unsigned> QueriedOpts1 = ParseAndGetOpts(Res, OriginalArgs);
+
+  ArgStringList GenArgs1Strs = GenerateArgs(Res, OriginalArgs, QueriedOpts1);
+
+  unsigned MissingArgIndex, MissingArgCount;
+  InputArgList GenArgs1 = getDriverOptTable().ParseArgs(
+      GenArgs1Strs, MissingArgIndex, MissingArgCount, options::CC1Option);
+
+  // Run the second parse (of generated arguments) on the original instance.
+  Swap(Res);
+
+  llvm::DenseSet<unsigned> QueriedOpts2 = ParseAndGetOpts(Res, GenArgs1);
+
+  ArgStringList GenArgs2Strs = GenerateArgs(Res, GenArgs1, QueriedOpts2);
+
+  bool GenArgsMatch =
+      std::equal(GenArgs1Strs.begin(), GenArgs1Strs.end(), GenArgs2Strs.begin(),
+                 GenArgs2Strs.end(), [](const char *A, const char *B) {
+                   return StringRef(A) == StringRef(B);
+                 });
+
+  if (!GenArgsMatch || DebugRoundTrip) {
+    DebugArgs(GenArgs1Strs, "Generated args #1: ");
+    DebugArgs(GenArgs2Strs, "Generated args #2: ");
+  }
+
+  if (!GenArgsMatch)
+    llvm::report_fatal_error("Mismatch between generated arguments during "
+                             "round-tripping.");
 }
 
 static void addDiagnosticArgs(ArgList &Args, OptSpecifier Group,
@@ -1799,9 +1885,9 @@
   return Driver::GetResourcesPath(ClangExecutable, CLANG_RESOURCE_DIR);
 }
 
-static void GenerateHeaderSearchArgs(const HeaderSearchOptions &Opts,
-                                     SmallVectorImpl<const char *> &Args,
-                                     CompilerInvocation::StringAllocator SA) {
+void CompilerInvocation::GenerateHeaderSearchArgs(
+    HeaderSearchOptions &Opts, SmallVectorImpl<const char *> &Args,
+    CompilerInvocation::StringAllocator SA) {
   const HeaderSearchOptions *HeaderSearchOpts = &Opts;
 #define HEADER_SEARCH_OPTION_WITH_MARSHALLING(                                 \
     PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,        \
@@ -1813,6 +1899,116 @@
       IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
 #include "clang/Driver/Options.inc"
 #undef HEADER_SEARCH_OPTION_WITH_MARSHALLING
+
+  if (Opts.UseLibcxx)
+    GenerateArg(Args, OPT_stdlib_EQ, "libc++", SA);
+
+  if (!Opts.ModuleCachePath.empty())
+    GenerateArg(Args, OPT_fmodules_cache_path, Opts.ModuleCachePath, SA);
+
+  for (const auto &File : Opts.PrebuiltModuleFiles)
+    GenerateArg(Args, OPT_fmodule_file, File.first + "=" + File.second, SA);
+
+  for (const auto &Path : Opts.PrebuiltModulePaths)
+    GenerateArg(Args, OPT_fprebuilt_module_path, Path, SA);
+
+  for (const auto &Macro : Opts.ModulesIgnoreMacros)
+    GenerateArg(Args, OPT_fmodules_ignore_macro, Macro.val(), SA);
+
+  auto Matches = [](const HeaderSearchOptions::Entry &Entry,
+                    llvm::ArrayRef<frontend::IncludeDirGroup> Groups,
+                    llvm::Optional<bool> IsFramework,
+                    llvm::Optional<bool> IgnoreSysRoot) {
+    return llvm::find(Groups, Entry.Group) != Groups.end() &&
+           (!IsFramework || (Entry.IsFramework == *IsFramework)) &&
+           (!IgnoreSysRoot || (Entry.IgnoreSysRoot == *IgnoreSysRoot));
+  };
+
+  auto It = Opts.UserEntries.begin();
+  auto End = Opts.UserEntries.end();
+
+  // Add -I..., -F..., and -index-header-map options in order.
+  for (; It < End &&
+         Matches(*It, {frontend::IndexHeaderMap, frontend::Angled}, None, true);
+       ++It) {
+    OptSpecifier Opt = [It, Matches]() {
+      if (Matches(*It, frontend::IndexHeaderMap, true, true))
+        return OPT_F;
+      if (Matches(*It, frontend::IndexHeaderMap, false, true))
+        return OPT_I;
+      if (Matches(*It, frontend::Angled, true, true))
+        return OPT_F;
+      if (Matches(*It, frontend::Angled, false, true))
+        return OPT_I;
+      llvm_unreachable("Unexpected HeaderSearchOptions::Entry.");
+    }();
+
+    if (It->Group == frontend::IndexHeaderMap)
+      GenerateArg(Args, OPT_index_header_map, SA);
+    GenerateArg(Args, Opt, It->Path, SA);
+  };
+
+  // Note: some paths that came from "[-iprefix=xx] -iwithprefixbefore=yy" may
+  // have already been generated as "-I[xx]yy". If that's the case, their
+  // position on command line was such that this has no semantic impact on
+  // include paths.
+  for (; It < End &&
+         Matches(*It, {frontend::After, frontend::Angled}, false, true);
+       ++It) {
+    OptSpecifier Opt =
+        It->Group == frontend::After ? OPT_iwithprefix : OPT_iwithprefixbefore;
+    GenerateArg(Args, Opt, It->Path, SA);
+  }
+
+  // Note: Some paths that came from "-idirafter=xxyy" may have already been
+  // generated as "-iwithprefix=xxyy". If that's the case, their position on
+  // command line was such that this has no semantic impact on include paths.
+  for (; It < End && Matches(*It, {frontend::After}, false, true); ++It)
+    GenerateArg(Args, OPT_idirafter, It->Path, SA);
+  for (; It < End && Matches(*It, {frontend::Quoted}, false, true); ++It)
+    GenerateArg(Args, OPT_iquote, It->Path, SA);
+  for (; It < End && Matches(*It, {frontend::System}, false, None); ++It)
+    GenerateArg(Args, It->IgnoreSysRoot ? OPT_isystem : OPT_iwithsysroot,
+                It->Path, SA);
+  for (; It < End && Matches(*It, {frontend::System}, true, true); ++It)
+    GenerateArg(Args, OPT_iframework, It->Path, SA);
+  for (; It < End && Matches(*It, {frontend::System}, true, false); ++It)
+    GenerateArg(Args, OPT_iframeworkwithsysroot, It->Path, SA);
+
+  // Add the paths for the various language specific isystem flags.
+  for (; It < End && Matches(*It, {frontend::CSystem}, false, true); ++It)
+    GenerateArg(Args, OPT_c_isystem, It->Path, SA);
+  for (; It < End && Matches(*It, {frontend::CXXSystem}, false, true); ++It)
+    GenerateArg(Args, OPT_cxx_isystem, It->Path, SA);
+  for (; It < End && Matches(*It, {frontend::ObjCSystem}, false, true); ++It)
+    GenerateArg(Args, OPT_objc_isystem, It->Path, SA);
+  for (; It < End && Matches(*It, {frontend::ObjCXXSystem}, false, true); ++It)
+    GenerateArg(Args, OPT_objcxx_isystem, It->Path, SA);
+
+  // Add the internal paths from a driver that detects standard include paths.
+  // Note: Some paths that came from "-internal-isystem" arguments may have
+  // already been generated as "-isystem". If that's the case, their position on
+  // command line was such that this has no semantic impact on include paths.
+  for (; It < End &&
+         Matches(*It, {frontend::System, frontend::ExternCSystem}, false, true);
+       ++It) {
+    OptSpecifier Opt = It->Group == frontend::System
+                           ? OPT_internal_isystem
+                           : OPT_internal_externc_isystem;
+    GenerateArg(Args, Opt, It->Path, SA);
+  }
+
+  assert(It == End && "Unhandled HeaderSearchOption::Entry.");
+
+  // Add the path prefixes which are implicitly treated as being system headers.
+  for (const auto &P : Opts.SystemHeaderPrefixes) {
+    OptSpecifier Opt = P.IsSystemHeader ? OPT_system_header_prefix
+                                        : OPT_no_system_header_prefix;
+    GenerateArg(Args, Opt, P.Prefix, SA);
+  }
+
+  for (const std::string &F : Opts.VFSOverlayFiles)
+    GenerateArg(Args, OPT_ivfsoverlay, F, SA);
 }
 
 static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args,
@@ -1948,6 +2144,31 @@
     Opts.AddVFSOverlayFile(A->getValue());
 }
 
+void CompilerInvocation::ParseHeaderSearchArgs(CompilerInvocation &Res,
+                                               HeaderSearchOptions &Opts,
+                                               ArgList &Args,
+                                               DiagnosticsEngine &Diags,
+                                               const std::string &WorkingDir) {
+  auto Temp = std::make_shared<HeaderSearchOptions>();
+
+  auto Parse = [&Diags, &WorkingDir](CompilerInvocation &Res, ArgList &Args) {
+    ::ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Diags, WorkingDir);
+  };
+
+  auto Generate = [](CompilerInvocation &Res,
+                     ArgList &OriginalArgs,
+                     SmallVectorImpl<const char *> &GeneratedArgs,
+                     CompilerInvocation::StringAllocator SA) {
+    GenerateHeaderSearchArgs(Res.getHeaderSearchOpts(), GeneratedArgs, SA);
+  };
+
+  auto Swap = [&Temp](CompilerInvocation &Res) {
+    Res.HeaderSearchOpts.swap(Temp);
+  };
+
+  RoundTrip(Parse, Generate, Swap, Res, Args);
+}
+
 void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
                                          const llvm::Triple &T,
                                          std::vector<std::string> &Includes,
@@ -2183,9 +2404,9 @@
                              SmallVectorImpl<const char *> &Args,
                              CompilerInvocation::StringAllocator SA) {
   if (Opts.IncludeDefaultHeader)
-    Args.push_back(SA(GetOptName(OPT_finclude_default_header)));
+    GenerateArg(Args, OPT_finclude_default_header, SA);
   if (Opts.DeclareOpenCLBuiltins)
-    Args.push_back(SA(GetOptName(OPT_fdeclare_opencl_builtins)));
+    GenerateArg(Args, OPT_fdeclare_opencl_builtins, SA);
 }
 
 void CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
@@ -2966,7 +3187,7 @@
                                       LangOpts.IsHeaderFile);
   ParseTargetArgs(Res.getTargetOpts(), Args, Diags);
   llvm::Triple T(Res.getTargetOpts().Triple);
-  ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Diags,
+  ParseHeaderSearchArgs(Res, Res.getHeaderSearchOpts(), Args, Diags,
                         Res.getFileSystemOpts().WorkingDir);
   if (DashX.getFormat() == InputKind::Precompiled ||
       DashX.getLanguage() == Language::LLVM_IR) {
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -4475,6 +4475,9 @@
   const bool IsAssertBuild = true;
 #endif
 
+  if (IsAssertBuild)
+    CmdArgs.push_back("-round-trip-args");
+
   // Disable the verification pass in -asserts builds.
   if (!IsAssertBuild)
     CmdArgs.push_back("-disable-llvm-verifier");
Index: clang/include/clang/Frontend/CompilerInvocation.h
===================================================================
--- clang/include/clang/Frontend/CompilerInvocation.h
+++ clang/include/clang/Frontend/CompilerInvocation.h
@@ -272,6 +272,18 @@
                                const llvm::Triple &T,
                                const std::string &OutputFile,
                                const LangOptions &LangOptsRef);
+
+  /// Parse commnad line options that map to HeaderSearchOptions.
+  static void ParseHeaderSearchArgs(CompilerInvocation &Res,
+                                    HeaderSearchOptions &Opts,
+                                    llvm::opt::ArgList &Args,
+                                    DiagnosticsEngine &Diags,
+                                    const std::string &WorkingDir);
+
+  /// Generate command line options from HeaderSearchOptions.
+  static void GenerateHeaderSearchArgs(HeaderSearchOptions &Opts,
+                                       SmallVectorImpl<const char *> &Args,
+                                       CompilerInvocation::StringAllocator SA);
 };
 
 IntrusiveRefCntPtr<llvm::vfs::FileSystem>
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -590,6 +590,11 @@
 def gen_cdb_fragment_path: Separate<["-"], "gen-cdb-fragment-path">, InternalDebugOpt,
   HelpText<"Emit a compilation database fragment to the specified directory">;
 
+def round_trip_args : Flag<["-"], "round-trip-args">, Flags<[CC1Option, NoDriverOption]>,
+  HelpText<"Performs 'parse-generate-parse' round-trip of command line arguments.">;
+def round_trip_args_debug : Flag<["-"], "round-trip-args-debug">, Flags<[CC1Option, NoDriverOption]>,
+  HelpText<"Print debug information when performing the 'parse-generate-parse' round-trip of command line arguments.">;
+
 def _migrate : Flag<["--"], "migrate">, Flags<[NoXarchOption]>,
   HelpText<"Run the migrator">;
 def ccc_objcmt_migrate : Separate<["-"], "ccc-objcmt-migrate">,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to