[clang][cli] Generate and round-trip language options
authorJan Svoboda <jan_svoboda@apple.com>
Tue, 9 Feb 2021 09:17:04 +0000 (10:17 +0100)
committerJan Svoboda <jan_svoboda@apple.com>
Tue, 9 Feb 2021 09:18:55 +0000 (10:18 +0100)
This patch implements generation of remaining language options and tests it by performing parse-generate-parse round trip (on by default for assert builds, off otherwise).

This patch also correctly reports failures in `parseSanitizerKinds`, which is necessary for emitting diagnostics when an invalid sanitizer is passed to `-fsanitize=` during round-trip.

This patch also removes TableGen marshalling classes from two options:
* `fsanitize_blacklist` When parsing: it's first initialized via the generated code, but then also changed by manually written code, which is confusing.
* `fopenmp` When parsing: it's first initialized via generated code, but then conditionally changed by manually written code. This is also confusing. Moreover, we need to do some extra checks when generating it, which would be really cumbersome in TableGen. (Specifically, not emitting it when `-fopenmp-simd` was present.)

Reviewed By: dexonsmith

Differential Revision: https://reviews.llvm.org/D95793

clang/include/clang/Basic/Sanitizers.h
clang/include/clang/Driver/Options.td
clang/include/clang/Frontend/CompilerInvocation.h
clang/lib/Basic/Sanitizers.cpp
clang/lib/Frontend/CompilerInvocation.cpp
clang/unittests/Basic/CMakeLists.txt
clang/unittests/Basic/SanitizersTest.cpp [new file with mode: 0644]

index c6b0446..675fa15 100644 (file)
@@ -178,6 +178,10 @@ struct SanitizerSet {
 /// Returns a non-zero SanitizerMask, or \c 0 if \p Value is not known.
 SanitizerMask parseSanitizerValue(StringRef Value, bool AllowGroups);
 
+/// Serialize a SanitizerSet into values for -fsanitize= or -fno-sanitize=.
+void serializeSanitizerSet(SanitizerSet Set,
+                           SmallVectorImpl<StringRef> &Values);
+
 /// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers
 /// this group enables.
 SanitizerMask expandSanitizerGroups(SanitizerMask Kinds);
index 3f7519f..baea96d 100644 (file)
@@ -1423,8 +1423,7 @@ def fno_sanitize_EQ : CommaJoined<["-"], "fno-sanitize=">, Group<f_clang_Group>,
                       Flags<[CoreOption, NoXarchOption]>;
 def fsanitize_blacklist : Joined<["-"], "fsanitize-blacklist=">,
                           Group<f_clang_Group>,
-                          HelpText<"Path to blacklist file for sanitizers">,
-                          MarshallingInfoStringVector<LangOpts<"SanitizerBlacklistFiles">>;
+                          HelpText<"Path to blacklist file for sanitizers">;
 def fsanitize_system_blacklist : Joined<["-"], "fsanitize-system-blacklist=">,
   HelpText<"Path to system blacklist file for sanitizers">,
   Flags<[CC1Option]>;
@@ -2159,8 +2158,7 @@ def fno_objc_nonfragile_abi : Flag<["-"], "fno-objc-nonfragile-abi">, Group<f_Gr
 def fobjc_sender_dependent_dispatch : Flag<["-"], "fobjc-sender-dependent-dispatch">, Group<f_Group>;
 def fomit_frame_pointer : Flag<["-"], "fomit-frame-pointer">, Group<f_Group>;
 def fopenmp : Flag<["-"], "fopenmp">, Group<f_Group>, Flags<[CC1Option, NoArgumentUnused]>,
-  HelpText<"Parse OpenMP pragmas and generate parallel code.">,
-  MarshallingInfoFlag<LangOpts<"OpenMP">, "0u">, Normalizer<"makeFlagToValueNormalizer(50u)">;
+  HelpText<"Parse OpenMP pragmas and generate parallel code.">;
 def fno_openmp : Flag<["-"], "fno-openmp">, Group<f_Group>, Flags<[NoArgumentUnused]>;
 def fopenmp_version_EQ : Joined<["-"], "fopenmp-version=">, Group<f_Group>, Flags<[CC1Option, NoArgumentUnused]>;
 def fopenmp_EQ : Joined<["-"], "fopenmp=">, Group<f_Group>;
index e880713..0b53c86 100644 (file)
@@ -249,11 +249,22 @@ private:
                        DiagnosticsEngine &Diags);
 
   /// Parse command line options that map to LangOptions.
-  static bool ParseLangArgs(LangOptions &Opts, llvm::opt::ArgList &Args,
-                            InputKind IK, const llvm::Triple &T,
+  static bool ParseLangArgsImpl(LangOptions &Opts, llvm::opt::ArgList &Args,
+                                InputKind IK, const llvm::Triple &T,
+                                std::vector<std::string> &Includes,
+                                DiagnosticsEngine &Diags);
+
+  static bool ParseLangArgs(CompilerInvocation &Res, LangOptions &Opts,
+                            llvm::opt::ArgList &Args, InputKind IK,
+                            const llvm::Triple &T,
                             std::vector<std::string> &Includes,
                             DiagnosticsEngine &Diags);
 
+  /// Generate command line options from LangOptions.
+  static void GenerateLangArgs(const LangOptions &Opts,
+                               SmallVectorImpl<const char *> &Args,
+                               StringAllocator SA, const llvm::Triple &T);
+
   /// Parse command line options that map to CodeGenOptions.
   static bool ParseCodeGenArgs(CodeGenOptions &Opts, llvm::opt::ArgList &Args,
                                InputKind IK, DiagnosticsEngine &Diags,
index f5f81b5..19882cd 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "clang/Basic/Sanitizers.h"
 #include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringSwitch.h"
 
 using namespace clang;
@@ -34,6 +35,14 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
   return ParsedKind;
 }
 
+void clang::serializeSanitizerSet(SanitizerSet Set,
+                                  SmallVectorImpl<StringRef> &Values) {
+#define SANITIZER(NAME, ID)                                                    \
+  if (Set.has(SanitizerKind::ID))                                              \
+    Values.push_back(NAME);
+#include "clang/Basic/Sanitizers.def"
+}
+
 SanitizerMask clang::expandSanitizerGroups(SanitizerMask Kinds) {
 #define SANITIZER(NAME, ID)
 #define SANITIZER_GROUP(NAME, ID, ALIAS)                                       \
index 189e7c6..09444b2 100644 (file)
@@ -214,6 +214,7 @@ static void denormalizeStringImpl(SmallVectorImpl<const char *> &Args,
     Args.push_back(SA(Value));
     break;
   case Option::JoinedClass:
+  case Option::CommaJoinedClass:
     Args.push_back(SA(Twine(Spelling) + Value));
     break;
   default:
@@ -1212,6 +1213,12 @@ static void parseSanitizerKinds(StringRef FlagName,
   }
 }
 
+static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
+  SmallVector<StringRef, 4> Values;
+  serializeSanitizerSet(S, Values);
+  return Values;
+}
+
 static void parseXRayInstrumentationBundle(StringRef FlagName, StringRef Bundle,
                                            ArgList &Args, DiagnosticsEngine &D,
                                            XRayInstrSet &S) {
@@ -2633,19 +2640,231 @@ static const StringRef GetInputKindName(InputKind IK) {
   llvm_unreachable("unknown input language");
 }
 
-static void GenerateLangArgs(const LangOptions &Opts,
-                             SmallVectorImpl<const char *> &Args,
-                             CompilerInvocation::StringAllocator SA) {
+void CompilerInvocation::GenerateLangArgs(const LangOptions &Opts,
+                                          SmallVectorImpl<const char *> &Args,
+                                          StringAllocator SA,
+                                          const llvm::Triple &T) {
+  OptSpecifier StdOpt;
+  switch (Opts.LangStd) {
+  case LangStandard::lang_opencl10:
+  case LangStandard::lang_opencl11:
+  case LangStandard::lang_opencl12:
+  case LangStandard::lang_opencl20:
+  case LangStandard::lang_opencl30:
+  case LangStandard::lang_openclcpp:
+    StdOpt = OPT_cl_std_EQ;
+    break;
+  default:
+    StdOpt = OPT_std_EQ;
+    break;
+  }
+
+  auto LangStandard = LangStandard::getLangStandardForKind(Opts.LangStd);
+  GenerateArg(Args, StdOpt, LangStandard.getName(), SA);
+
   if (Opts.IncludeDefaultHeader)
     GenerateArg(Args, OPT_finclude_default_header, SA);
   if (Opts.DeclareOpenCLBuiltins)
     GenerateArg(Args, OPT_fdeclare_opencl_builtins, SA);
+
+  const LangOptions *LangOpts = &Opts;
+
+#define LANG_OPTION_WITH_MARSHALLING(                                          \
+    PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,        \
+    HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH,   \
+    DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER,     \
+    MERGER, EXTRACTOR, TABLE_INDEX)                                            \
+  GENERATE_OPTION_WITH_MARSHALLING(                                            \
+      Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE,    \
+      IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
+#include "clang/Driver/Options.inc"
+#undef LANG_OPTION_WITH_MARSHALLING
+
+  // The '-fcf-protection=' option is generated by CodeGenOpts generator.
+
+  if (Opts.ObjC) {
+    std::string Buffer;
+    llvm::raw_string_ostream Stream(Buffer);
+    Stream << Opts.ObjCRuntime;
+    GenerateArg(Args, OPT_fobjc_runtime_EQ, Stream.str(), SA);
+
+    if (Opts.GC == LangOptions::GCOnly)
+      GenerateArg(Args, OPT_fobjc_gc_only, SA);
+    else if (Opts.GC == LangOptions::HybridGC)
+      GenerateArg(Args, OPT_fobjc_gc, SA);
+    else if (Opts.ObjCAutoRefCount == 1)
+      GenerateArg(Args, OPT_fobjc_arc, SA);
+
+    if (Opts.ObjCWeakRuntime)
+      GenerateArg(Args, OPT_fobjc_runtime_has_weak, SA);
+
+    if (Opts.ObjCWeak)
+      GenerateArg(Args, OPT_fobjc_weak, SA);
+
+    if (Opts.ObjCSubscriptingLegacyRuntime)
+      GenerateArg(Args, OPT_fobjc_subscripting_legacy_runtime, SA);
+  }
+
+  if (Opts.GNUCVersion != 0) {
+    unsigned Major = Opts.GNUCVersion / 100 / 100;
+    unsigned Minor = (Opts.GNUCVersion / 100) % 100;
+    unsigned Patch = Opts.GNUCVersion % 100;
+    GenerateArg(Args, OPT_fgnuc_version_EQ,
+                Twine(Major) + "." + Twine(Minor) + "." + Twine(Patch), SA);
+  }
+
+  if (Opts.SignedOverflowBehavior == LangOptions::SOB_Trapping) {
+    GenerateArg(Args, OPT_ftrapv, SA);
+    GenerateArg(Args, OPT_ftrapv_handler, Opts.OverflowHandler, SA);
+  } else if (Opts.SignedOverflowBehavior == LangOptions::SOB_Defined) {
+    GenerateArg(Args, OPT_fwrapv, SA);
+  }
+
+  if (Opts.MSCompatibilityVersion != 0) {
+    unsigned Major = Opts.MSCompatibilityVersion / 10000000;
+    unsigned Minor = (Opts.MSCompatibilityVersion / 100000) % 100;
+    unsigned Subminor = Opts.MSCompatibilityVersion % 100000;
+    GenerateArg(Args, OPT_fms_compatibility_version,
+                Twine(Major) + "." + Twine(Minor) + "." + Twine(Subminor), SA);
+  }
+
+  if ((!Opts.GNUMode && !Opts.MSVCCompat && !Opts.CPlusPlus17) || T.isOSzOS()) {
+    if (!Opts.Trigraphs)
+      GenerateArg(Args, OPT_fno_trigraphs, SA);
+  } else {
+    if (Opts.Trigraphs)
+      GenerateArg(Args, OPT_ftrigraphs, SA);
+  }
+
+  if (Opts.Blocks && !(Opts.OpenCL && Opts.OpenCLVersion == 200))
+    GenerateArg(Args, OPT_fblocks, SA);
+
+  if (Opts.ConvergentFunctions &&
+      !(Opts.OpenCL || (Opts.CUDA && Opts.CUDAIsDevice) || Opts.SYCLIsDevice))
+    GenerateArg(Args, OPT_fconvergent_functions, SA);
+
+  if (Opts.NoBuiltin && !Opts.Freestanding)
+    GenerateArg(Args, OPT_fno_builtin, SA);
+
+  // Not generating '-fno-builtin-xxx'. It's handled for CodeGenOptions, that
+  // also read OPT_fno_builtin_.
+
+  if (Opts.LongDoubleSize == 128)
+    GenerateArg(Args, OPT_mlong_double_128, SA);
+  else if (Opts.LongDoubleSize == 64)
+    GenerateArg(Args, OPT_mlong_double_64, SA);
+
+  // Not generating '-mrtd', it's just an alias for '-fdefault-calling-conv='.
+
+  // OpenMP was requested via '-fopenmp', not implied by '-fopenmp-simd' or
+  // '-fopenmp-targets='.
+  if (Opts.OpenMP && !Opts.OpenMPSimd) {
+    GenerateArg(Args, OPT_fopenmp, SA);
+
+    if (Opts.OpenMP != 50)
+      GenerateArg(Args, OPT_fopenmp_version_EQ, Twine(Opts.OpenMP), SA);
+
+    if (!Opts.OpenMPUseTLS)
+      GenerateArg(Args, OPT_fnoopenmp_use_tls, SA);
+
+    if (Opts.OpenMPIsDevice)
+      GenerateArg(Args, OPT_fopenmp_is_device, SA);
+
+    if (Opts.OpenMPIRBuilder)
+      GenerateArg(Args, OPT_fopenmp_enable_irbuilder, SA);
+  }
+
+  if (Opts.OpenMPSimd) {
+    GenerateArg(Args, OPT_fopenmp_simd, SA);
+
+    if (Opts.OpenMP != 50)
+      GenerateArg(Args, OPT_fopenmp_version_EQ, Twine(Opts.OpenMP), SA);
+  }
+
+  if (Opts.OpenMPCUDANumSMs != 0)
+    GenerateArg(Args, OPT_fopenmp_cuda_number_of_sm_EQ,
+                Twine(Opts.OpenMPCUDANumSMs), SA);
+
+  if (Opts.OpenMPCUDABlocksPerSM != 0)
+    GenerateArg(Args, OPT_fopenmp_cuda_blocks_per_sm_EQ,
+                Twine(Opts.OpenMPCUDABlocksPerSM), SA);
+
+  if (Opts.OpenMPCUDAReductionBufNum != 1024)
+    GenerateArg(Args, OPT_fopenmp_cuda_teams_reduction_recs_num_EQ,
+                Twine(Opts.OpenMPCUDAReductionBufNum), SA);
+
+  if (!Opts.OMPTargetTriples.empty()) {
+    std::string Targets;
+    llvm::raw_string_ostream OS(Targets);
+    llvm::interleave(
+        Opts.OMPTargetTriples, OS,
+        [&OS](const llvm::Triple &T) { OS << T.str(); }, ",");
+    GenerateArg(Args, OPT_fopenmp_targets_EQ, OS.str(), SA);
+  }
+
+  if (!Opts.OMPHostIRFile.empty())
+    GenerateArg(Args, OPT_fopenmp_host_ir_file_path, Opts.OMPHostIRFile, SA);
+
+  if (Opts.OpenMPCUDAMode)
+    GenerateArg(Args, OPT_fopenmp_cuda_mode, SA);
+
+  if (Opts.OpenMPCUDATargetParallel)
+    GenerateArg(Args, OPT_fopenmp_cuda_parallel_target_regions, SA);
+
+  if (Opts.OpenMPCUDAForceFullRuntime)
+    GenerateArg(Args, OPT_fopenmp_cuda_force_full_runtime, SA);
+
+  // The arguments used to set 'Optimize' and 'OptimizeSize' will be generated
+  // by CodeGenOptions.
+
+  if (Opts.NoInlineDefine && Opts.Optimize)
+    GenerateArg(Args, OPT_fno_inline, SA);
+
+  if (Opts.DefaultFPContractMode == LangOptions::FPM_Fast)
+    GenerateArg(Args, OPT_ffp_contract, "fast", SA);
+  else if (Opts.DefaultFPContractMode == LangOptions::FPM_On)
+    GenerateArg(Args, OPT_ffp_contract, "on", SA);
+  else if (Opts.DefaultFPContractMode == LangOptions::FPM_Off)
+    GenerateArg(Args, OPT_ffp_contract, "off", SA);
+  else if (Opts.DefaultFPContractMode == LangOptions::FPM_FastHonorPragmas)
+    GenerateArg(Args, OPT_ffp_contract, "fast-honor-pragmas", SA);
+
+  for (StringRef Sanitizer : serializeSanitizerKinds(Opts.Sanitize))
+    GenerateArg(Args, OPT_fsanitize_EQ, Sanitizer, SA);
+
+  // Conflating '-fsanitize-system-blacklist' and '-fsanitize-blacklist'.
+  for (const std::string &F : Opts.SanitizerBlacklistFiles)
+    GenerateArg(Args, OPT_fsanitize_blacklist, F, SA);
+
+  if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver3_8)
+    GenerateArg(Args, OPT_fclang_abi_compat_EQ, "3.8", SA);
+  else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver4)
+    GenerateArg(Args, OPT_fclang_abi_compat_EQ, "4.0", SA);
+  else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver6)
+    GenerateArg(Args, OPT_fclang_abi_compat_EQ, "6.0", SA);
+  else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver7)
+    GenerateArg(Args, OPT_fclang_abi_compat_EQ, "7.0", SA);
+  else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver9)
+    GenerateArg(Args, OPT_fclang_abi_compat_EQ, "9.0", SA);
+  else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver11)
+    GenerateArg(Args, OPT_fclang_abi_compat_EQ, "11.0", SA);
+
+  if (Opts.getSignReturnAddressScope() ==
+      LangOptions::SignReturnAddressScopeKind::All)
+    GenerateArg(Args, OPT_msign_return_address_EQ, "all", SA);
+  else if (Opts.getSignReturnAddressScope() ==
+           LangOptions::SignReturnAddressScopeKind::NonLeaf)
+    GenerateArg(Args, OPT_msign_return_address_EQ, "non-leaf", SA);
+
+  if (Opts.getSignReturnAddressKey() ==
+      LangOptions::SignReturnAddressKeyKind::BKey)
+    GenerateArg(Args, OPT_msign_return_address_key_EQ, "b_key", SA);
 }
 
-bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
-                                       InputKind IK, const llvm::Triple &T,
-                                       std::vector<std::string> &Includes,
-                                       DiagnosticsEngine &Diags) {
+bool CompilerInvocation::ParseLangArgsImpl(LangOptions &Opts, ArgList &Args,
+                                           InputKind IK, const llvm::Triple &T,
+                                           std::vector<std::string> &Includes,
+                                           DiagnosticsEngine &Diags) {
   unsigned NumErrorsBefore = Diags.getNumErrors();
 
   // FIXME: Cleanup per-file based stuff.
@@ -2868,6 +3087,8 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
     }
   }
 
+  // Check if -fopenmp is specified and set default version to 5.0.
+  Opts.OpenMP = Args.hasArg(OPT_fopenmp) ? 50 : 0;
   // Check if -fopenmp-simd is specified.
   bool IsSimdSpecified =
       Args.hasFlag(options::OPT_fopenmp_simd, options::OPT_fno_openmp_simd,
@@ -3011,6 +3232,7 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
   // Parse -fsanitize= arguments.
   parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ),
                       Diags, Opts.Sanitize);
+  Opts.SanitizerBlacklistFiles = Args.getAllArgValues(OPT_fsanitize_blacklist);
   std::vector<std::string> systemBlacklists =
       Args.getAllArgValues(OPT_fsanitize_system_blacklist);
   Opts.SanitizerBlacklistFiles.insert(Opts.SanitizerBlacklistFiles.end(),
@@ -3087,6 +3309,55 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
   return Success && Diags.getNumErrors() == NumErrorsBefore;
 }
 
+bool CompilerInvocation::ParseLangArgs(CompilerInvocation &Res,
+                                       LangOptions &Opts,
+                                       llvm::opt::ArgList &Args, InputKind IK,
+                                       const llvm::Triple &T,
+                                       std::vector<std::string> &Includes,
+                                       DiagnosticsEngine &Diags) {
+  auto DummyOpts = std::make_shared<LangOptions>();
+
+  // We need to work around inconsistencies related to optimization flags. Their
+  // primary consumer is CodeGenOptions. However, the LangOptions parser also
+  // queries them, which means RoundTrip expects us to generate them. We don't
+  // want to do it in GenerateLangArgs, because it should eventually be the
+  // responsibility of GenerateCodeGenArgs. Until we start doing one big
+  // round-trip, let's do it here.
+  //
+  // Our parser always queries OPT_O_Group. When given -O1, -O2 or -O3, it also
+  // queries OPT_O. To ensure RoundTrip consistently considers us responsible
+  // for generating all of them, we ensure to proactively query them all.
+
+  return RoundTrip(
+      [IK, &T, &Includes](CompilerInvocation &Res, ArgList &Args,
+                          DiagnosticsEngine &Diags) {
+        // Proactively query all optimization flags.
+        Args.getLastArg(OPT_O0, OPT_O4, OPT_O, OPT_Ofast);
+        return ParseLangArgsImpl(*Res.getLangOpts(), Args, IK, T, Includes,
+                                 Diags);
+      },
+      [&T, &Args](CompilerInvocation &Res,
+                  SmallVectorImpl<const char *> &GenArgs, StringAllocator SA) {
+        GenerateLangArgs(*Res.getLangOpts(), GenArgs, SA, T);
+        // Generate all optimization flags we queried.
+        if (Arg *A = Args.getLastArg(OPT_O_Group)) {
+          OptSpecifier Opt = A->getOption().getID();
+
+          if (A->getNumValues() > 0)
+            GenerateArg(GenArgs, Opt, A->getValues().back(), SA);
+          else
+            GenerateArg(GenArgs, Opt, SA);
+        }
+
+        // We also queried -fcf-protection, but don't have enough information to
+        // generate it. Eventually, it will be generated from CodeGenOptions.
+        if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ))
+          GenerateArg(GenArgs, OPT_fcf_protection_EQ, A->getValue(), SA);
+      },
+      [&DummyOpts](CompilerInvocation &Res) { Res.LangOpts.swap(DummyOpts); },
+      Res, Args, Diags, "LangOptions");
+}
+
 static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
   switch (Action) {
   case frontend::ASTDeclList:
@@ -3425,7 +3696,7 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res,
   } else {
     // Other LangOpts are only initialized when the input is not AST or LLVM IR.
     // FIXME: Should we really be calling this for an Language::Asm input?
-    Success &= ParseLangArgs(LangOpts, Args, DashX, T,
+    Success &= ParseLangArgs(Res, LangOpts, Args, DashX, T,
                              Res.getPreprocessorOpts().Includes, Diags);
     if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC)
       LangOpts.ObjCExceptions = 1;
@@ -3612,19 +3883,19 @@ void CompilerInvocation::generateCC1CommandLine(
                                    EXTRACTOR, TABLE_INDEX)
 
 #define DIAG_OPTION_WITH_MARSHALLING OPTION_WITH_MARSHALLING
-#define LANG_OPTION_WITH_MARSHALLING OPTION_WITH_MARSHALLING
 #define CODEGEN_OPTION_WITH_MARSHALLING OPTION_WITH_MARSHALLING
 
 #include "clang/Driver/Options.inc"
 
 #undef CODEGEN_OPTION_WITH_MARSHALLING
-#undef LANG_OPTION_WITH_MARSHALLING
 #undef DIAG_OPTION_WITH_MARSHALLING
 #undef OPTION_WITH_MARSHALLING
 
+  llvm::Triple T(TargetOpts->Triple);
+
   GenerateAnalyzerArgs(*AnalyzerOpts, Args, SA);
   GenerateHeaderSearchArgs(*HeaderSearchOpts, Args, SA);
-  GenerateLangArgs(*LangOpts, Args, SA);
+  GenerateLangArgs(*LangOpts, Args, SA, T);
   GeneratePreprocessorArgs(*PreprocessorOpts, Args, SA, *LangOpts,
                            FrontendOpts, CodeGenOpts);
 }
index add190b..9d23a06 100644 (file)
@@ -8,6 +8,7 @@ add_clang_unittest(BasicTests
   FileEntryTest.cpp
   FileManagerTest.cpp
   LineOffsetMappingTest.cpp
+  SanitizersTest.cpp
   SourceManagerTest.cpp
   )
 
diff --git a/clang/unittests/Basic/SanitizersTest.cpp b/clang/unittests/Basic/SanitizersTest.cpp
new file mode 100644 (file)
index 0000000..c6537cb
--- /dev/null
@@ -0,0 +1,49 @@
+//===- unittests/Basic/SanitizersTest.cpp - Test Sanitizers ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Sanitizers.h"
+
+#include "gmock/gmock-matchers.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+
+using testing::Contains;
+using testing::Not;
+
+TEST(SanitizersTest, serializeSanitizers) {
+  SanitizerSet Set;
+  Set.set(parseSanitizerValue("memory", false), true);
+  Set.set(parseSanitizerValue("nullability-arg", false), true);
+
+  SmallVector<StringRef, 4> Serialized;
+  serializeSanitizerSet(Set, Serialized);
+
+  ASSERT_EQ(Serialized.size(), 2u);
+  ASSERT_THAT(Serialized, Contains("memory"));
+  ASSERT_THAT(Serialized, Contains("nullability-arg"));
+}
+
+TEST(SanitizersTest, serializeSanitizersIndividual) {
+  SanitizerSet Set;
+  Set.set(parseSanitizerValue("memory", false), true);
+  Set.set(parseSanitizerValue("nullability-arg", false), true);
+  Set.set(parseSanitizerValue("nullability-assign", false), true);
+  Set.set(parseSanitizerValue("nullability-return", false), true);
+
+  SmallVector<StringRef, 4> Serialized;
+  serializeSanitizerSet(Set, Serialized);
+
+  ASSERT_EQ(Serialized.size(), 4u);
+  ASSERT_THAT(Serialized, Contains("memory"));
+  ASSERT_THAT(Serialized, Contains("nullability-arg"));
+  ASSERT_THAT(Serialized, Contains("nullability-assign"));
+  ASSERT_THAT(Serialized, Contains("nullability-return"));
+  // Individual sanitizers don't get squashed into a single group.
+  ASSERT_THAT(Serialized, Not(Contains("nullability")));
+}