Skip to content

[sanitizer] Parse weighted sanitizer args and -fsanitize-skip-hot-cutoff #121619

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 52 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
ca1fabc
[sanitizer] Parse weighted sanitizer args and -fno-sanitize-top-hot
thurstond Jan 4, 2025
7701659
clang-format
thurstond Jan 4, 2025
ade9e63
Update flag description
thurstond Jan 6, 2025
6e20cf8
Plumb values through CodeGen
thurstond Jan 6, 2025
cc3a2ac
clang-format
thurstond Jan 6, 2025
9a2a0e8
using SanitizerMaskWeights = std::array<float, SanitizerKind::SO_Count>
thurstond Jan 6, 2025
bfa57c6
clang-format
thurstond Jan 6, 2025
346adc1
Rename Weights to Cutoffs
thurstond Jan 6, 2025
66dbc49
Limit changes to Driver
thurstond Jan 6, 2025
6f5e714
Reorder to make comment more logical
thurstond Jan 7, 2025
fc623f2
Refactor into parseArgCutoffs; update comment
thurstond Jan 7, 2025
00d135e
Remove unnecessary suppression
thurstond Jan 7, 2025
4d97bed
clang-format
thurstond Jan 7, 2025
1f528c5
Change Cutoffs* to Cutoffs&
thurstond Jan 7, 2025
071e10e
Remove SanitizerMask TopHot
thurstond Jan 7, 2025
c970179
Rely on RVO
thurstond Jan 7, 2025
b2bb1d6
Fix uninitialized memory
thurstond Jan 7, 2025
369e6c8
clang-format
thurstond Jan 7, 2025
c6d4e73
Allow passing -fno-sanitize-top-hot=undefined=0.0. Add more tests.
thurstond Jan 7, 2025
85b898c
Check cutoffs in parsing. Update tests.
thurstond Jan 7, 2025
6cd43bb
Change cutoffs parser to return an empty SanitizerMask (Cutoffs becom…
thurstond Jan 7, 2025
c4b6a68
Simplify parseNoSanitizeHotArgs by not relying on parseSanitizeArgs
thurstond Jan 7, 2025
57d6c59
Change parseArgCutoffs to void return
thurstond Jan 7, 2025
56ba349
Convert SanitizerMaskCutoffs to struct
thurstond Jan 7, 2025
2298f93
Omit redundant initialization
thurstond Jan 7, 2025
a5ff144
Remove unnecessary braces
thurstond Jan 7, 2025
46e90e5
Rename 'data' to 'cutoffs'
thurstond Jan 7, 2025
58550e9
Improve comment
vitalybuka Jan 7, 2025
b2f0e5d
Make SanitizerMaskCutoffs class
vitalybuka Jan 7, 2025
be0f4e8
Remove unrelated change
vitalybuka Jan 7, 2025
a21c77b
Simplify parseSanitizerWeightedValue
vitalybuka Jan 7, 2025
27fe651
Fix clamp call
vitalybuka Jan 7, 2025
45a9fa5
Whitespace
thurstond Jan 7, 2025
5ab2a2b
Fix case in comment
vitalybuka Jan 7, 2025
a9c8d58
Hide clear logic into SanitizerMaskCutoffs
vitalybuka Jan 8, 2025
b668bcb
use string::empty
vitalybuka Jan 8, 2025
9321def
Return SanitizerMaskCutoffs from parseNoSanitizeHotArgs by value
vitalybuka Jan 8, 2025
d39638d
Extract ::set
vitalybuka Jan 8, 2025
99cfdb2
return float by value
vitalybuka Jan 8, 2025
58c752b
Updates
vitalybuka Jan 8, 2025
f27eb85
Include
vitalybuka Jan 8, 2025
1aed608
Fix clamp for double
vitalybuka Jan 8, 2025
b82e48d
Format
vitalybuka Jan 8, 2025
a8e79ce
Claim matched arguments
thurstond Jan 8, 2025
d3fa04d
Change test to use -Werror and not check for error messages, per Fang…
thurstond Jan 8, 2025
55f42e7
Format
vitalybuka Jan 8, 2025
d8a3696
This reverts part of commit b82e48d83663afead2b5b0ed4c5f2fb2b32683ad.
vitalybuka Jan 8, 2025
17ecca4
rtrim('0')
vitalybuka Jan 8, 2025
b43d513
clang-format
vitalybuka Jan 8, 2025
caa4964
Rename -fno-sanitize-top-hot to -fsanitize-skip-hot-cutoff
thurstond Jan 9, 2025
9837912
clang-format
thurstond Jan 9, 2025
10e0a55
++clang
thurstond Jan 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,11 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// the expense of debuggability).
SanitizerSet SanitizeMergeHandlers;

/// Set of thresholds in a range [0.0, 1.0]: the top hottest code responsible
/// for the given fraction of PGO counters will be excluded from sanitization
/// (0.0 [default] to skip none, 1.0 to skip all).
SanitizerMaskCutoffs SanitizeSkipHotCutoffs;

/// List of backend command-line options for -fembed-bitcode.
std::vector<uint8_t> CmdArgs;

Expand Down
24 changes: 24 additions & 0 deletions clang/include/clang/Basic/Sanitizers.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ struct SanitizerKind {
#include "clang/Basic/Sanitizers.def"
}; // SanitizerKind

class SanitizerMaskCutoffs {
std::vector<double> Cutoffs;

public:
std::optional<double> operator[](unsigned Kind) const;

void set(SanitizerMask K, double V);
void clear(SanitizerMask K = SanitizerKind::All);
};

struct SanitizerSet {
/// Check if a certain (single) sanitizer is enabled.
bool has(SanitizerMask K) const {
Expand Down Expand Up @@ -186,10 +196,24 @@ 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.
/// 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 or the weight is not valid.
bool parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
SanitizerMaskCutoffs &Cutoffs);

/// Serialize a SanitizerSet into values for -fsanitize= or -fno-sanitize=.
void serializeSanitizerSet(SanitizerSet Set,
SmallVectorImpl<StringRef> &Values);

/// Serialize a SanitizerMaskCutoffs into values for -fsanitize= or
/// -fno-sanitize=.
void serializeSanitizerMaskCutoffs(const SanitizerMaskCutoffs &Cutoffs,
SmallVectorImpl<std::string> &Values);

/// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers
/// this group enables.
SanitizerMask expandSanitizerGroups(SanitizerMask Kinds);
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2649,6 +2649,14 @@ 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 fsanitize_skip_hot_cutoff_EQ
: CommaJoined<["-"], "fsanitize-skip-hot-cutoff=">,
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>,...">;

} // end -f[no-]sanitize* flags

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Driver/SanitizerArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class SanitizerArgs {
SanitizerSet RecoverableSanitizers;
SanitizerSet TrapSanitizers;
SanitizerSet MergeHandlers;
SanitizerMaskCutoffs SkipHotCutoffs;

std::vector<std::string> UserIgnorelistFiles;
std::vector<std::string> SystemIgnorelistFiles;
Expand Down
60 changes: 60 additions & 0 deletions clang/lib/Basic/Sanitizers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,35 @@
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <optional>

using namespace clang;

static const double SanitizerMaskCutoffsEps = 0.000000001f;

void SanitizerMaskCutoffs::set(SanitizerMask K, double V) {
if (V < SanitizerMaskCutoffsEps && Cutoffs.empty())
return;
for (unsigned int i = 0; i < SanitizerKind::SO_Count; ++i)
if (K & SanitizerMask::bitPosToMask(i)) {
Cutoffs.resize(SanitizerKind::SO_Count);
Cutoffs[i] = V;
}
}

std::optional<double> SanitizerMaskCutoffs::operator[](unsigned Kind) const {
if (Cutoffs.empty() || Cutoffs[Kind] < SanitizerMaskCutoffsEps)
return std::nullopt;

return Cutoffs[Kind];
}

void SanitizerMaskCutoffs::clear(SanitizerMask K) { set(K, 0); }

// Once LLVM switches to C++17, the constexpr variables can be inline and we
// won't need this.
#define SANITIZER(NAME, ID) constexpr SanitizerMask SanitizerKind::ID;
Expand All @@ -36,6 +61,29 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
return ParsedKind;
}

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) \
.StartsWith(NAME "=", \
AllowGroups ? SanitizerKind::ID##Group : SanitizerMask())
#include "clang/Basic/Sanitizers.def"
.Default(SanitizerMask());

if (!ParsedKind)
return false;
auto [N, W] = Value.split('=');
double A;
if (W.getAsDouble(A))
return false;
A = std::clamp(A, 0.0, 1.0);
// AllowGroups is already taken into account for ParsedKind,
// hence we unconditionally expandSanitizerGroups.
Cutoffs.set(expandSanitizerGroups(ParsedKind), A);
return true;
}

void clang::serializeSanitizerSet(SanitizerSet Set,
SmallVectorImpl<StringRef> &Values) {
#define SANITIZER(NAME, ID) \
Expand All @@ -44,6 +92,18 @@ void clang::serializeSanitizerSet(SanitizerSet Set,
#include "clang/Basic/Sanitizers.def"
}

void clang::serializeSanitizerMaskCutoffs(
const SanitizerMaskCutoffs &Cutoffs, SmallVectorImpl<std::string> &Values) {
#define SANITIZER(NAME, ID) \
if (auto C = Cutoffs[SanitizerKind::SO_##ID]) { \
std::string Str; \
llvm::raw_string_ostream OS(Str); \
OS << NAME "=" << llvm::format("%.8f", *C); \
Values.emplace_back(StringRef(Str).rtrim('0')); \
}
#include "clang/Basic/Sanitizers.def"
}

SanitizerMask clang::expandSanitizerGroups(SanitizerMask Kinds) {
#define SANITIZER(NAME, ID)
#define SANITIZER_GROUP(NAME, ID, ALIAS) \
Expand Down
53 changes: 53 additions & 0 deletions clang/lib/Driver/SanitizerArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "clang/Driver/Driver.h"
#include "clang/Driver/Options.h"
#include "clang/Driver/ToolChain.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Path.h"
Expand Down Expand Up @@ -113,6 +114,12 @@ enum BinaryMetadataFeature {
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.
/// Cutoffs are stored in the passed parameter.
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.
static int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A,
Expand Down Expand Up @@ -323,6 +330,19 @@ static SanitizerMask parseSanitizeTrapArgs(const Driver &D,
options::OPT_fno_sanitize_trap_EQ);
}

static SanitizerMaskCutoffs
parseSanitizeSkipHotCutoffArgs(const Driver &D, const llvm::opt::ArgList &Args,
bool DiagnoseErrors) {
SanitizerMaskCutoffs Cutoffs;
for (const auto *Arg : Args)
if (Arg->getOption().matches(options::OPT_fsanitize_skip_hot_cutoff_EQ)) {
Arg->claim();
parseArgCutoffs(D, Arg, DiagnoseErrors, Cutoffs);
}

return Cutoffs;
}

bool SanitizerArgs::needsFuzzerInterceptors() const {
return needsFuzzer() && !needsAsanRt() && !needsTsanRt() && !needsMsanRt();
}
Expand Down Expand Up @@ -713,6 +733,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
options::OPT_fno_sanitize_merge_handlers_EQ);
MergeKinds &= Kinds;

// Parse -fno-sanitize-top-hot flags
SkipHotCutoffs = parseSanitizeSkipHotCutoffArgs(D, Args, DiagnoseErrors);

// Setup ignorelist files.
// Add default ignorelist from resource directory for activated sanitizers,
// and validate special case lists format.
Expand Down Expand Up @@ -1132,6 +1155,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
"Overlap between recoverable and trapping sanitizers");

MergeHandlers.Mask |= MergeKinds;

// Zero out SkipHotCutoffs for unused sanitizers
SkipHotCutoffs.clear(~Sanitizers.Mask);
}

static std::string toString(const clang::SanitizerSet &Sanitizers) {
Expand All @@ -1146,6 +1172,12 @@ static std::string toString(const clang::SanitizerSet &Sanitizers) {
return Res;
}

static std::string toString(const clang::SanitizerMaskCutoffs &Cutoffs) {
llvm::SmallVector<std::string, 4> Res;
serializeSanitizerMaskCutoffs(Cutoffs, Res);
return llvm::join(Res, ",");
}

static void addSpecialCaseListOpt(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
const char *SCLOptFlag,
Expand Down Expand Up @@ -1297,6 +1329,11 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
CmdArgs.push_back(
Args.MakeArgString("-fsanitize-merge=" + toString(MergeHandlers)));

std::string SkipHotCutoffsStr = toString(SkipHotCutoffs);
if (!SkipHotCutoffsStr.empty())
CmdArgs.push_back(
Args.MakeArgString("-fsanitize-skip-hot-cutoff=" + SkipHotCutoffsStr));

addSpecialCaseListOpt(Args, CmdArgs,
"-fsanitize-ignorelist=", UserIgnorelistFiles);
addSpecialCaseListOpt(Args, CmdArgs,
Expand Down Expand Up @@ -1494,6 +1531,22 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
return Kinds;
}

void parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
bool DiagnoseErrors, SanitizerMaskCutoffs &Cutoffs) {
assert(A->getOption().matches(options::OPT_fsanitize_skip_hot_cutoff_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
// a cutoff of 0.
if (!parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs) &&
DiagnoseErrors)
D.Diag(clang::diag::err_drv_unsupported_option_argument)
<< A->getSpelling() << Value;
}
}

static int parseOverflowPatternExclusionValues(const Driver &D,
const llvm::opt::Arg *A,
bool DiagnoseErrors) {
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,18 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
return Values;
}

static SanitizerMaskCutoffs
parseSanitizerWeightedKinds(StringRef FlagName,
const std::vector<std::string> &Sanitizers,
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,
ArgList &Args, DiagnosticsEngine &D,
XRayInstrSet &S) {
Expand Down Expand Up @@ -1796,6 +1808,11 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
serializeSanitizerKinds(Opts.SanitizeMergeHandlers))
GenerateArg(Consumer, OPT_fsanitize_merge_handlers_EQ, Sanitizer);

SmallVector<std::string, 4> Values;
serializeSanitizerMaskCutoffs(Opts.SanitizeSkipHotCutoffs, Values);
for (std::string Sanitizer : Values)
GenerateArg(Consumer, OPT_fsanitize_skip_hot_cutoff_EQ, Sanitizer);

if (!Opts.EmitVersionIdentMetadata)
GenerateArg(Consumer, OPT_Qn);

Expand Down Expand Up @@ -2277,6 +2294,11 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
Args.getAllArgValues(OPT_fsanitize_merge_handlers_EQ),
Diags, Opts.SanitizeMergeHandlers);

// Parse -fsanitize-skip-hot-cutoff= arguments.
Opts.SanitizeSkipHotCutoffs = parseSanitizerWeightedKinds(
"-fsanitize-skip-hot-cutoff=",
Args.getAllArgValues(OPT_fsanitize_skip_hot_cutoff_EQ), Diags);

Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);

if (!LangOpts->CUDAIsDevice)
Expand Down
53 changes: 53 additions & 0 deletions clang/test/Driver/fsanitize.c
Original file line number Diff line number Diff line change
Expand Up @@ -1154,3 +1154,56 @@

// 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'


// * Test -fsanitize-skip-hot-cutoff *

// -fsanitize-skip-hot-cutoff=undefined=0.5
// RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-skip-hot-cutoff=undefined=0.5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF1
// CHECK-SKIP-HOT-CUTOFF1: "-fsanitize-skip-hot-cutoff={{((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}"}}

// No-op: no sanitizers are specified
// RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff=undefined=0.5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF2
// CHECK-SKIP-HOT-CUTOFF2-NOT: "-fsanitize"
// CHECK-SKIP-HOT-CUTOFF2-NOT: "-fsanitize-skip-hot-cutoff"

// Enable undefined, then cancel out integer using a cutoff of 0.0
// RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-skip-hot-cutoff=undefined=0.5,integer=0.0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF3
// CHECK-SKIP-HOT-CUTOFF3: "-fsanitize-skip-hot-cutoff={{((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 -Werror --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-skip-hot-cutoff=undefined=0.5,integer=0.0,signed-integer-overflow=0.7 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF4
// CHECK-SKIP-HOT-CUTOFF4: "-fsanitize-skip-hot-cutoff={{((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.[57]0*,?){16}"}}

// Check that -fsanitize-skip-hot-cutoff=undefined=0.4 does not widen the set of -fsanitize=integer checks.
// RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize=integer -fsanitize-skip-hot-cutoff=undefined=0.4 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF5
// CHECK-SKIP-HOT-CUTOFF5: "-fsanitize-skip-hot-cutoff={{((integer-divide-by-zero|shift-base|shift-exponent|signed-integer-overflow)=0.40*,?){4}"}}

// 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 -Werror --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-skip-hot-cutoff=undefined=0.0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF6
// CHECK-SKIP-HOT-CUTOFF6: "-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-SKIP-HOT-CUTOFF6-NOT: "-fsanitize-skip-hot-cutoff"

// Invalid: bad sanitizer
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff=pot=0.0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF7
// CHECK-SKIP-HOT-CUTOFF7: unsupported argument 'pot=0.0' to option '-fsanitize-skip-hot-cutoff='

// Invalid: bad cutoff
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff=undefined=xyzzy %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF8
// CHECK-SKIP-HOT-CUTOFF8: unsupported argument 'undefined=xyzzy' to option '-fsanitize-skip-hot-cutoff='

// Invalid: -fno-sanitize-top without parameters
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF9
// CHECK-SKIP-HOT-CUTOFF9: unknown argument: '-fsanitize-skip-hot-cutoff'

// Invalid: -fno-sanitize-top=undefined without cutoff
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF10
// CHECK-SKIP-HOT-CUTOFF10: unsupported argument 'undefined' to option '-fsanitize-skip-hot-cutoff='

// Invalid: -fno-sanitize-top=undefined= without cutoff
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff=undefined= %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF11
// CHECK-SKIP-HOT-CUTOFF11: unsupported argument 'undefined=' to option '-fsanitize-skip-hot-cutoff='

// No-op: -fno-sanitize-top= without parameters is unusual but valid
// RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff= %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF12
// CHECK-SKIP-HOT-CUTOFF12-NOT: "-fsanitize-skip-hot-cutoff"
Loading