Skip to content

Commit 76fac9c

Browse files
[sanitizer] Parse weighted sanitizer args and -fsanitize-skip-hot-cutoff (#121619)
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 a new flag, `-fsanitize-skip-hot-cutoff`. `-fsanitize-skip-hot-cutoff` currently has no effect; future work will use it to generalize ubsan-guard-checks (originally introduced in 5f9ed2f). --------- Co-authored-by: Vitaly Buka <[email protected]>
1 parent 186bd8e commit 76fac9c

File tree

8 files changed

+226
-0
lines changed

8 files changed

+226
-0
lines changed

clang/include/clang/Basic/CodeGenOptions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,11 @@ class CodeGenOptions : public CodeGenOptionsBase {
384384
/// the expense of debuggability).
385385
SanitizerSet SanitizeMergeHandlers;
386386

387+
/// Set of thresholds in a range [0.0, 1.0]: the top hottest code responsible
388+
/// for the given fraction of PGO counters will be excluded from sanitization
389+
/// (0.0 [default] to skip none, 1.0 to skip all).
390+
SanitizerMaskCutoffs SanitizeSkipHotCutoffs;
391+
387392
/// List of backend command-line options for -fembed-bitcode.
388393
std::vector<uint8_t> CmdArgs;
389394

clang/include/clang/Basic/Sanitizers.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,16 @@ struct SanitizerKind {
154154
#include "clang/Basic/Sanitizers.def"
155155
}; // SanitizerKind
156156

157+
class SanitizerMaskCutoffs {
158+
std::vector<double> Cutoffs;
159+
160+
public:
161+
std::optional<double> operator[](unsigned Kind) const;
162+
163+
void set(SanitizerMask K, double V);
164+
void clear(SanitizerMask K = SanitizerKind::All);
165+
};
166+
157167
struct SanitizerSet {
158168
/// Check if a certain (single) sanitizer is enabled.
159169
bool has(SanitizerMask K) const {
@@ -186,10 +196,24 @@ struct SanitizerSet {
186196
/// Returns a non-zero SanitizerMask, or \c 0 if \p Value is not known.
187197
SanitizerMask parseSanitizerValue(StringRef Value, bool AllowGroups);
188198

199+
/// Parse a single weighted value (e.g., 'undefined=0.05') from a -fsanitize= or
200+
/// -fno-sanitize= value list.
201+
/// The relevant weight(s) are updated in the passed Cutoffs parameter.
202+
/// Individual Cutoffs are never reset to zero unless explicitly set
203+
/// (e.g., 'null=0.0').
204+
/// Returns \c false if \p Value is not known or the weight is not valid.
205+
bool parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
206+
SanitizerMaskCutoffs &Cutoffs);
207+
189208
/// Serialize a SanitizerSet into values for -fsanitize= or -fno-sanitize=.
190209
void serializeSanitizerSet(SanitizerSet Set,
191210
SmallVectorImpl<StringRef> &Values);
192211

212+
/// Serialize a SanitizerMaskCutoffs into values for -fsanitize= or
213+
/// -fno-sanitize=.
214+
void serializeSanitizerMaskCutoffs(const SanitizerMaskCutoffs &Cutoffs,
215+
SmallVectorImpl<std::string> &Values);
216+
193217
/// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers
194218
/// this group enables.
195219
SanitizerMask expandSanitizerGroups(SanitizerMask Kinds);

clang/include/clang/Driver/Options.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2652,6 +2652,14 @@ def fsanitize_undefined_strip_path_components_EQ : Joined<["-"], "fsanitize-unde
26522652
HelpText<"Strip (or keep only, if negative) a given number of path components "
26532653
"when emitting check metadata.">,
26542654
MarshallingInfoInt<CodeGenOpts<"EmitCheckPathComponentsToStrip">, "0", "int">;
2655+
def fsanitize_skip_hot_cutoff_EQ
2656+
: CommaJoined<["-"], "fsanitize-skip-hot-cutoff=">,
2657+
Group<f_clang_Group>,
2658+
HelpText<
2659+
"Exclude sanitization for the top hottest code responsible for "
2660+
"the given fraction of PGO counters "
2661+
"(0.0 [default] = skip none; 1.0 = skip all). "
2662+
"Argument format: <sanitizer1>=<value1>,<sanitizer2>=<value2>,...">;
26552663

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

clang/include/clang/Driver/SanitizerArgs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class SanitizerArgs {
2626
SanitizerSet RecoverableSanitizers;
2727
SanitizerSet TrapSanitizers;
2828
SanitizerSet MergeHandlers;
29+
SanitizerMaskCutoffs SkipHotCutoffs;
2930

3031
std::vector<std::string> UserIgnorelistFiles;
3132
std::vector<std::string> SystemIgnorelistFiles;

clang/lib/Basic/Sanitizers.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,35 @@
1414
#include "llvm/ADT/Hashing.h"
1515
#include "llvm/ADT/SmallVector.h"
1616
#include "llvm/ADT/StringSwitch.h"
17+
#include "llvm/Support/Format.h"
1718
#include "llvm/Support/MathExtras.h"
19+
#include "llvm/Support/raw_ostream.h"
20+
#include <algorithm>
21+
#include <optional>
1822

1923
using namespace clang;
2024

25+
static const double SanitizerMaskCutoffsEps = 0.000000001f;
26+
27+
void SanitizerMaskCutoffs::set(SanitizerMask K, double V) {
28+
if (V < SanitizerMaskCutoffsEps && Cutoffs.empty())
29+
return;
30+
for (unsigned int i = 0; i < SanitizerKind::SO_Count; ++i)
31+
if (K & SanitizerMask::bitPosToMask(i)) {
32+
Cutoffs.resize(SanitizerKind::SO_Count);
33+
Cutoffs[i] = V;
34+
}
35+
}
36+
37+
std::optional<double> SanitizerMaskCutoffs::operator[](unsigned Kind) const {
38+
if (Cutoffs.empty() || Cutoffs[Kind] < SanitizerMaskCutoffsEps)
39+
return std::nullopt;
40+
41+
return Cutoffs[Kind];
42+
}
43+
44+
void SanitizerMaskCutoffs::clear(SanitizerMask K) { set(K, 0); }
45+
2146
// Once LLVM switches to C++17, the constexpr variables can be inline and we
2247
// won't need this.
2348
#define SANITIZER(NAME, ID) constexpr SanitizerMask SanitizerKind::ID;
@@ -36,6 +61,29 @@ SanitizerMask clang::parseSanitizerValue(StringRef Value, bool AllowGroups) {
3661
return ParsedKind;
3762
}
3863

64+
bool clang::parseSanitizerWeightedValue(StringRef Value, bool AllowGroups,
65+
SanitizerMaskCutoffs &Cutoffs) {
66+
SanitizerMask ParsedKind = llvm::StringSwitch<SanitizerMask>(Value)
67+
#define SANITIZER(NAME, ID) .StartsWith(NAME "=", SanitizerKind::ID)
68+
#define SANITIZER_GROUP(NAME, ID, ALIAS) \
69+
.StartsWith(NAME "=", \
70+
AllowGroups ? SanitizerKind::ID##Group : SanitizerMask())
71+
#include "clang/Basic/Sanitizers.def"
72+
.Default(SanitizerMask());
73+
74+
if (!ParsedKind)
75+
return false;
76+
auto [N, W] = Value.split('=');
77+
double A;
78+
if (W.getAsDouble(A))
79+
return false;
80+
A = std::clamp(A, 0.0, 1.0);
81+
// AllowGroups is already taken into account for ParsedKind,
82+
// hence we unconditionally expandSanitizerGroups.
83+
Cutoffs.set(expandSanitizerGroups(ParsedKind), A);
84+
return true;
85+
}
86+
3987
void clang::serializeSanitizerSet(SanitizerSet Set,
4088
SmallVectorImpl<StringRef> &Values) {
4189
#define SANITIZER(NAME, ID) \
@@ -44,6 +92,18 @@ void clang::serializeSanitizerSet(SanitizerSet Set,
4492
#include "clang/Basic/Sanitizers.def"
4593
}
4694

95+
void clang::serializeSanitizerMaskCutoffs(
96+
const SanitizerMaskCutoffs &Cutoffs, SmallVectorImpl<std::string> &Values) {
97+
#define SANITIZER(NAME, ID) \
98+
if (auto C = Cutoffs[SanitizerKind::SO_##ID]) { \
99+
std::string Str; \
100+
llvm::raw_string_ostream OS(Str); \
101+
OS << NAME "=" << llvm::format("%.8f", *C); \
102+
Values.emplace_back(StringRef(Str).rtrim('0')); \
103+
}
104+
#include "clang/Basic/Sanitizers.def"
105+
}
106+
47107
SanitizerMask clang::expandSanitizerGroups(SanitizerMask Kinds) {
48108
#define SANITIZER(NAME, ID)
49109
#define SANITIZER_GROUP(NAME, ID, ALIAS) \

clang/lib/Driver/SanitizerArgs.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "clang/Driver/Driver.h"
1111
#include "clang/Driver/Options.h"
1212
#include "clang/Driver/ToolChain.h"
13+
#include "llvm/ADT/SmallVector.h"
1314
#include "llvm/ADT/StringRef.h"
1415
#include "llvm/ADT/StringSwitch.h"
1516
#include "llvm/Support/Path.h"
@@ -113,6 +114,12 @@ enum BinaryMetadataFeature {
113114
static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
114115
bool DiagnoseErrors);
115116

117+
/// Parse a -fsanitize=<sanitizer1>=<value1>... or -fno-sanitize= argument's
118+
/// values, diagnosing any invalid components.
119+
/// Cutoffs are stored in the passed parameter.
120+
static void parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
121+
bool DiagnoseErrors, SanitizerMaskCutoffs &Cutoffs);
122+
116123
/// Parse -f(no-)?sanitize-coverage= flag values, diagnosing any invalid
117124
/// components. Returns OR of members of \c CoverageFeature enumeration.
118125
static int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A,
@@ -323,6 +330,19 @@ static SanitizerMask parseSanitizeTrapArgs(const Driver &D,
323330
options::OPT_fno_sanitize_trap_EQ);
324331
}
325332

333+
static SanitizerMaskCutoffs
334+
parseSanitizeSkipHotCutoffArgs(const Driver &D, const llvm::opt::ArgList &Args,
335+
bool DiagnoseErrors) {
336+
SanitizerMaskCutoffs Cutoffs;
337+
for (const auto *Arg : Args)
338+
if (Arg->getOption().matches(options::OPT_fsanitize_skip_hot_cutoff_EQ)) {
339+
Arg->claim();
340+
parseArgCutoffs(D, Arg, DiagnoseErrors, Cutoffs);
341+
}
342+
343+
return Cutoffs;
344+
}
345+
326346
bool SanitizerArgs::needsFuzzerInterceptors() const {
327347
return needsFuzzer() && !needsAsanRt() && !needsTsanRt() && !needsMsanRt();
328348
}
@@ -713,6 +733,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
713733
options::OPT_fno_sanitize_merge_handlers_EQ);
714734
MergeKinds &= Kinds;
715735

736+
// Parse -fno-sanitize-top-hot flags
737+
SkipHotCutoffs = parseSanitizeSkipHotCutoffArgs(D, Args, DiagnoseErrors);
738+
716739
// Setup ignorelist files.
717740
// Add default ignorelist from resource directory for activated sanitizers,
718741
// and validate special case lists format.
@@ -1132,6 +1155,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
11321155
"Overlap between recoverable and trapping sanitizers");
11331156

11341157
MergeHandlers.Mask |= MergeKinds;
1158+
1159+
// Zero out SkipHotCutoffs for unused sanitizers
1160+
SkipHotCutoffs.clear(~Sanitizers.Mask);
11351161
}
11361162

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

1175+
static std::string toString(const clang::SanitizerMaskCutoffs &Cutoffs) {
1176+
llvm::SmallVector<std::string, 4> Res;
1177+
serializeSanitizerMaskCutoffs(Cutoffs, Res);
1178+
return llvm::join(Res, ",");
1179+
}
1180+
11491181
static void addSpecialCaseListOpt(const llvm::opt::ArgList &Args,
11501182
llvm::opt::ArgStringList &CmdArgs,
11511183
const char *SCLOptFlag,
@@ -1297,6 +1329,11 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
12971329
CmdArgs.push_back(
12981330
Args.MakeArgString("-fsanitize-merge=" + toString(MergeHandlers)));
12991331

1332+
std::string SkipHotCutoffsStr = toString(SkipHotCutoffs);
1333+
if (!SkipHotCutoffsStr.empty())
1334+
CmdArgs.push_back(
1335+
Args.MakeArgString("-fsanitize-skip-hot-cutoff=" + SkipHotCutoffsStr));
1336+
13001337
addSpecialCaseListOpt(Args, CmdArgs,
13011338
"-fsanitize-ignorelist=", UserIgnorelistFiles);
13021339
addSpecialCaseListOpt(Args, CmdArgs,
@@ -1494,6 +1531,22 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
14941531
return Kinds;
14951532
}
14961533

1534+
void parseArgCutoffs(const Driver &D, const llvm::opt::Arg *A,
1535+
bool DiagnoseErrors, SanitizerMaskCutoffs &Cutoffs) {
1536+
assert(A->getOption().matches(options::OPT_fsanitize_skip_hot_cutoff_EQ) &&
1537+
"Invalid argument in parseArgCutoffs!");
1538+
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
1539+
const char *Value = A->getValue(i);
1540+
1541+
// We don't check the value of Cutoffs[i]: it's legal to specify
1542+
// a cutoff of 0.
1543+
if (!parseSanitizerWeightedValue(Value, /*AllowGroups=*/true, Cutoffs) &&
1544+
DiagnoseErrors)
1545+
D.Diag(clang::diag::err_drv_unsupported_option_argument)
1546+
<< A->getSpelling() << Value;
1547+
}
1548+
}
1549+
14971550
static int parseOverflowPatternExclusionValues(const Driver &D,
14981551
const llvm::opt::Arg *A,
14991552
bool DiagnoseErrors) {

clang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,6 +1453,18 @@ static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
14531453
return Values;
14541454
}
14551455

1456+
static SanitizerMaskCutoffs
1457+
parseSanitizerWeightedKinds(StringRef FlagName,
1458+
const std::vector<std::string> &Sanitizers,
1459+
DiagnosticsEngine &Diags) {
1460+
SanitizerMaskCutoffs Cutoffs;
1461+
for (const auto &Sanitizer : Sanitizers) {
1462+
if (!parseSanitizerWeightedValue(Sanitizer, /*AllowGroups=*/false, Cutoffs))
1463+
Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer;
1464+
}
1465+
return Cutoffs;
1466+
}
1467+
14561468
static void parseXRayInstrumentationBundle(StringRef FlagName, StringRef Bundle,
14571469
ArgList &Args, DiagnosticsEngine &D,
14581470
XRayInstrSet &S) {
@@ -1813,6 +1825,11 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
18131825
serializeSanitizerKinds(Opts.SanitizeMergeHandlers))
18141826
GenerateArg(Consumer, OPT_fsanitize_merge_handlers_EQ, Sanitizer);
18151827

1828+
SmallVector<std::string, 4> Values;
1829+
serializeSanitizerMaskCutoffs(Opts.SanitizeSkipHotCutoffs, Values);
1830+
for (std::string Sanitizer : Values)
1831+
GenerateArg(Consumer, OPT_fsanitize_skip_hot_cutoff_EQ, Sanitizer);
1832+
18161833
if (!Opts.EmitVersionIdentMetadata)
18171834
GenerateArg(Consumer, OPT_Qn);
18181835

@@ -2293,6 +2310,11 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
22932310
Args.getAllArgValues(OPT_fsanitize_merge_handlers_EQ),
22942311
Diags, Opts.SanitizeMergeHandlers);
22952312

2313+
// Parse -fsanitize-skip-hot-cutoff= arguments.
2314+
Opts.SanitizeSkipHotCutoffs = parseSanitizerWeightedKinds(
2315+
"-fsanitize-skip-hot-cutoff=",
2316+
Args.getAllArgValues(OPT_fsanitize_skip_hot_cutoff_EQ), Diags);
2317+
22962318
Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
22972319

22982320
if (!LangOpts->CUDAIsDevice)

clang/test/Driver/fsanitize.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,3 +1154,56 @@
11541154

11551155
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-UBSAN
11561156
// CHECK-REALTIME-UBSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=undefined'
1157+
1158+
1159+
// * Test -fsanitize-skip-hot-cutoff *
1160+
1161+
// -fsanitize-skip-hot-cutoff=undefined=0.5
1162+
// 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
1163+
// 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}"}}
1164+
1165+
// No-op: no sanitizers are specified
1166+
// 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
1167+
// CHECK-SKIP-HOT-CUTOFF2-NOT: "-fsanitize"
1168+
// CHECK-SKIP-HOT-CUTOFF2-NOT: "-fsanitize-skip-hot-cutoff"
1169+
1170+
// Enable undefined, then cancel out integer using a cutoff of 0.0
1171+
// 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
1172+
// 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}"}}
1173+
1174+
// Enable undefined, then cancel out integer using a cutoff of 0.0, then re-enable signed-integer-overflow
1175+
// 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
1176+
// 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}"}}
1177+
1178+
// Check that -fsanitize-skip-hot-cutoff=undefined=0.4 does not widen the set of -fsanitize=integer checks.
1179+
// 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
1180+
// CHECK-SKIP-HOT-CUTOFF5: "-fsanitize-skip-hot-cutoff={{((integer-divide-by-zero|shift-base|shift-exponent|signed-integer-overflow)=0.40*,?){4}"}}
1181+
1182+
// 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.
1183+
// 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
1184+
// 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}"}}
1185+
// CHECK-SKIP-HOT-CUTOFF6-NOT: "-fsanitize-skip-hot-cutoff"
1186+
1187+
// Invalid: bad sanitizer
1188+
// 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
1189+
// CHECK-SKIP-HOT-CUTOFF7: unsupported argument 'pot=0.0' to option '-fsanitize-skip-hot-cutoff='
1190+
1191+
// Invalid: bad cutoff
1192+
// 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
1193+
// CHECK-SKIP-HOT-CUTOFF8: unsupported argument 'undefined=xyzzy' to option '-fsanitize-skip-hot-cutoff='
1194+
1195+
// Invalid: -fno-sanitize-top without parameters
1196+
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF9
1197+
// CHECK-SKIP-HOT-CUTOFF9: unknown argument: '-fsanitize-skip-hot-cutoff'
1198+
1199+
// Invalid: -fno-sanitize-top=undefined without cutoff
1200+
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF10
1201+
// CHECK-SKIP-HOT-CUTOFF10: unsupported argument 'undefined' to option '-fsanitize-skip-hot-cutoff='
1202+
1203+
// Invalid: -fno-sanitize-top=undefined= without cutoff
1204+
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff=undefined= %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF11
1205+
// CHECK-SKIP-HOT-CUTOFF11: unsupported argument 'undefined=' to option '-fsanitize-skip-hot-cutoff='
1206+
1207+
// No-op: -fno-sanitize-top= without parameters is unusual but valid
1208+
// RUN: %clang -Werror --target=x86_64-linux-gnu -fsanitize-skip-hot-cutoff= %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SKIP-HOT-CUTOFF12
1209+
// CHECK-SKIP-HOT-CUTOFF12-NOT: "-fsanitize-skip-hot-cutoff"

0 commit comments

Comments
 (0)