[clang] [sanitizer] Parse weighted sanitizer args and -fno-sanitize-top-hot (PR #121619)

Thurston Dang via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 7 14:31:57 PST 2025


https://github.com/thurstond updated https://github.com/llvm/llvm-project/pull/121619

>From ca1fabc5ea75af0acdd1969c0ad505e04103e1c9 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Sat, 4 Jan 2025 02:53:00 +0000
Subject: [PATCH 01/25] [sanitizer] Parse weighted sanitizer args and
 -fno-sanitize-top-hot

This adds a function to parse weighted sanitizer flags (e.g.,
-fsanitize-blah=undefined=0.5,null=0.3) and adds the plumbing to
apply that to -fno-sanitize-top-hot from the frontend to backend.

-fno-sanitize-top-hot currently has no effect; future work will
use it to generalize ubsan-guard-checks (originaly introduced in 5f9ed2ff8364ff3e4fac410472f421299dafa793).
---
 clang/include/clang/Basic/CodeGenOptions.h |  4 ++
 clang/include/clang/Basic/Sanitizers.h     | 14 +++++
 clang/include/clang/Driver/Options.td      |  7 +++
 clang/include/clang/Driver/SanitizerArgs.h |  1 +
 clang/lib/Basic/Sanitizers.cpp             | 38 ++++++++++++
 clang/lib/Driver/SanitizerArgs.cpp         | 69 +++++++++++++++++-----
 clang/lib/Frontend/CompilerInvocation.cpp  |  5 ++
 7 files changed, 124 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 8097c9ef772bc7..f69f52e49a2fe9 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -384,6 +384,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// the expense of debuggability).
   SanitizerSet SanitizeMergeHandlers;
 
+  /// Set of top hotness thresholds, specifying the fraction of code that is
+  /// excluded from sanitization (0 = skip none, 0.1 = skip hottest 10%, 1.0 = skip all).
+  SanitizerMaskWeights NoSanitizeTopHot = {0};
+
   /// List of backend command-line options for -fembed-bitcode.
   std::vector<uint8_t> CmdArgs;
 
diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index c890242269b334..fa6b557819a1a1 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -154,6 +154,8 @@ struct SanitizerKind {
 #include "clang/Basic/Sanitizers.def"
 }; // SanitizerKind
 
+typedef double SanitizerMaskWeights[SanitizerKind::SO_Count];
+
 struct SanitizerSet {
   /// Check if a certain (single) sanitizer is enabled.
   bool has(SanitizerMask K) const {
@@ -186,10 +188,22 @@ struct SanitizerSet {
 /// Returns a non-zero SanitizerMask, or \c 0 if \p Value is not known.
 SanitizerMask parseSanitizerValue(StringRef Value, bool AllowGroups);
 
+/// Parse a single weighted value (e.g., 'undefined=0.05') from a -fsanitize= or
+/// -fno-sanitize= value list.
+/// Returns a non-zero SanitizerMask, or \c 0 if \p Value is not known.
+/// The relevant weight(s) are updated in the passed array.
+/// Individual weights are never reset to zero unless explicitly set
+/// (e.g., 'null=0.0').
+SanitizerMask parseSanitizerWeightedValue(StringRef Value, bool AllowGroups, SanitizerMaskWeights Weights);
+
 /// Serialize a SanitizerSet into values for -fsanitize= or -fno-sanitize=.
 void serializeSanitizerSet(SanitizerSet Set,
                            SmallVectorImpl<StringRef> &Values);
 
+/// Serialize a SanitizerMaskWeights into values for -fsanitize= or -fno-sanitize=.
+void serializeSanitizerMaskWeights(const SanitizerMaskWeights Weights,
+                                   SmallVectorImpl<StringRef> &Values);
+
 /// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers
 /// this group enables.
 SanitizerMask expandSanitizerGroups(SanitizerMask Kinds);
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index d922709db17786..631a6099781e6c 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2649,6 +2649,13 @@ def fsanitize_undefined_strip_path_components_EQ : Joined<["-"], "fsanitize-unde
   HelpText<"Strip (or keep only, if negative) a given number of path components "
            "when emitting check metadata.">,
   MarshallingInfoInt<CodeGenOpts<"EmitCheckPathComponentsToStrip">, "0", "int">;
+def fno_sanitize_top_hot_EQ
+    : CommaJoined<["-"], "fno-sanitize-top-hot=">,
+      Group<f_clang_Group>,
+      HelpText<"Skip sanitization for the fraction of top hottest code "
+               "(0.0 [default] = do not skip any sanitization; "
+               "0.1 = skip the hottest 10% of code; "
+               "1.0 = skip all sanitization)">;
 
 } // end -f[no-]sanitize* flags
 
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 3b275092bbbe86..854893269e8543 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -26,6 +26,7 @@ class SanitizerArgs {
   SanitizerSet RecoverableSanitizers;
   SanitizerSet TrapSanitizers;
   SanitizerSet MergeHandlers;
+  SanitizerMaskWeights TopHot = {0};
 
   std::vector<std::string> UserIgnorelistFiles;
   std::vector<std::string> SystemIgnorelistFiles;
diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 62ccdf8e9bbf28..adfab2d3afab01 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -36,6 +36,36 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
   return ParsedKind;
 }
 
+SanitizerMask clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups, SanitizerMaskWeights Weights) {
+  SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value)
+#define SANITIZER(NAME, ID) .StartsWith(NAME"=", SanitizerKind::ID)
+#define SANITIZER_GROUP(NAME, ID, ALIAS)                                       \
+  .StartsWith(NAME"=", AllowGroups ? SanitizerKind::ID##Group : SanitizerMask())
+#include "clang/Basic/Sanitizers.def"
+    .Default(SanitizerMask());
+
+  if (ParsedKind && Weights) {
+      size_t equalsIndex = Value.find_first_of('=');
+      if (equalsIndex != llvm::StringLiteral::npos) {
+          double arg;
+          if (   (Value.size() > (equalsIndex + 1))
+              && !Value.substr(equalsIndex + 1).getAsDouble(arg)) {
+              // AllowGroups is already taken into account for ParsedKind,
+              // hence we unconditionally expandSanitizerGroups.
+              SanitizerMask ExpandedKind = expandSanitizerGroups(ParsedKind);
+
+              for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
+                  if(ExpandedKind & SanitizerMask::bitPosToMask(i)) {
+                      Weights[i] = arg;
+                  }
+              }
+          }
+      }
+  }
+
+  return ParsedKind;
+}
+
 void clang::serializeSanitizerSet(SanitizerSet Set,
                                   SmallVectorImpl<StringRef> &Values) {
 #define SANITIZER(NAME, ID)                                                    \
@@ -44,6 +74,14 @@ void clang::serializeSanitizerSet(SanitizerSet Set,
 #include "clang/Basic/Sanitizers.def"
 }
 
+void clang::serializeSanitizerMaskWeights(const SanitizerMaskWeights Weights,
+                                  SmallVectorImpl<StringRef> &Values) {
+#define SANITIZER(NAME, ID)                                                    \
+  if (Weights[SanitizerKind::SO_##ID])                                              \
+    Values.push_back(std::string(NAME) + "=" + std::to_string(Weights[SanitizerKind::SO_##ID]));
+#include "clang/Basic/Sanitizers.def"
+}
+
 SanitizerMask clang::expandSanitizerGroups(SanitizerMask Kinds) {
 #define SANITIZER(NAME, ID)
 #define SANITIZER_GROUP(NAME, ID, ALIAS)                                       \
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 98116e2c8336b8..0f500ca14c527b 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -111,7 +111,7 @@ enum BinaryMetadataFeature {
 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
 /// invalid components. Returns a SanitizerMask.
 static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
-                                    bool DiagnoseErrors);
+                                    bool DiagnoseErrors, SanitizerMaskWeights Weights);
 
 /// Parse -f(no-)?sanitize-coverage= flag values, diagnosing any invalid
 /// components. Returns OR of members of \c CoverageFeature enumeration.
@@ -260,7 +260,7 @@ static SanitizerMask
 parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
                   bool DiagnoseErrors, SanitizerMask Default,
                   SanitizerMask AlwaysIn, SanitizerMask AlwaysOut, int OptInID,
-                  int OptOutID) {
+                  int OptOutID, SanitizerMaskWeights Weights) {
   assert(!(AlwaysIn & AlwaysOut) &&
          "parseSanitizeArgs called with contradictory in/out requirements");
 
@@ -271,7 +271,7 @@ parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
   SanitizerMask DiagnosedAlwaysOutViolations;
   for (const auto *Arg : Args) {
     if (Arg->getOption().matches(OptInID)) {
-      SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors);
+      SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors, Weights);
       // Report error if user explicitly tries to opt-in to an always-out
       // sanitizer.
       if (SanitizerMask KindsToDiagnose =
@@ -287,7 +287,7 @@ parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
       Output |= expandSanitizerGroups(Add);
       Arg->claim();
     } else if (Arg->getOption().matches(OptOutID)) {
-      SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors);
+      SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors, Weights);
       // Report error if user explicitly tries to opt-out of an always-in
       // sanitizer.
       if (SanitizerMask KindsToDiagnose =
@@ -320,7 +320,15 @@ static SanitizerMask parseSanitizeTrapArgs(const Driver &D,
   // (not even in recover mode) in order to avoid the need for a ubsan runtime.
   return parseSanitizeArgs(D, Args, DiagnoseErrors, TrappingDefault, AlwaysTrap,
                            NeverTrap, options::OPT_fsanitize_trap_EQ,
-                           options::OPT_fno_sanitize_trap_EQ);
+                           options::OPT_fno_sanitize_trap_EQ, nullptr);
+}
+
+static SanitizerMask parseNoSanitizeHotArgs(const Driver &D,
+                                            const llvm::opt::ArgList &Args,
+                                            bool DiagnoseErrors,
+                                            SanitizerMaskWeights Weights) {
+  return parseSanitizeArgs(D, Args, DiagnoseErrors, {}, {}, {},
+                           options::OPT_fno_sanitize_top_hot_EQ, -1, Weights);
 }
 
 bool SanitizerArgs::needsFuzzerInterceptors() const {
@@ -403,7 +411,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   for (const llvm::opt::Arg *Arg : llvm::reverse(Args)) {
     if (Arg->getOption().matches(options::OPT_fsanitize_EQ)) {
       Arg->claim();
-      SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors);
+      SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors, nullptr);
 
       if (RemoveObjectSizeAtO0) {
         AllRemove |= SanitizerKind::ObjectSize;
@@ -573,7 +581,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
       Kinds |= Add;
     } else if (Arg->getOption().matches(options::OPT_fno_sanitize_EQ)) {
       Arg->claim();
-      SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors);
+      SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors, nullptr);
       AllRemove |= expandSanitizerGroups(Remove);
     }
   }
@@ -698,7 +706,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   SanitizerMask RecoverableKinds = parseSanitizeArgs(
       D, Args, DiagnoseErrors, RecoverableByDefault, AlwaysRecoverable,
       Unrecoverable, options::OPT_fsanitize_recover_EQ,
-      options::OPT_fno_sanitize_recover_EQ);
+      options::OPT_fno_sanitize_recover_EQ, nullptr);
   RecoverableKinds |= AlwaysRecoverable;
   RecoverableKinds &= ~Unrecoverable;
   RecoverableKinds &= Kinds;
@@ -710,9 +718,13 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   SanitizerMask MergeKinds =
       parseSanitizeArgs(D, Args, DiagnoseErrors, MergeDefault, {}, {},
                         options::OPT_fsanitize_merge_handlers_EQ,
-                        options::OPT_fno_sanitize_merge_handlers_EQ);
+                        options::OPT_fno_sanitize_merge_handlers_EQ, nullptr);
   MergeKinds &= Kinds;
 
+  // Parse -fno-sanitize-top-hot flags
+  SanitizerMask HotMask = parseNoSanitizeHotArgs (D, Args, DiagnoseErrors, TopHot);
+  (void)HotMask;
+
   // Setup ignorelist files.
   // Add default ignorelist from resource directory for activated sanitizers,
   // and validate special case lists format.
@@ -1132,6 +1144,12 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
          "Overlap between recoverable and trapping sanitizers");
 
   MergeHandlers.Mask |= MergeKinds;
+
+  // Zero out TopHot for unused sanitizers
+  for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
+     if (!(Sanitizers.Mask & SanitizerMask::bitPosToMask(i)))
+       TopHot[i] = 0;
+  }
 }
 
 static std::string toString(const clang::SanitizerSet &Sanitizers) {
@@ -1146,6 +1164,18 @@ static std::string toString(const clang::SanitizerSet &Sanitizers) {
   return Res;
 }
 
+static std::string toString(const clang::SanitizerMaskWeights &Weights) {
+  std::string Res;
+#define SANITIZER(NAME, ID)                                                    \
+  if (Weights[SanitizerKind::SO_##ID]) {                                     \
+    if (!Res.empty())                                                          \
+      Res += ",";                                                              \
+    Res += std::string(NAME) + "=" + std::to_string(Weights[SanitizerKind::SO_##ID]);                                                               \
+  }
+#include "clang/Basic/Sanitizers.def"
+  return Res;
+}
+
 static void addSpecialCaseListOpt(const llvm::opt::ArgList &Args,
                                   llvm::opt::ArgStringList &CmdArgs,
                                   const char *SCLOptFlag,
@@ -1297,6 +1327,11 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
     CmdArgs.push_back(
         Args.MakeArgString("-fsanitize-merge=" + toString(MergeHandlers)));
 
+  std::string TopHotStr = toString(TopHot);
+  if (TopHotStr != "")
+    CmdArgs.push_back(
+        Args.MakeArgString("-fno-sanitize-top-hot=" + TopHotStr));
+
   addSpecialCaseListOpt(Args, CmdArgs,
                         "-fsanitize-ignorelist=", UserIgnorelistFiles);
   addSpecialCaseListOpt(Args, CmdArgs,
@@ -1463,7 +1498,7 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
 }
 
 SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
-                             bool DiagnoseErrors) {
+                             bool DiagnoseErrors, SanitizerMaskWeights Weights) {
   assert(
       (A->getOption().matches(options::OPT_fsanitize_EQ) ||
        A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
@@ -1472,7 +1507,8 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
        A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
        A->getOption().matches(options::OPT_fno_sanitize_trap_EQ) ||
        A->getOption().matches(options::OPT_fsanitize_merge_handlers_EQ) ||
-       A->getOption().matches(options::OPT_fno_sanitize_merge_handlers_EQ)) &&
+       A->getOption().matches(options::OPT_fno_sanitize_merge_handlers_EQ) ||
+       A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ)) &&
       "Invalid argument in parseArgValues!");
   SanitizerMask Kinds;
   for (int i = 0, n = A->getNumValues(); i != n; ++i) {
@@ -1482,8 +1518,13 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
     if (A->getOption().matches(options::OPT_fsanitize_EQ) &&
         0 == strcmp("all", Value))
       Kind = SanitizerMask();
-    else
+    else if (A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ)) {
+      assert(Weights && "Null weights parameter provided for parsing fno_sanitize_top_hot!");
+      Kind = parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Weights);
+    } else {
+      assert((!Weights) && "Non-null weights parameter erroneously provided!");
       Kind = parseSanitizerValue(Value, /*AllowGroups=*/true);
+    }
 
     if (Kind)
       Kinds |= Kind;
@@ -1586,12 +1627,12 @@ std::string lastArgumentForMask(const Driver &D, const llvm::opt::ArgList &Args,
     const auto *Arg = *I;
     if (Arg->getOption().matches(options::OPT_fsanitize_EQ)) {
       SanitizerMask AddKinds =
-          expandSanitizerGroups(parseArgValues(D, Arg, false));
+          expandSanitizerGroups(parseArgValues(D, Arg, false, nullptr));
       if (AddKinds & Mask)
         return describeSanitizeArg(Arg, Mask);
     } else if (Arg->getOption().matches(options::OPT_fno_sanitize_EQ)) {
       SanitizerMask RemoveKinds =
-          expandSanitizerGroups(parseArgValues(D, Arg, false));
+          expandSanitizerGroups(parseArgValues(D, Arg, false, nullptr));
       Mask &= ~RemoveKinds;
     }
   }
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 348c56cc37da3f..c1c11f5a2325c7 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1796,6 +1796,11 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
        serializeSanitizerKinds(Opts.SanitizeMergeHandlers))
     GenerateArg(Consumer, OPT_fsanitize_merge_handlers_EQ, Sanitizer);
 
+  SmallVector<StringRef, 4> Values;
+  serializeSanitizerMaskWeights(Opts.NoSanitizeTopHot, Values);
+  for (StringRef Sanitizer : Values)
+    GenerateArg(Consumer, OPT_fno_sanitize_top_hot_EQ, Sanitizer);
+
   if (!Opts.EmitVersionIdentMetadata)
     GenerateArg(Consumer, OPT_Qn);
 

>From 770165969c8f14562702fe177d288239720deef2 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Sat, 4 Jan 2025 03:22:24 +0000
Subject: [PATCH 02/25] clang-format

---
 clang/include/clang/Basic/CodeGenOptions.h |  3 +-
 clang/include/clang/Basic/Sanitizers.h     |  6 ++-
 clang/lib/Basic/Sanitizers.cpp             | 46 ++++++++++++----------
 clang/lib/Driver/SanitizerArgs.cpp         | 46 ++++++++++++----------
 4 files changed, 56 insertions(+), 45 deletions(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index f69f52e49a2fe9..39eabe0b1effa8 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -385,7 +385,8 @@ class CodeGenOptions : public CodeGenOptionsBase {
   SanitizerSet SanitizeMergeHandlers;
 
   /// Set of top hotness thresholds, specifying the fraction of code that is
-  /// excluded from sanitization (0 = skip none, 0.1 = skip hottest 10%, 1.0 = skip all).
+  /// excluded from sanitization (0 = skip none, 0.1 = skip hottest 10%, 1.0 =
+  /// skip all).
   SanitizerMaskWeights NoSanitizeTopHot = {0};
 
   /// List of backend command-line options for -fembed-bitcode.
diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index fa6b557819a1a1..f179fe5027ed0b 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -194,13 +194,15 @@ SanitizerMask parseSanitizerValue(StringRef Value, bool AllowGroups);
 /// The relevant weight(s) are updated in the passed array.
 /// Individual weights are never reset to zero unless explicitly set
 /// (e.g., 'null=0.0').
-SanitizerMask parseSanitizerWeightedValue(StringRef Value, bool AllowGroups, SanitizerMaskWeights Weights);
+SanitizerMask parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
+                                          SanitizerMaskWeights Weights);
 
 /// Serialize a SanitizerSet into values for -fsanitize= or -fno-sanitize=.
 void serializeSanitizerSet(SanitizerSet Set,
                            SmallVectorImpl<StringRef> &Values);
 
-/// Serialize a SanitizerMaskWeights into values for -fsanitize= or -fno-sanitize=.
+/// Serialize a SanitizerMaskWeights into values for -fsanitize= or
+/// -fno-sanitize=.
 void serializeSanitizerMaskWeights(const SanitizerMaskWeights Weights,
                                    SmallVectorImpl<StringRef> &Values);
 
diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index adfab2d3afab01..c0e08ccfcdf746 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -36,31 +36,34 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
   return ParsedKind;
 }
 
-SanitizerMask clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups, SanitizerMaskWeights Weights) {
+SanitizerMask clang::parseSanitizerWeightedValue(StringRef Value,
+                                                 bool AllowGroups,
+                                                 SanitizerMaskWeights Weights) {
   SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value)
-#define SANITIZER(NAME, ID) .StartsWith(NAME"=", SanitizerKind::ID)
+#define SANITIZER(NAME, ID) .StartsWith(NAME "=", SanitizerKind::ID)
 #define SANITIZER_GROUP(NAME, ID, ALIAS)                                       \
-  .StartsWith(NAME"=", AllowGroups ? SanitizerKind::ID##Group : SanitizerMask())
+  .StartsWith(NAME "=",                                                        \
+              AllowGroups ? SanitizerKind::ID##Group : SanitizerMask())
 #include "clang/Basic/Sanitizers.def"
-    .Default(SanitizerMask());
+                                 .Default(SanitizerMask());
 
   if (ParsedKind && Weights) {
-      size_t equalsIndex = Value.find_first_of('=');
-      if (equalsIndex != llvm::StringLiteral::npos) {
-          double arg;
-          if (   (Value.size() > (equalsIndex + 1))
-              && !Value.substr(equalsIndex + 1).getAsDouble(arg)) {
-              // AllowGroups is already taken into account for ParsedKind,
-              // hence we unconditionally expandSanitizerGroups.
-              SanitizerMask ExpandedKind = expandSanitizerGroups(ParsedKind);
-
-              for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
-                  if(ExpandedKind & SanitizerMask::bitPosToMask(i)) {
-                      Weights[i] = arg;
-                  }
-              }
+    size_t equalsIndex = Value.find_first_of('=');
+    if (equalsIndex != llvm::StringLiteral::npos) {
+      double arg;
+      if ((Value.size() > (equalsIndex + 1)) &&
+          !Value.substr(equalsIndex + 1).getAsDouble(arg)) {
+        // AllowGroups is already taken into account for ParsedKind,
+        // hence we unconditionally expandSanitizerGroups.
+        SanitizerMask ExpandedKind = expandSanitizerGroups(ParsedKind);
+
+        for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
+          if (ExpandedKind & SanitizerMask::bitPosToMask(i)) {
+            Weights[i] = arg;
           }
+        }
       }
+    }
   }
 
   return ParsedKind;
@@ -75,10 +78,11 @@ void clang::serializeSanitizerSet(SanitizerSet Set,
 }
 
 void clang::serializeSanitizerMaskWeights(const SanitizerMaskWeights Weights,
-                                  SmallVectorImpl<StringRef> &Values) {
+                                          SmallVectorImpl<StringRef> &Values) {
 #define SANITIZER(NAME, ID)                                                    \
-  if (Weights[SanitizerKind::SO_##ID])                                              \
-    Values.push_back(std::string(NAME) + "=" + std::to_string(Weights[SanitizerKind::SO_##ID]));
+  if (Weights[SanitizerKind::SO_##ID])                                         \
+    Values.push_back(std::string(NAME) + "=" +                                 \
+                     std::to_string(Weights[SanitizerKind::SO_##ID]));
 #include "clang/Basic/Sanitizers.def"
 }
 
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 0f500ca14c527b..bac24dc824671e 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -111,7 +111,8 @@ enum BinaryMetadataFeature {
 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
 /// invalid components. Returns a SanitizerMask.
 static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
-                                    bool DiagnoseErrors, SanitizerMaskWeights Weights);
+                                    bool DiagnoseErrors,
+                                    SanitizerMaskWeights Weights);
 
 /// Parse -f(no-)?sanitize-coverage= flag values, diagnosing any invalid
 /// components. Returns OR of members of \c CoverageFeature enumeration.
@@ -722,7 +723,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   MergeKinds &= Kinds;
 
   // Parse -fno-sanitize-top-hot flags
-  SanitizerMask HotMask = parseNoSanitizeHotArgs (D, Args, DiagnoseErrors, TopHot);
+  SanitizerMask HotMask =
+      parseNoSanitizeHotArgs(D, Args, DiagnoseErrors, TopHot);
   (void)HotMask;
 
   // Setup ignorelist files.
@@ -1147,8 +1149,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
 
   // Zero out TopHot for unused sanitizers
   for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
-     if (!(Sanitizers.Mask & SanitizerMask::bitPosToMask(i)))
-       TopHot[i] = 0;
+    if (!(Sanitizers.Mask & SanitizerMask::bitPosToMask(i)))
+      TopHot[i] = 0;
   }
 }
 
@@ -1167,10 +1169,11 @@ static std::string toString(const clang::SanitizerSet &Sanitizers) {
 static std::string toString(const clang::SanitizerMaskWeights &Weights) {
   std::string Res;
 #define SANITIZER(NAME, ID)                                                    \
-  if (Weights[SanitizerKind::SO_##ID]) {                                     \
+  if (Weights[SanitizerKind::SO_##ID]) {                                       \
     if (!Res.empty())                                                          \
       Res += ",";                                                              \
-    Res += std::string(NAME) + "=" + std::to_string(Weights[SanitizerKind::SO_##ID]);                                                               \
+    Res += std::string(NAME) + "=" +                                           \
+           std::to_string(Weights[SanitizerKind::SO_##ID]);                    \
   }
 #include "clang/Basic/Sanitizers.def"
   return Res;
@@ -1329,8 +1332,7 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
 
   std::string TopHotStr = toString(TopHot);
   if (TopHotStr != "")
-    CmdArgs.push_back(
-        Args.MakeArgString("-fno-sanitize-top-hot=" + TopHotStr));
+    CmdArgs.push_back(Args.MakeArgString("-fno-sanitize-top-hot=" + TopHotStr));
 
   addSpecialCaseListOpt(Args, CmdArgs,
                         "-fsanitize-ignorelist=", UserIgnorelistFiles);
@@ -1498,18 +1500,18 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
 }
 
 SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
-                             bool DiagnoseErrors, SanitizerMaskWeights Weights) {
-  assert(
-      (A->getOption().matches(options::OPT_fsanitize_EQ) ||
-       A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
-       A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
-       A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
-       A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
-       A->getOption().matches(options::OPT_fno_sanitize_trap_EQ) ||
-       A->getOption().matches(options::OPT_fsanitize_merge_handlers_EQ) ||
-       A->getOption().matches(options::OPT_fno_sanitize_merge_handlers_EQ) ||
-       A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ)) &&
-      "Invalid argument in parseArgValues!");
+                             bool DiagnoseErrors,
+                             SanitizerMaskWeights Weights) {
+  assert((A->getOption().matches(options::OPT_fsanitize_EQ) ||
+          A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
+          A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
+          A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
+          A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
+          A->getOption().matches(options::OPT_fno_sanitize_trap_EQ) ||
+          A->getOption().matches(options::OPT_fsanitize_merge_handlers_EQ) ||
+          A->getOption().matches(options::OPT_fno_sanitize_merge_handlers_EQ) ||
+          A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ)) &&
+         "Invalid argument in parseArgValues!");
   SanitizerMask Kinds;
   for (int i = 0, n = A->getNumValues(); i != n; ++i) {
     const char *Value = A->getValue(i);
@@ -1519,7 +1521,9 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
         0 == strcmp("all", Value))
       Kind = SanitizerMask();
     else if (A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ)) {
-      assert(Weights && "Null weights parameter provided for parsing fno_sanitize_top_hot!");
+      assert(
+          Weights &&
+          "Null weights parameter provided for parsing fno_sanitize_top_hot!");
       Kind = parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Weights);
     } else {
       assert((!Weights) && "Non-null weights parameter erroneously provided!");

>From ade9e63ff7be82c23014db570cdffae8c94c2a78 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Mon, 6 Jan 2025 18:41:15 +0000
Subject: [PATCH 03/25] Update flag description Update clang driver test

---
 clang/include/clang/Basic/CodeGenOptions.h |  6 +++---
 clang/include/clang/Driver/Options.td      |  6 ++----
 clang/lib/Basic/Sanitizers.cpp             |  6 ++----
 clang/test/Driver/fsanitize.c              | 16 ++++++++++++++++
 4 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 39eabe0b1effa8..2cf0197d17c8fa 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -384,9 +384,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// the expense of debuggability).
   SanitizerSet SanitizeMergeHandlers;
 
-  /// Set of top hotness thresholds, specifying the fraction of code that is
-  /// excluded from sanitization (0 = skip none, 0.1 = skip hottest 10%, 1.0 =
-  /// skip all).
+  /// Set of thresholds, specifying the top hottest fraction of code to be
+  /// excluded from sanitization (0.0 = skip none, 0.1 = skip hottest 10%,
+  /// 1.0 = skip all).
   SanitizerMaskWeights NoSanitizeTopHot = {0};
 
   /// List of backend command-line options for -fembed-bitcode.
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 631a6099781e6c..ecdd4d1655cb78 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2652,10 +2652,8 @@ def fsanitize_undefined_strip_path_components_EQ : Joined<["-"], "fsanitize-unde
 def fno_sanitize_top_hot_EQ
     : CommaJoined<["-"], "fno-sanitize-top-hot=">,
       Group<f_clang_Group>,
-      HelpText<"Skip sanitization for the fraction of top hottest code "
-               "(0.0 [default] = do not skip any sanitization; "
-               "0.1 = skip the hottest 10% of code; "
-               "1.0 = skip all sanitization)">;
+      HelpText<"Exclude sanitization for the top hottest fraction of code "
+               "(0.0 [default] = skip none; 0.1 = skip hottest 10%; 1.0 = skip all)">;
 
 } // end -f[no-]sanitize* flags
 
diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index c0e08ccfcdf746..924d86b0360518 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -57,11 +57,9 @@ SanitizerMask clang::parseSanitizerWeightedValue(StringRef Value,
         // hence we unconditionally expandSanitizerGroups.
         SanitizerMask ExpandedKind = expandSanitizerGroups(ParsedKind);
 
-        for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
-          if (ExpandedKind & SanitizerMask::bitPosToMask(i)) {
+        for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++)
+          if (ExpandedKind & SanitizerMask::bitPosToMask(i))
             Weights[i] = arg;
-          }
-        }
       }
     }
   }
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index aeae15aada70cc..9c08ad8c40bc69 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -1154,3 +1154,19 @@
 
 // RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,undefined  %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-UBSAN
 // CHECK-REALTIME-UBSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=undefined'
+
+
+// -fno-sanitize-top-hot
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT
+// CHECK-UNDEFINED-TOP-HOT: "-fno-sanitize-top-hot={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=0.5(0*),?){19}"}}
+
+// If no sanitizers are enabled, -fno-sanitize-top-hot is not passed
+// RUN: %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot=undefined=0.5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT2
+// CHECK-UNDEFINED-TOP-HOT2-NOT: "-fno-sanitize-top-hot"
+
+// Threshold of 0.0 cancels out the flag
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5,integer=0.0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT3
+// CHECK-UNDEFINED-TOP-HOT3: "-fno-sanitize-top-hot={{((unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=0.5(0*),?){15}"}}
+
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5,integer=0.0,signed-integer-overflow=0.7 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT4
+// CHECK-UNDEFINED-TOP-HOT4: "-fno-sanitize-top-hot={{((signed-integer-overflow|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=(0.5|0.7)(0*),?){16}"}}

>From 6e20cf8155f957906f6f511f9d9051a81c7d99a3 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Mon, 6 Jan 2025 21:44:53 +0000
Subject: [PATCH 04/25] Plumb values through CodeGen

---
 clang/include/clang/Basic/CodeGenOptions.h |  5 ++++-
 clang/include/clang/Basic/Sanitizers.h     |  2 +-
 clang/include/clang/Driver/SanitizerArgs.h |  3 ++-
 clang/lib/Basic/Sanitizers.cpp             |  6 +++---
 clang/lib/CodeGen/CGExpr.cpp               |  6 +++++-
 clang/lib/Driver/SanitizerArgs.cpp         | 17 +++++++++-------
 clang/lib/Frontend/CompilerInvocation.cpp  | 23 +++++++++++++++++++---
 clang/test/CodeGen/allow-ubsan-check.c     |  4 ++++
 clang/test/Driver/fsanitize.c              |  3 +++
 9 files changed, 52 insertions(+), 17 deletions(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 2cf0197d17c8fa..e648bc4ea90cdf 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -387,7 +387,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// Set of thresholds, specifying the top hottest fraction of code to be
   /// excluded from sanitization (0.0 = skip none, 0.1 = skip hottest 10%,
   /// 1.0 = skip all).
-  SanitizerMaskWeights NoSanitizeTopHot = {0};
+  SanitizerSet NoSanitizeTopHot;
+  /// N.B. The weights contain strictly more information than the SanitizerSet,
+  /// but the SanitizerSet is more efficient for some calculations.
+  SanitizerMaskWeights NoSanitizeTopHotWeights = {0};
 
   /// List of backend command-line options for -fembed-bitcode.
   std::vector<uint8_t> CmdArgs;
diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index f179fe5027ed0b..d80e04ca1290cf 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -204,7 +204,7 @@ void serializeSanitizerSet(SanitizerSet Set,
 /// Serialize a SanitizerMaskWeights into values for -fsanitize= or
 /// -fno-sanitize=.
 void serializeSanitizerMaskWeights(const SanitizerMaskWeights Weights,
-                                   SmallVectorImpl<StringRef> &Values);
+                                   SmallVectorImpl<std::string> &Values);
 
 /// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers
 /// this group enables.
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 854893269e8543..28c067444f2e4c 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -26,7 +26,8 @@ class SanitizerArgs {
   SanitizerSet RecoverableSanitizers;
   SanitizerSet TrapSanitizers;
   SanitizerSet MergeHandlers;
-  SanitizerMaskWeights TopHot = {0};
+  SanitizerSet TopHot;
+  SanitizerMaskWeights TopHotWeights = {0};
 
   std::vector<std::string> UserIgnorelistFiles;
   std::vector<std::string> SystemIgnorelistFiles;
diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 924d86b0360518..92d9fb72559614 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -76,11 +76,11 @@ void clang::serializeSanitizerSet(SanitizerSet Set,
 }
 
 void clang::serializeSanitizerMaskWeights(const SanitizerMaskWeights Weights,
-                                          SmallVectorImpl<StringRef> &Values) {
+                                          SmallVectorImpl<std::string> &Values) {
 #define SANITIZER(NAME, ID)                                                    \
   if (Weights[SanitizerKind::SO_##ID])                                         \
-    Values.push_back(std::string(NAME) + "=" +                                 \
-                     std::to_string(Weights[SanitizerKind::SO_##ID]));
+    Values.push_back(std::string(NAME "=")                                    \
+                     + std::to_string(Weights[SanitizerKind::SO_##ID]));
 #include "clang/Basic/Sanitizers.def"
 }
 
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index ba1cba291553b0..3e14b0ffa7b320 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3602,6 +3602,7 @@ void CodeGenFunction::EmitCheck(
   llvm::Value *RecoverableCond = nullptr;
   llvm::Value *TrapCond = nullptr;
   bool NoMerge = false;
+  bool SanitizeGuardChecks = ClSanitizeGuardChecks;
   for (int i = 0, n = Checked.size(); i < n; ++i) {
     llvm::Value *Check = Checked[i].first;
     // -fsanitize-trap= overrides -fsanitize-recover=.
@@ -3615,9 +3616,12 @@ void CodeGenFunction::EmitCheck(
 
     if (!CGM.getCodeGenOpts().SanitizeMergeHandlers.has(Checked[i].second))
       NoMerge = true;
+
+    if (!CGM.getCodeGenOpts().NoSanitizeTopHot.has(Checked[i].second))
+      SanitizeGuardChecks = true;
   }
 
-  if (ClSanitizeGuardChecks) {
+  if (SanitizeGuardChecks) {
     llvm::Value *Allow =
         Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::allow_ubsan_check),
                            llvm::ConstantInt::get(CGM.Int8Ty, CheckHandler));
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index bac24dc824671e..cb1c0801a8c457 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -723,9 +723,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   MergeKinds &= Kinds;
 
   // Parse -fno-sanitize-top-hot flags
-  SanitizerMask HotMask =
-      parseNoSanitizeHotArgs(D, Args, DiagnoseErrors, TopHot);
-  (void)HotMask;
+  SanitizerMask TopHotMask =
+      parseNoSanitizeHotArgs(D, Args, DiagnoseErrors, TopHotWeights);
+  (void)TopHotMask;
 
   // Setup ignorelist files.
   // Add default ignorelist from resource directory for activated sanitizers,
@@ -1147,10 +1147,13 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
 
   MergeHandlers.Mask |= MergeKinds;
 
+  TopHotMask &= Sanitizers.Mask;
+  TopHot.Mask = TopHotMask;
+
   // Zero out TopHot for unused sanitizers
   for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
     if (!(Sanitizers.Mask & SanitizerMask::bitPosToMask(i)))
-      TopHot[i] = 0;
+      TopHotWeights[i] = 0;
   }
 }
 
@@ -1330,9 +1333,9 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
     CmdArgs.push_back(
         Args.MakeArgString("-fsanitize-merge=" + toString(MergeHandlers)));
 
-  std::string TopHotStr = toString(TopHot);
-  if (TopHotStr != "")
-    CmdArgs.push_back(Args.MakeArgString("-fno-sanitize-top-hot=" + TopHotStr));
+  std::string TopHotWeightsStr = toString(TopHotWeights);
+  if (TopHotWeightsStr != "")
+    CmdArgs.push_back(Args.MakeArgString("-fno-sanitize-top-hot=" + TopHotWeightsStr));
 
   addSpecialCaseListOpt(Args, CmdArgs,
                         "-fsanitize-ignorelist=", UserIgnorelistFiles);
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index c1c11f5a2325c7..e2705b111fb8db 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1436,6 +1436,18 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
   return Values;
 }
 
+static void parseSanitizerWeightedKinds(StringRef FlagName,
+                                const std::vector<std::string> &Sanitizers,
+                                DiagnosticsEngine &Diags, SanitizerSet &S, SanitizerMaskWeights &Weights) {
+  for (const auto &Sanitizer : Sanitizers) {
+    SanitizerMask K = parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Weights);
+    if (K == SanitizerMask())
+      Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer;
+    else
+      S.set(K, true);
+  }
+}
+
 static void parseXRayInstrumentationBundle(StringRef FlagName, StringRef Bundle,
                                            ArgList &Args, DiagnosticsEngine &D,
                                            XRayInstrSet &S) {
@@ -1796,9 +1808,9 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
        serializeSanitizerKinds(Opts.SanitizeMergeHandlers))
     GenerateArg(Consumer, OPT_fsanitize_merge_handlers_EQ, Sanitizer);
 
-  SmallVector<StringRef, 4> Values;
-  serializeSanitizerMaskWeights(Opts.NoSanitizeTopHot, Values);
-  for (StringRef Sanitizer : Values)
+  SmallVector<std::string, 4> Values;
+  serializeSanitizerMaskWeights(Opts.NoSanitizeTopHotWeights, Values);
+  for (std::string Sanitizer : Values)
     GenerateArg(Consumer, OPT_fno_sanitize_top_hot_EQ, Sanitizer);
 
   if (!Opts.EmitVersionIdentMetadata)
@@ -2282,6 +2294,11 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
                       Args.getAllArgValues(OPT_fsanitize_merge_handlers_EQ),
                       Diags, Opts.SanitizeMergeHandlers);
 
+  // Parse -fno-sanitize-top-hot= arguments.
+  parseSanitizerWeightedKinds("-fno-sanitize-top-hot=",
+                      Args.getAllArgValues(OPT_fno_sanitize_top_hot_EQ),
+                      Diags, Opts.NoSanitizeTopHot, Opts.NoSanitizeTopHotWeights);
+
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 
   if (!LangOpts->CUDAIsDevice)
diff --git a/clang/test/CodeGen/allow-ubsan-check.c b/clang/test/CodeGen/allow-ubsan-check.c
index 5232d240854666..adc56dee9ad02e 100644
--- a/clang/test/CodeGen/allow-ubsan-check.c
+++ b/clang/test/CodeGen/allow-ubsan-check.c
@@ -3,6 +3,10 @@
 // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -mllvm -ubsan-guard-checks -fsanitize-trap=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=TRAP
 // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -mllvm -ubsan-guard-checks -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=RECOVER
 
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fno-sanitize-top-hot=signed-integer-overflow=1,integer-divide-by-zero=1,null=1 | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fno-sanitize-top-hot=signed-integer-overflow=1,integer-divide-by-zero=1,null=1 -fsanitize-trap=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=TRAP
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fno-sanitize-top-hot=signed-integer-overflow=1,integer-divide-by-zero=1,null=1 -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=RECOVER
+
 
 // CHECK-LABEL: define dso_local i32 @div(
 // CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR0:[0-9]+]] {
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index 9c08ad8c40bc69..211104fd667278 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -1170,3 +1170,6 @@
 
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5,integer=0.0,signed-integer-overflow=0.7 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT4
 // CHECK-UNDEFINED-TOP-HOT4: "-fno-sanitize-top-hot={{((signed-integer-overflow|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=(0.5|0.7)(0*),?){16}"}}
+
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=integer -fno-sanitize-top-hot=undefined=0.4 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT5
+// CHECK-UNDEFINED-TOP-HOT5: "-fno-sanitize-top-hot={{((integer-divide-by-zero|shift-base|shift-exponent|signed-integer-overflow)=0.4(0*),?){4}"}}

>From cc3a2ac63ebe32be52d9f227de182e178a2eac02 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Mon, 6 Jan 2025 21:50:26 +0000
Subject: [PATCH 05/25] clang-format

---
 clang/lib/Basic/Sanitizers.cpp            |  8 ++++----
 clang/lib/Driver/SanitizerArgs.cpp        |  3 ++-
 clang/lib/Frontend/CompilerInvocation.cpp | 14 ++++++++------
 3 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 92d9fb72559614..13f42bd143e084 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -75,12 +75,12 @@ void clang::serializeSanitizerSet(SanitizerSet Set,
 #include "clang/Basic/Sanitizers.def"
 }
 
-void clang::serializeSanitizerMaskWeights(const SanitizerMaskWeights Weights,
-                                          SmallVectorImpl<std::string> &Values) {
+void clang::serializeSanitizerMaskWeights(
+    const SanitizerMaskWeights Weights, SmallVectorImpl<std::string> &Values) {
 #define SANITIZER(NAME, ID)                                                    \
   if (Weights[SanitizerKind::SO_##ID])                                         \
-    Values.push_back(std::string(NAME "=")                                    \
-                     + std::to_string(Weights[SanitizerKind::SO_##ID]));
+    Values.push_back(std::string(NAME "=") +                                   \
+                     std::to_string(Weights[SanitizerKind::SO_##ID]));
 #include "clang/Basic/Sanitizers.def"
 }
 
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index cb1c0801a8c457..773ebf53b005a5 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -1335,7 +1335,8 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
 
   std::string TopHotWeightsStr = toString(TopHotWeights);
   if (TopHotWeightsStr != "")
-    CmdArgs.push_back(Args.MakeArgString("-fno-sanitize-top-hot=" + TopHotWeightsStr));
+    CmdArgs.push_back(
+        Args.MakeArgString("-fno-sanitize-top-hot=" + TopHotWeightsStr));
 
   addSpecialCaseListOpt(Args, CmdArgs,
                         "-fsanitize-ignorelist=", UserIgnorelistFiles);
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index e2705b111fb8db..df7fac32fec581 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1436,11 +1436,12 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
   return Values;
 }
 
-static void parseSanitizerWeightedKinds(StringRef FlagName,
-                                const std::vector<std::string> &Sanitizers,
-                                DiagnosticsEngine &Diags, SanitizerSet &S, SanitizerMaskWeights &Weights) {
+static void parseSanitizerWeightedKinds(
+    StringRef FlagName, const std::vector<std::string> &Sanitizers,
+    DiagnosticsEngine &Diags, SanitizerSet &S, SanitizerMaskWeights &Weights) {
   for (const auto &Sanitizer : Sanitizers) {
-    SanitizerMask K = parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Weights);
+    SanitizerMask K =
+        parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Weights);
     if (K == SanitizerMask())
       Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer;
     else
@@ -2296,8 +2297,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
 
   // Parse -fno-sanitize-top-hot= arguments.
   parseSanitizerWeightedKinds("-fno-sanitize-top-hot=",
-                      Args.getAllArgValues(OPT_fno_sanitize_top_hot_EQ),
-                      Diags, Opts.NoSanitizeTopHot, Opts.NoSanitizeTopHotWeights);
+                              Args.getAllArgValues(OPT_fno_sanitize_top_hot_EQ),
+                              Diags, Opts.NoSanitizeTopHot,
+                              Opts.NoSanitizeTopHotWeights);
 
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 

>From 9a2a0e86cf187961be63028e264f5388246a36cb Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Mon, 6 Jan 2025 22:43:30 +0000
Subject: [PATCH 06/25] using SanitizerMaskWeights = std::array<float,
 SanitizerKind::SO_Count>

---
 clang/include/clang/Basic/Sanitizers.h    |  4 ++--
 clang/lib/Basic/Sanitizers.cpp            |  4 ++--
 clang/lib/Driver/SanitizerArgs.cpp        | 10 +++++-----
 clang/lib/Frontend/CompilerInvocation.cpp |  4 ++--
 4 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index d80e04ca1290cf..c1242a1a69ef3f 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -154,7 +154,7 @@ struct SanitizerKind {
 #include "clang/Basic/Sanitizers.def"
 }; // SanitizerKind
 
-typedef double SanitizerMaskWeights[SanitizerKind::SO_Count];
+using SanitizerMaskWeights = std::array<float, SanitizerKind::SO_Count>;
 
 struct SanitizerSet {
   /// Check if a certain (single) sanitizer is enabled.
@@ -195,7 +195,7 @@ SanitizerMask parseSanitizerValue(StringRef Value, bool AllowGroups);
 /// Individual weights are never reset to zero unless explicitly set
 /// (e.g., 'null=0.0').
 SanitizerMask parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
-                                          SanitizerMaskWeights Weights);
+                                          SanitizerMaskWeights *Weights);
 
 /// Serialize a SanitizerSet into values for -fsanitize= or -fno-sanitize=.
 void serializeSanitizerSet(SanitizerSet Set,
diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 13f42bd143e084..179f2ad03714b1 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -38,7 +38,7 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
 
 SanitizerMask clang::parseSanitizerWeightedValue(StringRef Value,
                                                  bool AllowGroups,
-                                                 SanitizerMaskWeights Weights) {
+                                                 SanitizerMaskWeights *Weights) {
   SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value)
 #define SANITIZER(NAME, ID) .StartsWith(NAME "=", SanitizerKind::ID)
 #define SANITIZER_GROUP(NAME, ID, ALIAS)                                       \
@@ -59,7 +59,7 @@ SanitizerMask clang::parseSanitizerWeightedValue(StringRef Value,
 
         for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++)
           if (ExpandedKind & SanitizerMask::bitPosToMask(i))
-            Weights[i] = arg;
+            (*Weights)[i] = arg;
       }
     }
   }
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 773ebf53b005a5..44c1b353cf2d9f 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -112,7 +112,7 @@ enum BinaryMetadataFeature {
 /// invalid components. Returns a SanitizerMask.
 static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
                                     bool DiagnoseErrors,
-                                    SanitizerMaskWeights Weights);
+                                    SanitizerMaskWeights *Weights);
 
 /// Parse -f(no-)?sanitize-coverage= flag values, diagnosing any invalid
 /// components. Returns OR of members of \c CoverageFeature enumeration.
@@ -261,7 +261,7 @@ static SanitizerMask
 parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
                   bool DiagnoseErrors, SanitizerMask Default,
                   SanitizerMask AlwaysIn, SanitizerMask AlwaysOut, int OptInID,
-                  int OptOutID, SanitizerMaskWeights Weights) {
+                  int OptOutID, SanitizerMaskWeights* Weights) {
   assert(!(AlwaysIn & AlwaysOut) &&
          "parseSanitizeArgs called with contradictory in/out requirements");
 
@@ -327,7 +327,7 @@ static SanitizerMask parseSanitizeTrapArgs(const Driver &D,
 static SanitizerMask parseNoSanitizeHotArgs(const Driver &D,
                                             const llvm::opt::ArgList &Args,
                                             bool DiagnoseErrors,
-                                            SanitizerMaskWeights Weights) {
+                                            SanitizerMaskWeights *Weights) {
   return parseSanitizeArgs(D, Args, DiagnoseErrors, {}, {}, {},
                            options::OPT_fno_sanitize_top_hot_EQ, -1, Weights);
 }
@@ -724,7 +724,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
 
   // Parse -fno-sanitize-top-hot flags
   SanitizerMask TopHotMask =
-      parseNoSanitizeHotArgs(D, Args, DiagnoseErrors, TopHotWeights);
+      parseNoSanitizeHotArgs(D, Args, DiagnoseErrors, &TopHotWeights);
   (void)TopHotMask;
 
   // Setup ignorelist files.
@@ -1505,7 +1505,7 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
 
 SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
                              bool DiagnoseErrors,
-                             SanitizerMaskWeights Weights) {
+                             SanitizerMaskWeights *Weights) {
   assert((A->getOption().matches(options::OPT_fsanitize_EQ) ||
           A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
           A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index df7fac32fec581..084e62c467535e 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1438,7 +1438,7 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
 
 static void parseSanitizerWeightedKinds(
     StringRef FlagName, const std::vector<std::string> &Sanitizers,
-    DiagnosticsEngine &Diags, SanitizerSet &S, SanitizerMaskWeights &Weights) {
+    DiagnosticsEngine &Diags, SanitizerSet &S, SanitizerMaskWeights *Weights) {
   for (const auto &Sanitizer : Sanitizers) {
     SanitizerMask K =
         parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Weights);
@@ -2299,7 +2299,7 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
   parseSanitizerWeightedKinds("-fno-sanitize-top-hot=",
                               Args.getAllArgValues(OPT_fno_sanitize_top_hot_EQ),
                               Diags, Opts.NoSanitizeTopHot,
-                              Opts.NoSanitizeTopHotWeights);
+                              &Opts.NoSanitizeTopHotWeights);
 
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 

>From bfa57c67cd6364533d309c939d4036871601aa65 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Mon, 6 Jan 2025 22:43:40 +0000
Subject: [PATCH 07/25] clang-format

---
 clang/lib/Basic/Sanitizers.cpp     | 6 +++---
 clang/lib/Driver/SanitizerArgs.cpp | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 179f2ad03714b1..b2fc7f8bcf49f0 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -36,9 +36,9 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
   return ParsedKind;
 }
 
-SanitizerMask clang::parseSanitizerWeightedValue(StringRef Value,
-                                                 bool AllowGroups,
-                                                 SanitizerMaskWeights *Weights) {
+SanitizerMask
+clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
+                                   SanitizerMaskWeights *Weights) {
   SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value)
 #define SANITIZER(NAME, ID) .StartsWith(NAME "=", SanitizerKind::ID)
 #define SANITIZER_GROUP(NAME, ID, ALIAS)                                       \
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 44c1b353cf2d9f..faa97f3bc5da79 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -261,7 +261,7 @@ static SanitizerMask
 parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
                   bool DiagnoseErrors, SanitizerMask Default,
                   SanitizerMask AlwaysIn, SanitizerMask AlwaysOut, int OptInID,
-                  int OptOutID, SanitizerMaskWeights* Weights) {
+                  int OptOutID, SanitizerMaskWeights *Weights) {
   assert(!(AlwaysIn & AlwaysOut) &&
          "parseSanitizeArgs called with contradictory in/out requirements");
 

>From 346adc1d00a6f8565bd9d1604c90377dd3fcf5d8 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Mon, 6 Jan 2025 23:28:45 +0000
Subject: [PATCH 08/25] Rename Weights to Cutoffs

---
 clang/include/clang/Basic/CodeGenOptions.h | 10 +++---
 clang/include/clang/Basic/Sanitizers.h     | 10 +++---
 clang/include/clang/Driver/Options.td      |  6 ++--
 clang/include/clang/Driver/SanitizerArgs.h |  2 +-
 clang/lib/Basic/Sanitizers.cpp             | 14 ++++----
 clang/lib/Driver/SanitizerArgs.cpp         | 38 +++++++++++-----------
 clang/lib/Frontend/CompilerInvocation.cpp  |  8 ++---
 7 files changed, 45 insertions(+), 43 deletions(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index e648bc4ea90cdf..227bddf831002e 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -384,13 +384,13 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// the expense of debuggability).
   SanitizerSet SanitizeMergeHandlers;
 
-  /// Set of thresholds, specifying the top hottest fraction of code to be
-  /// excluded from sanitization (0.0 = skip none, 0.1 = skip hottest 10%,
-  /// 1.0 = skip all).
+  /// Set of thresholds: the top hottest code responsible for the given
+  /// fraction of PGO counters will be excluded from sanitization
+  /// (0.0 [default] = skip none, 1.0 = skip all).
   SanitizerSet NoSanitizeTopHot;
-  /// N.B. The weights contain strictly more information than the SanitizerSet,
+  /// N.B. The cutoffs contain strictly more information than the SanitizerSet,
   /// but the SanitizerSet is more efficient for some calculations.
-  SanitizerMaskWeights NoSanitizeTopHotWeights = {0};
+  SanitizerMaskCutoffs NoSanitizeTopHotCutoffs = {0};
 
   /// List of backend command-line options for -fembed-bitcode.
   std::vector<uint8_t> CmdArgs;
diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index c1242a1a69ef3f..12c2c93a7f89f6 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -154,7 +154,7 @@ struct SanitizerKind {
 #include "clang/Basic/Sanitizers.def"
 }; // SanitizerKind
 
-using SanitizerMaskWeights = std::array<float, SanitizerKind::SO_Count>;
+using SanitizerMaskCutoffs = std::array<float, SanitizerKind::SO_Count>;
 
 struct SanitizerSet {
   /// Check if a certain (single) sanitizer is enabled.
@@ -192,18 +192,18 @@ SanitizerMask parseSanitizerValue(StringRef Value, bool AllowGroups);
 /// -fno-sanitize= value list.
 /// Returns a non-zero SanitizerMask, or \c 0 if \p Value is not known.
 /// The relevant weight(s) are updated in the passed array.
-/// Individual weights are never reset to zero unless explicitly set
+/// Individual Cutoffs are never reset to zero unless explicitly set
 /// (e.g., 'null=0.0').
 SanitizerMask parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
-                                          SanitizerMaskWeights *Weights);
+                                          SanitizerMaskCutoffs *Cutoffs);
 
 /// Serialize a SanitizerSet into values for -fsanitize= or -fno-sanitize=.
 void serializeSanitizerSet(SanitizerSet Set,
                            SmallVectorImpl<StringRef> &Values);
 
-/// Serialize a SanitizerMaskWeights into values for -fsanitize= or
+/// Serialize a SanitizerMaskCutoffs into values for -fsanitize= or
 /// -fno-sanitize=.
-void serializeSanitizerMaskWeights(const SanitizerMaskWeights Weights,
+void serializeSanitizerMaskCutoffs(const SanitizerMaskCutoffs Cutoffs,
                                    SmallVectorImpl<std::string> &Values);
 
 /// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index ecdd4d1655cb78..027093157d4c73 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2652,8 +2652,10 @@ def fsanitize_undefined_strip_path_components_EQ : Joined<["-"], "fsanitize-unde
 def fno_sanitize_top_hot_EQ
     : CommaJoined<["-"], "fno-sanitize-top-hot=">,
       Group<f_clang_Group>,
-      HelpText<"Exclude sanitization for the top hottest fraction of code "
-               "(0.0 [default] = skip none; 0.1 = skip hottest 10%; 1.0 = skip all)">;
+      HelpText<"Exclude sanitization for the top hottest code responsible for "
+               "the given fraction of PGO counters "
+               "(0.0 [default] = skip none; 1.0 = skip all). "
+               "Argument format: <sanitizer1>=,<sanitizer2>=,...">;
 
 } // end -f[no-]sanitize* flags
 
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 28c067444f2e4c..2462228f533746 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -27,7 +27,7 @@ class SanitizerArgs {
   SanitizerSet TrapSanitizers;
   SanitizerSet MergeHandlers;
   SanitizerSet TopHot;
-  SanitizerMaskWeights TopHotWeights = {0};
+  SanitizerMaskCutoffs TopHotCutoffs = {0};
 
   std::vector<std::string> UserIgnorelistFiles;
   std::vector<std::string> SystemIgnorelistFiles;
diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index b2fc7f8bcf49f0..6711b05c4539dd 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -38,7 +38,7 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
 
 SanitizerMask
 clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
-                                   SanitizerMaskWeights *Weights) {
+                                   SanitizerMaskCutoffs *Cutoffs) {
   SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value)
 #define SANITIZER(NAME, ID) .StartsWith(NAME "=", SanitizerKind::ID)
 #define SANITIZER_GROUP(NAME, ID, ALIAS)                                       \
@@ -47,7 +47,7 @@ clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
 #include "clang/Basic/Sanitizers.def"
                                  .Default(SanitizerMask());
 
-  if (ParsedKind && Weights) {
+  if (ParsedKind && Cutoffs) {
     size_t equalsIndex = Value.find_first_of('=');
     if (equalsIndex != llvm::StringLiteral::npos) {
       double arg;
@@ -59,7 +59,7 @@ clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
 
         for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++)
           if (ExpandedKind & SanitizerMask::bitPosToMask(i))
-            (*Weights)[i] = arg;
+            (*Cutoffs)[i] = arg;
       }
     }
   }
@@ -75,12 +75,12 @@ void clang::serializeSanitizerSet(SanitizerSet Set,
 #include "clang/Basic/Sanitizers.def"
 }
 
-void clang::serializeSanitizerMaskWeights(
-    const SanitizerMaskWeights Weights, SmallVectorImpl<std::string> &Values) {
+void clang::serializeSanitizerMaskCutoffs(
+    const SanitizerMaskCutoffs Cutoffs, SmallVectorImpl<std::string> &Values) {
 #define SANITIZER(NAME, ID)                                                    \
-  if (Weights[SanitizerKind::SO_##ID])                                         \
+  if (Cutoffs[SanitizerKind::SO_##ID])                                         \
     Values.push_back(std::string(NAME "=") +                                   \
-                     std::to_string(Weights[SanitizerKind::SO_##ID]));
+                     std::to_string(Cutoffs[SanitizerKind::SO_##ID]));
 #include "clang/Basic/Sanitizers.def"
 }
 
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index faa97f3bc5da79..f7db3e5032ce1a 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -112,7 +112,7 @@ enum BinaryMetadataFeature {
 /// invalid components. Returns a SanitizerMask.
 static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
                                     bool DiagnoseErrors,
-                                    SanitizerMaskWeights *Weights);
+                                    SanitizerMaskCutoffs *Cutoffs);
 
 /// Parse -f(no-)?sanitize-coverage= flag values, diagnosing any invalid
 /// components. Returns OR of members of \c CoverageFeature enumeration.
@@ -261,7 +261,7 @@ static SanitizerMask
 parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
                   bool DiagnoseErrors, SanitizerMask Default,
                   SanitizerMask AlwaysIn, SanitizerMask AlwaysOut, int OptInID,
-                  int OptOutID, SanitizerMaskWeights *Weights) {
+                  int OptOutID, SanitizerMaskCutoffs *Cutoffs) {
   assert(!(AlwaysIn & AlwaysOut) &&
          "parseSanitizeArgs called with contradictory in/out requirements");
 
@@ -272,7 +272,7 @@ parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
   SanitizerMask DiagnosedAlwaysOutViolations;
   for (const auto *Arg : Args) {
     if (Arg->getOption().matches(OptInID)) {
-      SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors, Weights);
+      SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors, Cutoffs);
       // Report error if user explicitly tries to opt-in to an always-out
       // sanitizer.
       if (SanitizerMask KindsToDiagnose =
@@ -288,7 +288,7 @@ parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
       Output |= expandSanitizerGroups(Add);
       Arg->claim();
     } else if (Arg->getOption().matches(OptOutID)) {
-      SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors, Weights);
+      SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors, Cutoffs);
       // Report error if user explicitly tries to opt-out of an always-in
       // sanitizer.
       if (SanitizerMask KindsToDiagnose =
@@ -327,9 +327,9 @@ static SanitizerMask parseSanitizeTrapArgs(const Driver &D,
 static SanitizerMask parseNoSanitizeHotArgs(const Driver &D,
                                             const llvm::opt::ArgList &Args,
                                             bool DiagnoseErrors,
-                                            SanitizerMaskWeights *Weights) {
+                                            SanitizerMaskCutoffs *Cutoffs) {
   return parseSanitizeArgs(D, Args, DiagnoseErrors, {}, {}, {},
-                           options::OPT_fno_sanitize_top_hot_EQ, -1, Weights);
+                           options::OPT_fno_sanitize_top_hot_EQ, -1, Cutoffs);
 }
 
 bool SanitizerArgs::needsFuzzerInterceptors() const {
@@ -724,7 +724,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
 
   // Parse -fno-sanitize-top-hot flags
   SanitizerMask TopHotMask =
-      parseNoSanitizeHotArgs(D, Args, DiagnoseErrors, &TopHotWeights);
+      parseNoSanitizeHotArgs(D, Args, DiagnoseErrors, &TopHotCutoffs);
   (void)TopHotMask;
 
   // Setup ignorelist files.
@@ -1153,7 +1153,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   // Zero out TopHot for unused sanitizers
   for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
     if (!(Sanitizers.Mask & SanitizerMask::bitPosToMask(i)))
-      TopHotWeights[i] = 0;
+      TopHotCutoffs[i] = 0;
   }
 }
 
@@ -1169,14 +1169,14 @@ static std::string toString(const clang::SanitizerSet &Sanitizers) {
   return Res;
 }
 
-static std::string toString(const clang::SanitizerMaskWeights &Weights) {
+static std::string toString(const clang::SanitizerMaskCutoffs &Cutoffs) {
   std::string Res;
 #define SANITIZER(NAME, ID)                                                    \
-  if (Weights[SanitizerKind::SO_##ID]) {                                       \
+  if (Cutoffs[SanitizerKind::SO_##ID]) {                                       \
     if (!Res.empty())                                                          \
       Res += ",";                                                              \
     Res += std::string(NAME) + "=" +                                           \
-           std::to_string(Weights[SanitizerKind::SO_##ID]);                    \
+           std::to_string(Cutoffs[SanitizerKind::SO_##ID]);                    \
   }
 #include "clang/Basic/Sanitizers.def"
   return Res;
@@ -1333,10 +1333,10 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
     CmdArgs.push_back(
         Args.MakeArgString("-fsanitize-merge=" + toString(MergeHandlers)));
 
-  std::string TopHotWeightsStr = toString(TopHotWeights);
-  if (TopHotWeightsStr != "")
+  std::string TopHotCutoffsStr = toString(TopHotCutoffs);
+  if (TopHotCutoffsStr != "")
     CmdArgs.push_back(
-        Args.MakeArgString("-fno-sanitize-top-hot=" + TopHotWeightsStr));
+        Args.MakeArgString("-fno-sanitize-top-hot=" + TopHotCutoffsStr));
 
   addSpecialCaseListOpt(Args, CmdArgs,
                         "-fsanitize-ignorelist=", UserIgnorelistFiles);
@@ -1505,7 +1505,7 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
 
 SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
                              bool DiagnoseErrors,
-                             SanitizerMaskWeights *Weights) {
+                             SanitizerMaskCutoffs *Cutoffs) {
   assert((A->getOption().matches(options::OPT_fsanitize_EQ) ||
           A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
           A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
@@ -1526,11 +1526,11 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
       Kind = SanitizerMask();
     else if (A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ)) {
       assert(
-          Weights &&
-          "Null weights parameter provided for parsing fno_sanitize_top_hot!");
-      Kind = parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Weights);
+          Cutoffs &&
+          "Null Cutoffs parameter provided for parsing fno_sanitize_top_hot!");
+      Kind = parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs);
     } else {
-      assert((!Weights) && "Non-null weights parameter erroneously provided!");
+      assert((!Cutoffs) && "Non-null Cutoffs parameter erroneously provided!");
       Kind = parseSanitizerValue(Value, /*AllowGroups=*/true);
     }
 
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 084e62c467535e..78dd5099259f18 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1438,10 +1438,10 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
 
 static void parseSanitizerWeightedKinds(
     StringRef FlagName, const std::vector<std::string> &Sanitizers,
-    DiagnosticsEngine &Diags, SanitizerSet &S, SanitizerMaskWeights *Weights) {
+    DiagnosticsEngine &Diags, SanitizerSet &S, SanitizerMaskCutoffs *Cutoffs) {
   for (const auto &Sanitizer : Sanitizers) {
     SanitizerMask K =
-        parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Weights);
+        parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Cutoffs);
     if (K == SanitizerMask())
       Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer;
     else
@@ -1810,7 +1810,7 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
     GenerateArg(Consumer, OPT_fsanitize_merge_handlers_EQ, Sanitizer);
 
   SmallVector<std::string, 4> Values;
-  serializeSanitizerMaskWeights(Opts.NoSanitizeTopHotWeights, Values);
+  serializeSanitizerMaskCutoffs(Opts.NoSanitizeTopHotCutoffs, Values);
   for (std::string Sanitizer : Values)
     GenerateArg(Consumer, OPT_fno_sanitize_top_hot_EQ, Sanitizer);
 
@@ -2299,7 +2299,7 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
   parseSanitizerWeightedKinds("-fno-sanitize-top-hot=",
                               Args.getAllArgValues(OPT_fno_sanitize_top_hot_EQ),
                               Diags, Opts.NoSanitizeTopHot,
-                              &Opts.NoSanitizeTopHotWeights);
+                              &Opts.NoSanitizeTopHotCutoffs);
 
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 

>From 66dbc49408f13089b1602c3b27db14b9cccf6d64 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Mon, 6 Jan 2025 23:30:51 +0000
Subject: [PATCH 09/25] Limit changes to Driver

---
 clang/lib/CodeGen/CGExpr.cpp           | 6 +-----
 clang/test/CodeGen/allow-ubsan-check.c | 5 -----
 2 files changed, 1 insertion(+), 10 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 3e14b0ffa7b320..ba1cba291553b0 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3602,7 +3602,6 @@ void CodeGenFunction::EmitCheck(
   llvm::Value *RecoverableCond = nullptr;
   llvm::Value *TrapCond = nullptr;
   bool NoMerge = false;
-  bool SanitizeGuardChecks = ClSanitizeGuardChecks;
   for (int i = 0, n = Checked.size(); i < n; ++i) {
     llvm::Value *Check = Checked[i].first;
     // -fsanitize-trap= overrides -fsanitize-recover=.
@@ -3616,12 +3615,9 @@ void CodeGenFunction::EmitCheck(
 
     if (!CGM.getCodeGenOpts().SanitizeMergeHandlers.has(Checked[i].second))
       NoMerge = true;
-
-    if (!CGM.getCodeGenOpts().NoSanitizeTopHot.has(Checked[i].second))
-      SanitizeGuardChecks = true;
   }
 
-  if (SanitizeGuardChecks) {
+  if (ClSanitizeGuardChecks) {
     llvm::Value *Allow =
         Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::allow_ubsan_check),
                            llvm::ConstantInt::get(CGM.Int8Ty, CheckHandler));
diff --git a/clang/test/CodeGen/allow-ubsan-check.c b/clang/test/CodeGen/allow-ubsan-check.c
index adc56dee9ad02e..1c76049b57bda8 100644
--- a/clang/test/CodeGen/allow-ubsan-check.c
+++ b/clang/test/CodeGen/allow-ubsan-check.c
@@ -3,11 +3,6 @@
 // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -mllvm -ubsan-guard-checks -fsanitize-trap=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=TRAP
 // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -mllvm -ubsan-guard-checks -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=RECOVER
 
-// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fno-sanitize-top-hot=signed-integer-overflow=1,integer-divide-by-zero=1,null=1 | FileCheck %s
-// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fno-sanitize-top-hot=signed-integer-overflow=1,integer-divide-by-zero=1,null=1 -fsanitize-trap=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=TRAP
-// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fno-sanitize-top-hot=signed-integer-overflow=1,integer-divide-by-zero=1,null=1 -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=RECOVER
-
-
 // CHECK-LABEL: define dso_local i32 @div(
 // CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  entry:

>From 6f5e7145d0c8eb2f8f62638d9fa15b3be777fb68 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 00:17:57 +0000
Subject: [PATCH 10/25] Reorder to make comment more logical

---
 clang/include/clang/Basic/CodeGenOptions.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 227bddf831002e..32dc39ed2a812b 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -387,10 +387,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// Set of thresholds: the top hottest code responsible for the given
   /// fraction of PGO counters will be excluded from sanitization
   /// (0.0 [default] = skip none, 1.0 = skip all).
-  SanitizerSet NoSanitizeTopHot;
+  SanitizerMaskCutoffs NoSanitizeTopHotCutoffs = {0};
   /// N.B. The cutoffs contain strictly more information than the SanitizerSet,
   /// but the SanitizerSet is more efficient for some calculations.
-  SanitizerMaskCutoffs NoSanitizeTopHotCutoffs = {0};
+  SanitizerSet NoSanitizeTopHot;
 
   /// List of backend command-line options for -fembed-bitcode.
   std::vector<uint8_t> CmdArgs;

>From fc623f2f0e4cdda0533a26106a428d621dcacb5a Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 01:16:31 +0000
Subject: [PATCH 11/25] Refactor into parseArgCutoffs; update comment

---
 clang/include/clang/Basic/Sanitizers.h |  2 +-
 clang/include/clang/Driver/Options.td  |  2 +-
 clang/lib/Basic/Sanitizers.cpp         |  2 +-
 clang/lib/Driver/SanitizerArgs.cpp     | 74 +++++++++++++++++++-------
 4 files changed, 59 insertions(+), 21 deletions(-)

diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index 12c2c93a7f89f6..9ddd6cd546f9a1 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -203,7 +203,7 @@ void serializeSanitizerSet(SanitizerSet Set,
 
 /// Serialize a SanitizerMaskCutoffs into values for -fsanitize= or
 /// -fno-sanitize=.
-void serializeSanitizerMaskCutoffs(const SanitizerMaskCutoffs Cutoffs,
+void serializeSanitizerMaskCutoffs(const SanitizerMaskCutoffs &Cutoffs,
                                    SmallVectorImpl<std::string> &Values);
 
 /// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 027093157d4c73..b34faa544c609d 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2655,7 +2655,7 @@ def fno_sanitize_top_hot_EQ
       HelpText<"Exclude sanitization for the top hottest code responsible for "
                "the given fraction of PGO counters "
                "(0.0 [default] = skip none; 1.0 = skip all). "
-               "Argument format: <sanitizer1>=,<sanitizer2>=,...">;
+               "Argument format: <sanitizer1>=<value1>,<sanitizer2>=<value2>,...">;
 
 } // end -f[no-]sanitize* flags
 
diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 6711b05c4539dd..74150f32c0ae78 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -76,7 +76,7 @@ void clang::serializeSanitizerSet(SanitizerSet Set,
 }
 
 void clang::serializeSanitizerMaskCutoffs(
-    const SanitizerMaskCutoffs Cutoffs, SmallVectorImpl<std::string> &Values) {
+    const SanitizerMaskCutoffs &Cutoffs, SmallVectorImpl<std::string> &Values) {
 #define SANITIZER(NAME, ID)                                                    \
   if (Cutoffs[SanitizerKind::SO_##ID])                                         \
     Values.push_back(std::string(NAME "=") +                                   \
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index f7db3e5032ce1a..66d19545f40287 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -111,6 +111,22 @@ enum BinaryMetadataFeature {
 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
 /// invalid components. Returns a SanitizerMask.
 static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
+                                    bool DiagnoseErrors);
+
+/// Parse a -fsanitize=<sanitizer1>=<value1>... or -fno-sanitize= argument's
+/// values, diagnosing any invalid components. Returns a SanitizerMask.
+/// Cutoffs are stored in the passed parameter.
+static SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
+                                     bool DiagnoseErrors,
+                                     SanitizerMaskCutoffs *Cutoffs);
+
+/// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
+/// invalid components. Returns a SanitizerMask.
+///
+/// If Cutoffs is null, it assumes -fsanitize=<sanitizer1>...
+/// Othrewise, it assumes -fsanitize=<sanitizer1>=<value1>..., and cutoffs are
+/// stored in the passed parameter.
+static SanitizerMask parseArgValuesOrCutoffs(const Driver &D, const llvm::opt::Arg *A,
                                     bool DiagnoseErrors,
                                     SanitizerMaskCutoffs *Cutoffs);
 
@@ -272,7 +288,7 @@ parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
   SanitizerMask DiagnosedAlwaysOutViolations;
   for (const auto *Arg : Args) {
     if (Arg->getOption().matches(OptInID)) {
-      SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors, Cutoffs);
+      SanitizerMask Add = parseArgValuesOrCutoffs(D, Arg, DiagnoseErrors, Cutoffs);
       // Report error if user explicitly tries to opt-in to an always-out
       // sanitizer.
       if (SanitizerMask KindsToDiagnose =
@@ -288,7 +304,7 @@ parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
       Output |= expandSanitizerGroups(Add);
       Arg->claim();
     } else if (Arg->getOption().matches(OptOutID)) {
-      SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors, Cutoffs);
+      SanitizerMask Remove = parseArgValuesOrCutoffs(D, Arg, DiagnoseErrors, Cutoffs);
       // Report error if user explicitly tries to opt-out of an always-in
       // sanitizer.
       if (SanitizerMask KindsToDiagnose =
@@ -412,7 +428,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   for (const llvm::opt::Arg *Arg : llvm::reverse(Args)) {
     if (Arg->getOption().matches(options::OPT_fsanitize_EQ)) {
       Arg->claim();
-      SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors, nullptr);
+      SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors);
 
       if (RemoveObjectSizeAtO0) {
         AllRemove |= SanitizerKind::ObjectSize;
@@ -582,7 +598,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
       Kinds |= Add;
     } else if (Arg->getOption().matches(options::OPT_fno_sanitize_EQ)) {
       Arg->claim();
-      SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors, nullptr);
+      SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors);
       AllRemove |= expandSanitizerGroups(Remove);
     }
   }
@@ -1504,8 +1520,7 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
 }
 
 SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
-                             bool DiagnoseErrors,
-                             SanitizerMaskCutoffs *Cutoffs) {
+                             bool DiagnoseErrors) {
   assert((A->getOption().matches(options::OPT_fsanitize_EQ) ||
           A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
           A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
@@ -1513,8 +1528,7 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
           A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
           A->getOption().matches(options::OPT_fno_sanitize_trap_EQ) ||
           A->getOption().matches(options::OPT_fsanitize_merge_handlers_EQ) ||
-          A->getOption().matches(options::OPT_fno_sanitize_merge_handlers_EQ) ||
-          A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ)) &&
+          A->getOption().matches(options::OPT_fno_sanitize_merge_handlers_EQ)) &&
          "Invalid argument in parseArgValues!");
   SanitizerMask Kinds;
   for (int i = 0, n = A->getNumValues(); i != n; ++i) {
@@ -1524,15 +1538,8 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
     if (A->getOption().matches(options::OPT_fsanitize_EQ) &&
         0 == strcmp("all", Value))
       Kind = SanitizerMask();
-    else if (A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ)) {
-      assert(
-          Cutoffs &&
-          "Null Cutoffs parameter provided for parsing fno_sanitize_top_hot!");
-      Kind = parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs);
-    } else {
-      assert((!Cutoffs) && "Non-null Cutoffs parameter erroneously provided!");
+    else
       Kind = parseSanitizerValue(Value, /*AllowGroups=*/true);
-    }
 
     if (Kind)
       Kinds |= Kind;
@@ -1543,6 +1550,37 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
   return Kinds;
 }
 
+SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
+                              bool DiagnoseErrors,
+                              SanitizerMaskCutoffs *Cutoffs) {
+  assert(A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ) &&
+         "Invalid argument in parseArgCutoffs!");
+  assert(Cutoffs &&
+        "Null Cutoffs parameter provided for parsing fno_sanitize_top_hot!");
+
+  SanitizerMask Kinds;
+  for (int i = 0, n = A->getNumValues(); i != n; ++i) {
+    const char *Value = A->getValue(i);
+    SanitizerMask Kind = parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs);
+
+    if (Kind)
+      Kinds |= Kind;
+    else if (DiagnoseErrors)
+      D.Diag(clang::diag::err_drv_unsupported_option_argument)
+          << A->getSpelling() << Value;
+  }
+  return Kinds;
+}
+
+SanitizerMask parseArgValuesOrCutoffs(const Driver &D, const llvm::opt::Arg *A,
+                                      bool DiagnoseErrors,
+                                      SanitizerMaskCutoffs *Cutoffs) {
+  if (Cutoffs)
+    return parseArgCutoffs(D, A, DiagnoseErrors, Cutoffs);
+  else
+    return parseArgValues(D, A, DiagnoseErrors);
+}
+
 static int parseOverflowPatternExclusionValues(const Driver &D,
                                                const llvm::opt::Arg *A,
                                                bool DiagnoseErrors) {
@@ -1635,12 +1673,12 @@ std::string lastArgumentForMask(const Driver &D, const llvm::opt::ArgList &Args,
     const auto *Arg = *I;
     if (Arg->getOption().matches(options::OPT_fsanitize_EQ)) {
       SanitizerMask AddKinds =
-          expandSanitizerGroups(parseArgValues(D, Arg, false, nullptr));
+          expandSanitizerGroups(parseArgValues(D, Arg, false));
       if (AddKinds & Mask)
         return describeSanitizeArg(Arg, Mask);
     } else if (Arg->getOption().matches(options::OPT_fno_sanitize_EQ)) {
       SanitizerMask RemoveKinds =
-          expandSanitizerGroups(parseArgValues(D, Arg, false, nullptr));
+          expandSanitizerGroups(parseArgValues(D, Arg, false));
       Mask &= ~RemoveKinds;
     }
   }

>From 00d135ee99302bf30c727e785ec900e97030197d Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 01:22:52 +0000
Subject: [PATCH 12/25] Remove unnecessary suppression

---
 clang/lib/Driver/SanitizerArgs.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 66d19545f40287..1033493ecb6c3f 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -741,7 +741,6 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   // Parse -fno-sanitize-top-hot flags
   SanitizerMask TopHotMask =
       parseNoSanitizeHotArgs(D, Args, DiagnoseErrors, &TopHotCutoffs);
-  (void)TopHotMask;
 
   // Setup ignorelist files.
   // Add default ignorelist from resource directory for activated sanitizers,

>From 4d97bed1b403f5736ad8eb83140fca2f5c3ab1e7 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 01:23:50 +0000
Subject: [PATCH 13/25] clang-format

---
 clang/include/clang/Driver/Options.td |  9 ++++---
 clang/lib/Driver/SanitizerArgs.cpp    | 37 +++++++++++++++------------
 2 files changed, 26 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index b34faa544c609d..4db17baa9c92f7 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2652,10 +2652,11 @@ def fsanitize_undefined_strip_path_components_EQ : Joined<["-"], "fsanitize-unde
 def fno_sanitize_top_hot_EQ
     : CommaJoined<["-"], "fno-sanitize-top-hot=">,
       Group<f_clang_Group>,
-      HelpText<"Exclude sanitization for the top hottest code responsible for "
-               "the given fraction of PGO counters "
-               "(0.0 [default] = skip none; 1.0 = skip all). "
-               "Argument format: <sanitizer1>=<value1>,<sanitizer2>=<value2>,...">;
+      HelpText<
+          "Exclude sanitization for the top hottest code responsible for "
+          "the given fraction of PGO counters "
+          "(0.0 [default] = skip none; 1.0 = skip all). "
+          "Argument format: <sanitizer1>=<value1>,<sanitizer2>=<value2>,...">;
 
 } // end -f[no-]sanitize* flags
 
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 1033493ecb6c3f..18c7090088d5c6 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -126,9 +126,10 @@ static SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
 /// If Cutoffs is null, it assumes -fsanitize=<sanitizer1>...
 /// Othrewise, it assumes -fsanitize=<sanitizer1>=<value1>..., and cutoffs are
 /// stored in the passed parameter.
-static SanitizerMask parseArgValuesOrCutoffs(const Driver &D, const llvm::opt::Arg *A,
-                                    bool DiagnoseErrors,
-                                    SanitizerMaskCutoffs *Cutoffs);
+static SanitizerMask parseArgValuesOrCutoffs(const Driver &D,
+                                             const llvm::opt::Arg *A,
+                                             bool DiagnoseErrors,
+                                             SanitizerMaskCutoffs *Cutoffs);
 
 /// Parse -f(no-)?sanitize-coverage= flag values, diagnosing any invalid
 /// components. Returns OR of members of \c CoverageFeature enumeration.
@@ -288,7 +289,8 @@ parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
   SanitizerMask DiagnosedAlwaysOutViolations;
   for (const auto *Arg : Args) {
     if (Arg->getOption().matches(OptInID)) {
-      SanitizerMask Add = parseArgValuesOrCutoffs(D, Arg, DiagnoseErrors, Cutoffs);
+      SanitizerMask Add =
+          parseArgValuesOrCutoffs(D, Arg, DiagnoseErrors, Cutoffs);
       // Report error if user explicitly tries to opt-in to an always-out
       // sanitizer.
       if (SanitizerMask KindsToDiagnose =
@@ -304,7 +306,8 @@ parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
       Output |= expandSanitizerGroups(Add);
       Arg->claim();
     } else if (Arg->getOption().matches(OptOutID)) {
-      SanitizerMask Remove = parseArgValuesOrCutoffs(D, Arg, DiagnoseErrors, Cutoffs);
+      SanitizerMask Remove =
+          parseArgValuesOrCutoffs(D, Arg, DiagnoseErrors, Cutoffs);
       // Report error if user explicitly tries to opt-out of an always-in
       // sanitizer.
       if (SanitizerMask KindsToDiagnose =
@@ -1520,15 +1523,16 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
 
 SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
                              bool DiagnoseErrors) {
-  assert((A->getOption().matches(options::OPT_fsanitize_EQ) ||
-          A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
-          A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
-          A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
-          A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
-          A->getOption().matches(options::OPT_fno_sanitize_trap_EQ) ||
-          A->getOption().matches(options::OPT_fsanitize_merge_handlers_EQ) ||
-          A->getOption().matches(options::OPT_fno_sanitize_merge_handlers_EQ)) &&
-         "Invalid argument in parseArgValues!");
+  assert(
+      (A->getOption().matches(options::OPT_fsanitize_EQ) ||
+       A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
+       A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
+       A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
+       A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
+       A->getOption().matches(options::OPT_fno_sanitize_trap_EQ) ||
+       A->getOption().matches(options::OPT_fsanitize_merge_handlers_EQ) ||
+       A->getOption().matches(options::OPT_fno_sanitize_merge_handlers_EQ)) &&
+      "Invalid argument in parseArgValues!");
   SanitizerMask Kinds;
   for (int i = 0, n = A->getNumValues(); i != n; ++i) {
     const char *Value = A->getValue(i);
@@ -1555,12 +1559,13 @@ SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
   assert(A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ) &&
          "Invalid argument in parseArgCutoffs!");
   assert(Cutoffs &&
-        "Null Cutoffs parameter provided for parsing fno_sanitize_top_hot!");
+         "Null Cutoffs parameter provided for parsing fno_sanitize_top_hot!");
 
   SanitizerMask Kinds;
   for (int i = 0, n = A->getNumValues(); i != n; ++i) {
     const char *Value = A->getValue(i);
-    SanitizerMask Kind = parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs);
+    SanitizerMask Kind =
+        parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs);
 
     if (Kind)
       Kinds |= Kind;

>From 1f528c5c98b5dce1cd73aa3169d50b8e896daeb4 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 02:25:02 +0000
Subject: [PATCH 14/25] Change Cutoffs* to Cutoffs&

---
 clang/include/clang/Basic/Sanitizers.h    | 2 +-
 clang/lib/Basic/Sanitizers.cpp            | 6 +++---
 clang/lib/Driver/SanitizerArgs.cpp        | 9 +++------
 clang/lib/Frontend/CompilerInvocation.cpp | 4 ++--
 4 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index 9ddd6cd546f9a1..16f2b6c6da106a 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -195,7 +195,7 @@ SanitizerMask parseSanitizerValue(StringRef Value, bool AllowGroups);
 /// Individual Cutoffs are never reset to zero unless explicitly set
 /// (e.g., 'null=0.0').
 SanitizerMask parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
-                                          SanitizerMaskCutoffs *Cutoffs);
+                                          SanitizerMaskCutoffs &Cutoffs);
 
 /// Serialize a SanitizerSet into values for -fsanitize= or -fno-sanitize=.
 void serializeSanitizerSet(SanitizerSet Set,
diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 74150f32c0ae78..eecaf33ff97999 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -38,7 +38,7 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
 
 SanitizerMask
 clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
-                                   SanitizerMaskCutoffs *Cutoffs) {
+                                   SanitizerMaskCutoffs &Cutoffs) {
   SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value)
 #define SANITIZER(NAME, ID) .StartsWith(NAME "=", SanitizerKind::ID)
 #define SANITIZER_GROUP(NAME, ID, ALIAS)                                       \
@@ -47,7 +47,7 @@ clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
 #include "clang/Basic/Sanitizers.def"
                                  .Default(SanitizerMask());
 
-  if (ParsedKind && Cutoffs) {
+  if (ParsedKind) {
     size_t equalsIndex = Value.find_first_of('=');
     if (equalsIndex != llvm::StringLiteral::npos) {
       double arg;
@@ -59,7 +59,7 @@ clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
 
         for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++)
           if (ExpandedKind & SanitizerMask::bitPosToMask(i))
-            (*Cutoffs)[i] = arg;
+            Cutoffs[i] = arg;
       }
     }
   }
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 18c7090088d5c6..dd061264b3082f 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -118,7 +118,7 @@ static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
 /// Cutoffs are stored in the passed parameter.
 static SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
                                      bool DiagnoseErrors,
-                                     SanitizerMaskCutoffs *Cutoffs);
+                                     SanitizerMaskCutoffs &Cutoffs);
 
 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
 /// invalid components. Returns a SanitizerMask.
@@ -1555,12 +1555,9 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
 
 SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
                               bool DiagnoseErrors,
-                              SanitizerMaskCutoffs *Cutoffs) {
+                              SanitizerMaskCutoffs &Cutoffs) {
   assert(A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ) &&
          "Invalid argument in parseArgCutoffs!");
-  assert(Cutoffs &&
-         "Null Cutoffs parameter provided for parsing fno_sanitize_top_hot!");
-
   SanitizerMask Kinds;
   for (int i = 0, n = A->getNumValues(); i != n; ++i) {
     const char *Value = A->getValue(i);
@@ -1580,7 +1577,7 @@ SanitizerMask parseArgValuesOrCutoffs(const Driver &D, const llvm::opt::Arg *A,
                                       bool DiagnoseErrors,
                                       SanitizerMaskCutoffs *Cutoffs) {
   if (Cutoffs)
-    return parseArgCutoffs(D, A, DiagnoseErrors, Cutoffs);
+    return parseArgCutoffs(D, A, DiagnoseErrors, *Cutoffs);
   else
     return parseArgValues(D, A, DiagnoseErrors);
 }
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 78dd5099259f18..f71c41e6ae8f7c 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1438,7 +1438,7 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
 
 static void parseSanitizerWeightedKinds(
     StringRef FlagName, const std::vector<std::string> &Sanitizers,
-    DiagnosticsEngine &Diags, SanitizerSet &S, SanitizerMaskCutoffs *Cutoffs) {
+    DiagnosticsEngine &Diags, SanitizerSet &S, SanitizerMaskCutoffs &Cutoffs) {
   for (const auto &Sanitizer : Sanitizers) {
     SanitizerMask K =
         parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Cutoffs);
@@ -2299,7 +2299,7 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
   parseSanitizerWeightedKinds("-fno-sanitize-top-hot=",
                               Args.getAllArgValues(OPT_fno_sanitize_top_hot_EQ),
                               Diags, Opts.NoSanitizeTopHot,
-                              &Opts.NoSanitizeTopHotCutoffs);
+                              Opts.NoSanitizeTopHotCutoffs);
 
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 

>From 071e10e79598409e8393848b5fd067b19bc725a2 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 03:45:42 +0000
Subject: [PATCH 15/25] Remove SanitizerMask TopHot

---
 clang/include/clang/Basic/CodeGenOptions.h |  3 ---
 clang/include/clang/Basic/Sanitizers.h     |  6 +++---
 clang/include/clang/Driver/SanitizerArgs.h |  1 -
 clang/lib/Basic/Sanitizers.cpp             |  6 ++++--
 clang/lib/Driver/SanitizerArgs.cpp         | 17 +++++++++--------
 clang/lib/Frontend/CompilerInvocation.cpp  | 11 +++--------
 6 files changed, 19 insertions(+), 25 deletions(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 32dc39ed2a812b..856dc10be92119 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -388,9 +388,6 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// fraction of PGO counters will be excluded from sanitization
   /// (0.0 [default] = skip none, 1.0 = skip all).
   SanitizerMaskCutoffs NoSanitizeTopHotCutoffs = {0};
-  /// N.B. The cutoffs contain strictly more information than the SanitizerSet,
-  /// but the SanitizerSet is more efficient for some calculations.
-  SanitizerSet NoSanitizeTopHot;
 
   /// List of backend command-line options for -fembed-bitcode.
   std::vector<uint8_t> CmdArgs;
diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index 16f2b6c6da106a..d92c5551616753 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -190,12 +190,12 @@ SanitizerMask parseSanitizerValue(StringRef Value, bool AllowGroups);
 
 /// Parse a single weighted value (e.g., 'undefined=0.05') from a -fsanitize= or
 /// -fno-sanitize= value list.
-/// Returns a non-zero SanitizerMask, or \c 0 if \p Value is not known.
 /// The relevant weight(s) are updated in the passed array.
 /// Individual Cutoffs are never reset to zero unless explicitly set
 /// (e.g., 'null=0.0').
-SanitizerMask parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
-                                          SanitizerMaskCutoffs &Cutoffs);
+/// Returns \c False if \p Value is not known.
+bool parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
+                                 SanitizerMaskCutoffs &Cutoffs);
 
 /// Serialize a SanitizerSet into values for -fsanitize= or -fno-sanitize=.
 void serializeSanitizerSet(SanitizerSet Set,
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 2462228f533746..098e5a1682dbc9 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -26,7 +26,6 @@ class SanitizerArgs {
   SanitizerSet RecoverableSanitizers;
   SanitizerSet TrapSanitizers;
   SanitizerSet MergeHandlers;
-  SanitizerSet TopHot;
   SanitizerMaskCutoffs TopHotCutoffs = {0};
 
   std::vector<std::string> UserIgnorelistFiles;
diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index eecaf33ff97999..59a0f6f0af1dae 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -36,7 +36,7 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
   return ParsedKind;
 }
 
-SanitizerMask
+bool
 clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
                                    SanitizerMaskCutoffs &Cutoffs) {
   SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value)
@@ -62,9 +62,11 @@ clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
             Cutoffs[i] = arg;
       }
     }
+
+    return true;
   }
 
-  return ParsedKind;
+  return false;
 }
 
 void clang::serializeSanitizerSet(SanitizerSet Set,
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index dd061264b3082f..32715965b64b7f 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -742,8 +742,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   MergeKinds &= Kinds;
 
   // Parse -fno-sanitize-top-hot flags
-  SanitizerMask TopHotMask =
-      parseNoSanitizeHotArgs(D, Args, DiagnoseErrors, &TopHotCutoffs);
+  parseNoSanitizeHotArgs(D, Args, DiagnoseErrors, &TopHotCutoffs);
 
   // Setup ignorelist files.
   // Add default ignorelist from resource directory for activated sanitizers,
@@ -1165,10 +1164,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
 
   MergeHandlers.Mask |= MergeKinds;
 
-  TopHotMask &= Sanitizers.Mask;
-  TopHot.Mask = TopHotMask;
-
-  // Zero out TopHot for unused sanitizers
+  // Zero out TopHotCutoffs for unused sanitizers
   for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
     if (!(Sanitizers.Mask & SanitizerMask::bitPosToMask(i)))
       TopHotCutoffs[i] = 0;
@@ -1561,8 +1557,13 @@ SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
   SanitizerMask Kinds;
   for (int i = 0, n = A->getNumValues(); i != n; ++i) {
     const char *Value = A->getValue(i);
-    SanitizerMask Kind =
-        parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs);
+    parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs);
+
+    SanitizerMask Kind;
+    for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
+      if (Cutoffs[i])
+        Kind |= SanitizerMask::bitPosToMask(i);
+    }
 
     if (Kind)
       Kinds |= Kind;
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index f71c41e6ae8f7c..a62b7ac0d3b042 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1438,14 +1438,10 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
 
 static void parseSanitizerWeightedKinds(
     StringRef FlagName, const std::vector<std::string> &Sanitizers,
-    DiagnosticsEngine &Diags, SanitizerSet &S, SanitizerMaskCutoffs &Cutoffs) {
+    DiagnosticsEngine &Diags,  SanitizerMaskCutoffs &Cutoffs) {
   for (const auto &Sanitizer : Sanitizers) {
-    SanitizerMask K =
-        parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Cutoffs);
-    if (K == SanitizerMask())
+    if (!parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Cutoffs))
       Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer;
-    else
-      S.set(K, true);
   }
 }
 
@@ -2298,8 +2294,7 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
   // Parse -fno-sanitize-top-hot= arguments.
   parseSanitizerWeightedKinds("-fno-sanitize-top-hot=",
                               Args.getAllArgValues(OPT_fno_sanitize_top_hot_EQ),
-                              Diags, Opts.NoSanitizeTopHot,
-                              Opts.NoSanitizeTopHotCutoffs);
+                              Diags, Opts.NoSanitizeTopHotCutoffs);
 
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 

>From c970179e7b902ea009f21135623d9f0f715bdd12 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 03:54:00 +0000
Subject: [PATCH 16/25] Rely on RVO

---
 clang/lib/Frontend/CompilerInvocation.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index a62b7ac0d3b042..b9df7237b93028 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1436,13 +1436,15 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
   return Values;
 }
 
-static void parseSanitizerWeightedKinds(
+static SanitizerMaskCutoffs parseSanitizerWeightedKinds(
     StringRef FlagName, const std::vector<std::string> &Sanitizers,
-    DiagnosticsEngine &Diags,  SanitizerMaskCutoffs &Cutoffs) {
+    DiagnosticsEngine &Diags) {
+  SanitizerMaskCutoffs Cutoffs;
   for (const auto &Sanitizer : Sanitizers) {
     if (!parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Cutoffs))
       Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer;
   }
+  return Cutoffs;
 }
 
 static void parseXRayInstrumentationBundle(StringRef FlagName, StringRef Bundle,
@@ -2292,9 +2294,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
                       Diags, Opts.SanitizeMergeHandlers);
 
   // Parse -fno-sanitize-top-hot= arguments.
-  parseSanitizerWeightedKinds("-fno-sanitize-top-hot=",
+  Opts.NoSanitizeTopHotCutoffs = parseSanitizerWeightedKinds("-fno-sanitize-top-hot=",
                               Args.getAllArgValues(OPT_fno_sanitize_top_hot_EQ),
-                              Diags, Opts.NoSanitizeTopHotCutoffs);
+                              Diags);
 
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 

>From b2bb1d6d34afce14de2587c39517c20f1f732aec Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 04:30:25 +0000
Subject: [PATCH 17/25] Fix uninitialized memory

---
 clang/lib/Driver/SanitizerArgs.cpp        | 3 +++
 clang/lib/Frontend/CompilerInvocation.cpp | 2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 32715965b64b7f..7c68f5c8b7f98a 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -1561,6 +1561,9 @@ SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
 
     SanitizerMask Kind;
     for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
+      // Invoking bitPosToMask repeatedly is inefficient: we could simply
+      // repeatedly set the LSB then left-shift; however, we assume the
+      // compiler will optimize this (in any case, the runtime is negligible).
       if (Cutoffs[i])
         Kind |= SanitizerMask::bitPosToMask(i);
     }
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index b9df7237b93028..27885869929f37 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1439,7 +1439,7 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
 static SanitizerMaskCutoffs parseSanitizerWeightedKinds(
     StringRef FlagName, const std::vector<std::string> &Sanitizers,
     DiagnosticsEngine &Diags) {
-  SanitizerMaskCutoffs Cutoffs;
+  SanitizerMaskCutoffs Cutoffs = {0};
   for (const auto &Sanitizer : Sanitizers) {
     if (!parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Cutoffs))
       Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer;

>From 369e6c81fea87a69db90f3276984b51dfd342df4 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 04:30:56 +0000
Subject: [PATCH 18/25] clang-format

---
 clang/lib/Basic/Sanitizers.cpp            |  5 ++---
 clang/lib/Frontend/CompilerInvocation.cpp | 13 +++++++------
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 59a0f6f0af1dae..9b84b04a284ef0 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -36,9 +36,8 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
   return ParsedKind;
 }
 
-bool
-clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
-                                   SanitizerMaskCutoffs &Cutoffs) {
+bool clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
+                                        SanitizerMaskCutoffs &Cutoffs) {
   SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value)
 #define SANITIZER(NAME, ID) .StartsWith(NAME "=", SanitizerKind::ID)
 #define SANITIZER_GROUP(NAME, ID, ALIAS)                                       \
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 27885869929f37..100b408b3092fe 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1436,9 +1436,10 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
   return Values;
 }
 
-static SanitizerMaskCutoffs parseSanitizerWeightedKinds(
-    StringRef FlagName, const std::vector<std::string> &Sanitizers,
-    DiagnosticsEngine &Diags) {
+static SanitizerMaskCutoffs
+parseSanitizerWeightedKinds(StringRef FlagName,
+                            const std::vector<std::string> &Sanitizers,
+                            DiagnosticsEngine &Diags) {
   SanitizerMaskCutoffs Cutoffs = {0};
   for (const auto &Sanitizer : Sanitizers) {
     if (!parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Cutoffs))
@@ -2294,9 +2295,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
                       Diags, Opts.SanitizeMergeHandlers);
 
   // Parse -fno-sanitize-top-hot= arguments.
-  Opts.NoSanitizeTopHotCutoffs = parseSanitizerWeightedKinds("-fno-sanitize-top-hot=",
-                              Args.getAllArgValues(OPT_fno_sanitize_top_hot_EQ),
-                              Diags);
+  Opts.NoSanitizeTopHotCutoffs = parseSanitizerWeightedKinds(
+      "-fno-sanitize-top-hot=",
+      Args.getAllArgValues(OPT_fno_sanitize_top_hot_EQ), Diags);
 
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 

>From c6d4e7344c427e482cc744bb6b8b89e3b6371eb7 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 06:22:27 +0000
Subject: [PATCH 19/25] Allow passing -fno-sanitize-top-hot=undefined=0.0. Add
 more tests.

---
 clang/lib/Driver/SanitizerArgs.cpp | 27 ++++++++-------
 clang/test/Driver/fsanitize.c      | 53 ++++++++++++++++++++----------
 2 files changed, 49 insertions(+), 31 deletions(-)

diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 7c68f5c8b7f98a..fd0564ede4131b 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -1554,26 +1554,25 @@ SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
                               SanitizerMaskCutoffs &Cutoffs) {
   assert(A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ) &&
          "Invalid argument in parseArgCutoffs!");
-  SanitizerMask Kinds;
   for (int i = 0, n = A->getNumValues(); i != n; ++i) {
     const char *Value = A->getValue(i);
-    parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs);
-
-    SanitizerMask Kind;
-    for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
-      // Invoking bitPosToMask repeatedly is inefficient: we could simply
-      // repeatedly set the LSB then left-shift; however, we assume the
-      // compiler will optimize this (in any case, the runtime is negligible).
-      if (Cutoffs[i])
-        Kind |= SanitizerMask::bitPosToMask(i);
-    }
 
-    if (Kind)
-      Kinds |= Kind;
-    else if (DiagnoseErrors)
+    // We don't check the value of Cutoffs[i]: it's legal to specify
+    // -fsanitize-blah=value=0.0.
+    if (!parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs))
       D.Diag(clang::diag::err_drv_unsupported_option_argument)
           << A->getSpelling() << Value;
   }
+
+  SanitizerMask Kinds;
+  for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
+    // Invoking bitPosToMask repeatedly is inefficient: we could simply
+    // set the LSB then left-shift in a loop; however, we assume the compiler
+    // will optimize this (in any case, the runtime is negligible).
+    if (Cutoffs[i])
+      Kinds |= SanitizerMask::bitPosToMask(i);
+  }
+
   return Kinds;
 }
 
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index 211104fd667278..3eef0ce5f7128b 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -1156,20 +1156,39 @@
 // CHECK-REALTIME-UBSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=undefined'
 
 
-// -fno-sanitize-top-hot
-// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT
-// CHECK-UNDEFINED-TOP-HOT: "-fno-sanitize-top-hot={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=0.5(0*),?){19}"}}
-
-// If no sanitizers are enabled, -fno-sanitize-top-hot is not passed
-// RUN: %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot=undefined=0.5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT2
-// CHECK-UNDEFINED-TOP-HOT2-NOT: "-fno-sanitize-top-hot"
-
-// Threshold of 0.0 cancels out the flag
-// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5,integer=0.0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT3
-// CHECK-UNDEFINED-TOP-HOT3: "-fno-sanitize-top-hot={{((unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=0.5(0*),?){15}"}}
-
-// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5,integer=0.0,signed-integer-overflow=0.7 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT4
-// CHECK-UNDEFINED-TOP-HOT4: "-fno-sanitize-top-hot={{((signed-integer-overflow|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=(0.5|0.7)(0*),?){16}"}}
-
-// RUN: %clang --target=x86_64-linux-gnu -fsanitize=integer -fno-sanitize-top-hot=undefined=0.4 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TOP-HOT5
-// CHECK-UNDEFINED-TOP-HOT5: "-fno-sanitize-top-hot={{((integer-divide-by-zero|shift-base|shift-exponent|signed-integer-overflow)=0.4(0*),?){4}"}}
+// * Test -fno-sanitize-top-hot *
+
+// -fno-sanitize-top-hot=undefined=0.5
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT1
+// CHECK-TOP-HOT1: "-fno-sanitize-top-hot={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=0.5(0*),?){19}"}}
+
+// If no sanitizers are specified, -fno-sanitize-top-hot=... is a no-op and does not enable any sanitizers.
+// RUN: %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot=undefined=0.5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT2
+// CHECK-TOP-HOT2-NOT: "-fsanitize"
+// CHECK-TOP-HOT2-NOT: "-fno-sanitize-top-hot"
+
+// Enable undefined, then cancel out integer using a cutoff of 0.0
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5,integer=0.0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT3
+// CHECK-TOP-HOT3: "-fno-sanitize-top-hot={{((unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=0.5(0*),?){15}"}}
+
+// Enable undefined, then cancel out integer using a cutoff of 0.0, then re-enable signed-integer-overflow
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5,integer=0.0,signed-integer-overflow=0.7 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT4
+// CHECK-TOP-HOT4: "-fno-sanitize-top-hot={{((signed-integer-overflow|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=(0.5|0.7)(0*),?){16}"}}
+
+// Check that -fno-sanitize-top-hot=undefined=0.4 does not widen the set of -fsanitize= checks.
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=integer -fno-sanitize-top-hot=undefined=0.4 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT5
+// CHECK-TOP-HOT5: "-fno-sanitize-top-hot={{((integer-divide-by-zero|shift-base|shift-exponent|signed-integer-overflow)=0.4(0*),?){4}"}}
+
+// It's allowed for the user to specify a cutoff of 0.0, though the argument is not passed along by the driver.
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT6
+// CHECK-TOP-HOT6: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr),?){19}"}}
+// CHECK-TOP-HOT6-NOT: unsupported argument
+// CHECK-TOP-HOT6-NOT: "-fno-sanitize-top-hot"
+
+// Invalid sanitizer 'pot'
+// RUN: not %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot=pot=0.0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT7
+// CHECK-TOP-HOT7: unsupported argument 'pot=0.0' to option '-fno-sanitize-top-hot='
+
+// -fno-sanitize-top without parameters is not valid
+// RUN: not %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT8
+// CHECK-TOP-HOT8: unknown argument: '-fno-sanitize-top-hot'

>From 85b898c33ff540a2c9f35a0d28a3745abd395255 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 17:08:04 +0000
Subject: [PATCH 20/25] Check cutoffs in parsing. Update tests.

---
 clang/lib/Basic/Sanitizers.cpp |  4 ++--
 clang/test/Driver/fsanitize.c  | 24 ++++++++++++++++++++----
 2 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 9b84b04a284ef0..e3c54d45610631 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -59,10 +59,10 @@ bool clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
         for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++)
           if (ExpandedKind & SanitizerMask::bitPosToMask(i))
             Cutoffs[i] = arg;
+
+        return true;
       }
     }
-
-    return true;
   }
 
   return false;
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index 3eef0ce5f7128b..f2da42e834a4d3 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -1185,10 +1185,26 @@
 // CHECK-TOP-HOT6-NOT: unsupported argument
 // CHECK-TOP-HOT6-NOT: "-fno-sanitize-top-hot"
 
-// Invalid sanitizer 'pot'
+// Invalid: bad sanitizer
 // RUN: not %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot=pot=0.0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT7
 // CHECK-TOP-HOT7: unsupported argument 'pot=0.0' to option '-fno-sanitize-top-hot='
 
-// -fno-sanitize-top without parameters is not valid
-// RUN: not %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT8
-// CHECK-TOP-HOT8: unknown argument: '-fno-sanitize-top-hot'
+// Invalid: bad cutoff
+// RUN: not %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot=undefined=xyzzy %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT8
+// CHECK-TOP-HOT8: unsupported argument 'undefined=xyzzy' to option '-fno-sanitize-top-hot='
+
+// Invalid: -fno-sanitize-top without parameters
+// RUN: not %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT9
+// CHECK-TOP-HOT9: unknown argument: '-fno-sanitize-top-hot'
+
+// Invalid: -fno-sanitize-top=undefined without cutoff
+// RUN: not %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT10
+// CHECK-TOP-HOT10: unsupported argument 'undefined' to option '-fno-sanitize-top-hot='
+
+// Invalid: -fno-sanitize-top=undefined= without cutoff
+// RUN: not %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot=undefined= %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT11
+// CHECK-TOP-HOT11: unsupported argument 'undefined=' to option '-fno-sanitize-top-hot='
+
+// -fno-sanitize-top= without parameters is unusual but valid (no-op)
+// RUN: %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot= %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT12
+// CHECK-TOP-HOT12-NOT: "-fno-sanitize-top-hot"

>From 6cd43bb3ceea000d763d17bfc5fc2ad085579bcd Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 17:14:41 +0000
Subject: [PATCH 21/25] Change cutoffs parser to return an empty SanitizerMask
 (Cutoffs becomes the sole source of truth)

---
 clang/lib/Driver/SanitizerArgs.cpp | 22 ++++++++--------------
 clang/test/Driver/fsanitize.c      |  9 +++++----
 2 files changed, 13 insertions(+), 18 deletions(-)

diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index fd0564ede4131b..3d28cb60a0c4dc 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -114,7 +114,7 @@ static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
                                     bool DiagnoseErrors);
 
 /// Parse a -fsanitize=<sanitizer1>=<value1>... or -fno-sanitize= argument's
-/// values, diagnosing any invalid components. Returns a SanitizerMask.
+/// values, diagnosing any invalid components. Returns an EMPTY SanitizerMask.
 /// Cutoffs are stored in the passed parameter.
 static SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
                                      bool DiagnoseErrors,
@@ -123,9 +123,12 @@ static SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
 /// invalid components. Returns a SanitizerMask.
 ///
-/// If Cutoffs is null, it assumes -fsanitize=<sanitizer1>...
-/// Othrewise, it assumes -fsanitize=<sanitizer1>=<value1>..., and cutoffs are
-/// stored in the passed parameter.
+/// If Cutoffs is null, it assumes -fsanitize=<sanitizer1>,<sanitizer2>,...
+/// and returns the result in the SanitizerMask.
+///
+/// Otherwise, it assumes -fsanitize=<sanitizer1>=<value1>,<sanitizer2>=<value2>,...
+/// and returns an EMPTY SanitizerMask; results are stored in the passed
+/// Cutoffs.
 static SanitizerMask parseArgValuesOrCutoffs(const Driver &D,
                                              const llvm::opt::Arg *A,
                                              bool DiagnoseErrors,
@@ -1564,16 +1567,7 @@ SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
           << A->getSpelling() << Value;
   }
 
-  SanitizerMask Kinds;
-  for (unsigned int i = 0; i < SanitizerKind::SO_Count; i++) {
-    // Invoking bitPosToMask repeatedly is inefficient: we could simply
-    // set the LSB then left-shift in a loop; however, we assume the compiler
-    // will optimize this (in any case, the runtime is negligible).
-    if (Cutoffs[i])
-      Kinds |= SanitizerMask::bitPosToMask(i);
-  }
-
-  return Kinds;
+  return {};
 }
 
 SanitizerMask parseArgValuesOrCutoffs(const Driver &D, const llvm::opt::Arg *A,
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index f2da42e834a4d3..8fab84dad1270e 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -1162,7 +1162,7 @@
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT1
 // CHECK-TOP-HOT1: "-fno-sanitize-top-hot={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=0.5(0*),?){19}"}}
 
-// If no sanitizers are specified, -fno-sanitize-top-hot=... is a no-op and does not enable any sanitizers.
+// No-op: no sanitizers are specified
 // RUN: %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot=undefined=0.5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT2
 // CHECK-TOP-HOT2-NOT: "-fsanitize"
 // CHECK-TOP-HOT2-NOT: "-fno-sanitize-top-hot"
@@ -1175,11 +1175,11 @@
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.5,integer=0.0,signed-integer-overflow=0.7 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT4
 // CHECK-TOP-HOT4: "-fno-sanitize-top-hot={{((signed-integer-overflow|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr)=(0.5|0.7)(0*),?){16}"}}
 
-// Check that -fno-sanitize-top-hot=undefined=0.4 does not widen the set of -fsanitize= checks.
+// Check that -fno-sanitize-top-hot=undefined=0.4 does not widen the set of -fsanitize=integer checks.
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=integer -fno-sanitize-top-hot=undefined=0.4 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT5
 // CHECK-TOP-HOT5: "-fno-sanitize-top-hot={{((integer-divide-by-zero|shift-base|shift-exponent|signed-integer-overflow)=0.4(0*),?){4}"}}
 
-// It's allowed for the user to specify a cutoff of 0.0, though the argument is not passed along by the driver.
+// No-op: it's allowed for the user to specify a cutoff of 0.0, though the argument is not passed along by the driver.
 // RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fno-sanitize-top-hot=undefined=0.0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT6
 // CHECK-TOP-HOT6: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|vptr),?){19}"}}
 // CHECK-TOP-HOT6-NOT: unsupported argument
@@ -1205,6 +1205,7 @@
 // RUN: not %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot=undefined= %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT11
 // CHECK-TOP-HOT11: unsupported argument 'undefined=' to option '-fno-sanitize-top-hot='
 
-// -fno-sanitize-top= without parameters is unusual but valid (no-op)
+// No-op: -fno-sanitize-top= without parameters is unusual but valid
 // RUN: %clang --target=x86_64-linux-gnu -fno-sanitize-top-hot= %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOP-HOT12
+// CHECK-TOP-HOT12-NOT: unsupported argument
 // CHECK-TOP-HOT12-NOT: "-fno-sanitize-top-hot"

>From c4b6a68fd5eb3e8a802225c604f833ea2ffa31e9 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 17:55:14 +0000
Subject: [PATCH 22/25] Simplify parseNoSanitizeHotArgs by not relying on
 parseSanitizeArgs

---
 clang/lib/Driver/SanitizerArgs.cpp | 41 ++++++++++--------------------
 1 file changed, 13 insertions(+), 28 deletions(-)

diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 3d28cb60a0c4dc..f0d6cb029c3916 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -120,20 +120,6 @@ static SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
                                      bool DiagnoseErrors,
                                      SanitizerMaskCutoffs &Cutoffs);
 
-/// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
-/// invalid components. Returns a SanitizerMask.
-///
-/// If Cutoffs is null, it assumes -fsanitize=<sanitizer1>,<sanitizer2>,...
-/// and returns the result in the SanitizerMask.
-///
-/// Otherwise, it assumes -fsanitize=<sanitizer1>=<value1>,<sanitizer2>=<value2>,...
-/// and returns an EMPTY SanitizerMask; results are stored in the passed
-/// Cutoffs.
-static SanitizerMask parseArgValuesOrCutoffs(const Driver &D,
-                                             const llvm::opt::Arg *A,
-                                             bool DiagnoseErrors,
-                                             SanitizerMaskCutoffs *Cutoffs);
-
 /// Parse -f(no-)?sanitize-coverage= flag values, diagnosing any invalid
 /// components. Returns OR of members of \c CoverageFeature enumeration.
 static int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A,
@@ -281,7 +267,7 @@ static SanitizerMask
 parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
                   bool DiagnoseErrors, SanitizerMask Default,
                   SanitizerMask AlwaysIn, SanitizerMask AlwaysOut, int OptInID,
-                  int OptOutID, SanitizerMaskCutoffs *Cutoffs) {
+                  int OptOutID) {
   assert(!(AlwaysIn & AlwaysOut) &&
          "parseSanitizeArgs called with contradictory in/out requirements");
 
@@ -292,8 +278,7 @@ parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
   SanitizerMask DiagnosedAlwaysOutViolations;
   for (const auto *Arg : Args) {
     if (Arg->getOption().matches(OptInID)) {
-      SanitizerMask Add =
-          parseArgValuesOrCutoffs(D, Arg, DiagnoseErrors, Cutoffs);
+      SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors);
       // Report error if user explicitly tries to opt-in to an always-out
       // sanitizer.
       if (SanitizerMask KindsToDiagnose =
@@ -309,8 +294,7 @@ parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
       Output |= expandSanitizerGroups(Add);
       Arg->claim();
     } else if (Arg->getOption().matches(OptOutID)) {
-      SanitizerMask Remove =
-          parseArgValuesOrCutoffs(D, Arg, DiagnoseErrors, Cutoffs);
+      SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors);
       // Report error if user explicitly tries to opt-out of an always-in
       // sanitizer.
       if (SanitizerMask KindsToDiagnose =
@@ -343,15 +327,16 @@ static SanitizerMask parseSanitizeTrapArgs(const Driver &D,
   // (not even in recover mode) in order to avoid the need for a ubsan runtime.
   return parseSanitizeArgs(D, Args, DiagnoseErrors, TrappingDefault, AlwaysTrap,
                            NeverTrap, options::OPT_fsanitize_trap_EQ,
-                           options::OPT_fno_sanitize_trap_EQ, nullptr);
+                           options::OPT_fno_sanitize_trap_EQ);
 }
 
-static SanitizerMask parseNoSanitizeHotArgs(const Driver &D,
-                                            const llvm::opt::ArgList &Args,
-                                            bool DiagnoseErrors,
-                                            SanitizerMaskCutoffs *Cutoffs) {
-  return parseSanitizeArgs(D, Args, DiagnoseErrors, {}, {}, {},
-                           options::OPT_fno_sanitize_top_hot_EQ, -1, Cutoffs);
+static void parseNoSanitizeHotArgs(const Driver &D,
+                                   const llvm::opt::ArgList &Args,
+                                   bool DiagnoseErrors,
+                                   SanitizerMaskCutoffs *Cutoffs) {
+  for (const auto *Arg : Args)
+    if (Arg->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ))
+      parseArgCutoffs(D, Arg, DiagnoseErrors, *Cutoffs);
 }
 
 bool SanitizerArgs::needsFuzzerInterceptors() const {
@@ -729,7 +714,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   SanitizerMask RecoverableKinds = parseSanitizeArgs(
       D, Args, DiagnoseErrors, RecoverableByDefault, AlwaysRecoverable,
       Unrecoverable, options::OPT_fsanitize_recover_EQ,
-      options::OPT_fno_sanitize_recover_EQ, nullptr);
+      options::OPT_fno_sanitize_recover_EQ);
   RecoverableKinds |= AlwaysRecoverable;
   RecoverableKinds &= ~Unrecoverable;
   RecoverableKinds &= Kinds;
@@ -741,7 +726,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   SanitizerMask MergeKinds =
       parseSanitizeArgs(D, Args, DiagnoseErrors, MergeDefault, {}, {},
                         options::OPT_fsanitize_merge_handlers_EQ,
-                        options::OPT_fno_sanitize_merge_handlers_EQ, nullptr);
+                        options::OPT_fno_sanitize_merge_handlers_EQ);
   MergeKinds &= Kinds;
 
   // Parse -fno-sanitize-top-hot flags

>From 57d6c5946c507b461ae4307c20aa81f0d9463fac Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 17:59:21 +0000
Subject: [PATCH 23/25] Change parseArgCutoffs to void return

---
 clang/include/clang/Basic/Sanitizers.h |  4 ++--
 clang/lib/Driver/SanitizerArgs.cpp     | 28 ++++++++------------------
 2 files changed, 10 insertions(+), 22 deletions(-)

diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index d92c5551616753..718d19b76f4dfe 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -190,10 +190,10 @@ SanitizerMask parseSanitizerValue(StringRef Value, bool AllowGroups);
 
 /// Parse a single weighted value (e.g., 'undefined=0.05') from a -fsanitize= or
 /// -fno-sanitize= value list.
-/// The relevant weight(s) are updated in the passed array.
+/// The relevant weight(s) are updated in the passed Cutoffs parameter.
 /// Individual Cutoffs are never reset to zero unless explicitly set
 /// (e.g., 'null=0.0').
-/// Returns \c False if \p Value is not known.
+/// Returns \c False if \p Value is not known or the weight is not valid.
 bool parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
                                  SanitizerMaskCutoffs &Cutoffs);
 
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index f0d6cb029c3916..9e47ce2bbaca35 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -114,11 +114,10 @@ static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
                                     bool DiagnoseErrors);
 
 /// Parse a -fsanitize=<sanitizer1>=<value1>... or -fno-sanitize= argument's
-/// values, diagnosing any invalid components. Returns an EMPTY SanitizerMask.
+/// values, diagnosing any invalid components.
 /// Cutoffs are stored in the passed parameter.
-static SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
-                                     bool DiagnoseErrors,
-                                     SanitizerMaskCutoffs &Cutoffs);
+static void parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
+                            bool DiagnoseErrors, SanitizerMaskCutoffs &Cutoffs);
 
 /// Parse -f(no-)?sanitize-coverage= flag values, diagnosing any invalid
 /// components. Returns OR of members of \c CoverageFeature enumeration.
@@ -1537,31 +1536,20 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
   return Kinds;
 }
 
-SanitizerMask parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
-                              bool DiagnoseErrors,
-                              SanitizerMaskCutoffs &Cutoffs) {
+void parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
+                     bool DiagnoseErrors, SanitizerMaskCutoffs &Cutoffs) {
   assert(A->getOption().matches(options::OPT_fno_sanitize_top_hot_EQ) &&
          "Invalid argument in parseArgCutoffs!");
   for (int i = 0, n = A->getNumValues(); i != n; ++i) {
     const char *Value = A->getValue(i);
 
     // We don't check the value of Cutoffs[i]: it's legal to specify
-    // -fsanitize-blah=value=0.0.
-    if (!parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs))
+    // a cutoff of 0.
+    if (!parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs) &&
+        DiagnoseErrors)
       D.Diag(clang::diag::err_drv_unsupported_option_argument)
           << A->getSpelling() << Value;
   }
-
-  return {};
-}
-
-SanitizerMask parseArgValuesOrCutoffs(const Driver &D, const llvm::opt::Arg *A,
-                                      bool DiagnoseErrors,
-                                      SanitizerMaskCutoffs *Cutoffs) {
-  if (Cutoffs)
-    return parseArgCutoffs(D, A, DiagnoseErrors, *Cutoffs);
-  else
-    return parseArgValues(D, A, DiagnoseErrors);
 }
 
 static int parseOverflowPatternExclusionValues(const Driver &D,

>From 56ba349e7f085fca3bfd531ea219b1d9e8ef177c Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 22:28:24 +0000
Subject: [PATCH 24/25] Convert SanitizerMaskCutoffs to struct

---
 clang/include/clang/Basic/Sanitizers.h     | 8 +++++++-
 clang/include/clang/Driver/SanitizerArgs.h | 2 +-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index 718d19b76f4dfe..67c18e4c539a8d 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -154,7 +154,13 @@ struct SanitizerKind {
 #include "clang/Basic/Sanitizers.def"
 }; // SanitizerKind
 
-using SanitizerMaskCutoffs = std::array<float, SanitizerKind::SO_Count>;
+struct SanitizerMaskCutoffs {
+  std::array<float, SanitizerKind::SO_Count> data = {0};
+
+  float &operator[](int index) { return data[index]; }
+
+  const float &operator[](int index) const { return data[index]; }
+}; // SanitizerMaskCutoffs;
 
 struct SanitizerSet {
   /// Check if a certain (single) sanitizer is enabled.
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 098e5a1682dbc9..6e46922800a5f1 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -26,7 +26,7 @@ class SanitizerArgs {
   SanitizerSet RecoverableSanitizers;
   SanitizerSet TrapSanitizers;
   SanitizerSet MergeHandlers;
-  SanitizerMaskCutoffs TopHotCutoffs = {0};
+  SanitizerMaskCutoffs TopHotCutoffs;
 
   std::vector<std::string> UserIgnorelistFiles;
   std::vector<std::string> SystemIgnorelistFiles;

>From 2298f93af8ea35021b02091c7a2365dda31ee38c Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 7 Jan 2025 22:31:23 +0000
Subject: [PATCH 25/25] Omit redundant initialization

---
 clang/include/clang/Basic/CodeGenOptions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 856dc10be92119..581182a4d00809 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -387,7 +387,7 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// Set of thresholds: the top hottest code responsible for the given
   /// fraction of PGO counters will be excluded from sanitization
   /// (0.0 [default] = skip none, 1.0 = skip all).
-  SanitizerMaskCutoffs NoSanitizeTopHotCutoffs = {0};
+  SanitizerMaskCutoffs NoSanitizeTopHotCutoffs;
 
   /// List of backend command-line options for -fembed-bitcode.
   std::vector<uint8_t> CmdArgs;



More information about the cfe-commits mailing list