[llvm] [Support] Accept -no-<name> to negate cl::opt<bool> (PR #224978)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 01:22:39 PDT 2026
https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/224978
>From 15aceb56828e410fe7898cf5dd386aa9849c8f47 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 20 Sep 2026 16:55:19 -0700
Subject: [PATCH 1/3] [Support] Accept -no-<name> to negate cl::opt<bool>
As a longstanding quirk, a boolean option is negated with -<name>=false
or -<name>=0. Most command line libraries spell negation as -no-<name>.
Accept -no-<name> by resolving at lookup time when no option named
no-<name> is registered. This lets tests and scripts move to the
spelling both parsers understand.
In the future, we shall ban `-<name>=false` and `-<name>=0` for boolean
options. See https://discourse.llvm.org/t/91877
LLM-aided
---
llvm/docs/CommandLine.md | 4 ++-
llvm/include/llvm/Support/CommandLine.h | 8 +++++
llvm/lib/Support/CommandLine.cpp | 12 ++++++-
llvm/unittests/Support/CommandLineTest.cpp | 37 ++++++++++++++++++++++
4 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/llvm/docs/CommandLine.md b/llvm/docs/CommandLine.md
index bf5123fdcfa10..4dce7cb5e4b24 100644
--- a/llvm/docs/CommandLine.md
+++ b/llvm/docs/CommandLine.md
@@ -221,13 +221,15 @@ literally into the content of the string variable... we obviously cannot do that
in the boolean case, however, so we must use a smarter parser. In the case of
the boolean parser, it allows no options (in which case it assigns the value of
true to the variable), or it allows the values "`true`" or "`false`" to be
-specified, allowing any of the following inputs:
+specified; a boolean option can also be negated with a `no-` prefix, allowing
+any of the following inputs:
```
compiler -f # No value, 'Force' == true
compiler -f=true # Value specified, 'Force' == true
compiler -f=TRUE # Value specified, 'Force' == true
compiler -f=FALSE # Value specified, 'Force' == false
+compiler -no-f # Negated, 'Force' == false
```
... you get the idea. The {ref}`bool parser <bool parser>` just turns the string values into
diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h
index d74958ba4f463..622eb597c2625 100644
--- a/llvm/include/llvm/Support/CommandLine.h
+++ b/llvm/include/llvm/Support/CommandLine.h
@@ -292,6 +292,9 @@ class LLVM_ABI Option {
return Value ? ((enum ValueExpected)Value) : getValueExpectedFlagDefault();
}
+ // Whether -no-<ArgStr> is accepted and sets the option to false.
+ virtual bool isNegatable() const { return false; }
+
inline enum OptionHidden getOptionHiddenFlag() const {
return (enum OptionHidden)HiddenFlag;
}
@@ -1481,6 +1484,11 @@ class opt
return Parser.getValueExpectedFlagDefault();
}
+ bool isNegatable() const override {
+ return std::is_same_v<DataType, bool> ||
+ std::is_same_v<DataType, boolOrDefault>;
+ }
+
void getExtraOptionNames(SmallVectorImpl<StringRef> &OptionNames) override {
return Parser.getExtraOptionNames(OptionNames);
}
diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index f363190bca1ce..cd9341e3e5706 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -561,7 +561,17 @@ Option *CommandLineParser::LookupOption(SubCommand &Sub, StringRef &Arg,
// If we have an equals sign, remember the value.
if (EqualPos == StringRef::npos) {
// Look up the option.
- return Sub.OptionsMap.lookup(Arg);
+ if (Option *O = Sub.OptionsMap.lookup(Arg))
+ return O;
+ // -no-<name> negates the boolean option <name>.
+ StringRef Name = Arg;
+ if (!Name.consume_front("no-"))
+ return nullptr;
+ Option *O = Sub.OptionsMap.lookup(Name);
+ if (!O || !O->isNegatable())
+ return nullptr;
+ Value = "false";
+ return O;
}
// If the argument before the = is a valid option name and the option allows
diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index 956d5b97c2703..c45ed339a93c1 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -2187,6 +2187,43 @@ TEST(CommandLineTest, ConsumeAfterTwoPositionals) {
EXPECT_TRUE(Errs.empty());
}
+TEST(CommandLineTest, BoolNegation) {
+ cl::ResetCommandLineParser();
+
+ StackOption<bool> OptFlag("flag", cl::init(true));
+ StackOption<cl::boolOrDefault> OptBod("bod");
+ StackOption<std::string> OptStr("str");
+ // A registered no-<name> takes precedence over negating <name>.
+ StackOption<bool> OptExplicit("explicit", cl::init(true));
+ StackOption<bool> OptNoExplicit("no-explicit");
+
+ const char *args1[] = {"prog", "-no-flag", "--no-bod", "-no-explicit"};
+ EXPECT_TRUE(
+ cl::ParseCommandLineOptions(4, args1, StringRef(), &llvm::nulls()));
+ EXPECT_FALSE(OptFlag);
+ EXPECT_EQ(cl::boolOrDefault::BOU_FALSE, OptBod);
+ EXPECT_TRUE(OptExplicit);
+ EXPECT_TRUE(OptNoExplicit);
+ cl::ResetAllOptionOccurrences();
+
+ // The last occurrence wins.
+ const char *args2[] = {"prog", "-no-flag", "-flag"};
+ EXPECT_TRUE(
+ cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));
+ EXPECT_TRUE(OptFlag);
+ cl::ResetAllOptionOccurrences();
+
+ // Only boolean options are negatable, and the negated form takes no value.
+ const char *args3[] = {"prog", "-no-str"};
+ EXPECT_FALSE(
+ cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls()));
+ cl::ResetAllOptionOccurrences();
+
+ const char *args4[] = {"prog", "-no-flag=false"};
+ EXPECT_FALSE(
+ cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls()));
+}
+
TEST(CommandLineTest, ConsumeOptionalString) {
cl::ResetCommandLineParser();
>From 7761ec6e06e33e9da2b63cb54155f451e68fea21 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 20 Sep 2026 17:45:19 -0700
Subject: [PATCH 2/3] update a few lit tests for demonstration
---
llvm/lib/CodeGen/StackSlotColoring.cpp | 7 +++----
llvm/lib/Target/X86/X86CallFrameOptimization.cpp | 9 ++++-----
llvm/test/Transforms/InstCombine/fputs-opt-size.ll | 2 +-
llvm/test/Transforms/SimplifyCFG/switch_undef.ll | 2 +-
4 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/CodeGen/StackSlotColoring.cpp b/llvm/lib/CodeGen/StackSlotColoring.cpp
index 4bc74814d6fc4..41131ac3680ee 100644
--- a/llvm/lib/CodeGen/StackSlotColoring.cpp
+++ b/llvm/lib/CodeGen/StackSlotColoring.cpp
@@ -50,9 +50,8 @@ using namespace llvm;
#define DEBUG_TYPE "stack-slot-coloring"
static cl::opt<bool>
-DisableSharing("no-stack-slot-sharing",
- cl::init(false), cl::Hidden,
- cl::desc("Suppress slot sharing during stack coloring"));
+ EnableSharing("stack-slot-sharing", cl::init(true), cl::Hidden,
+ cl::desc("Share stack slots during stack coloring"));
static cl::opt<int> DCELimit("ssc-dce-limit", cl::init(-1), cl::Hidden);
@@ -312,7 +311,7 @@ int StackSlotColoring::ColorSlot(LiveInterval *li) {
int FI = li->reg().stackSlotIndex();
uint8_t StackID = MFI->getStackID(FI);
- if (!DisableSharing) {
+ if (EnableSharing) {
// Check if it's possible to reuse any of the used colors.
Color = UsedColors[StackID].find_first();
diff --git a/llvm/lib/Target/X86/X86CallFrameOptimization.cpp b/llvm/lib/Target/X86/X86CallFrameOptimization.cpp
index 4966ec2fe8dc9..2ef586a76963b 100644
--- a/llvm/lib/Target/X86/X86CallFrameOptimization.cpp
+++ b/llvm/lib/Target/X86/X86CallFrameOptimization.cpp
@@ -52,10 +52,9 @@ using namespace llvm;
#define DEBUG_TYPE "x86-cf-opt"
-static cl::opt<bool>
- NoX86CFOpt("no-x86-call-frame-opt",
- cl::desc("Avoid optimizing x86 call frames for size"),
- cl::init(false), cl::Hidden);
+static cl::opt<bool> X86CFOpt("x86-call-frame-opt",
+ cl::desc("Optimize x86 call frames for size"),
+ cl::init(true), cl::Hidden);
namespace {
@@ -145,7 +144,7 @@ INITIALIZE_PASS(X86CallFrameOptimizationLegacy, DEBUG_TYPE,
// Also returns false in cases where it's potentially legal, but
// we don't even want to try.
bool X86CallFrameOptimizationImpl::isLegal(MachineFunction &MF) {
- if (NoX86CFOpt.getValue())
+ if (!X86CFOpt)
return false;
// We can't encode multiple DW_CFA_GNU_args_size or DW_CFA_def_cfa_offset
diff --git a/llvm/test/Transforms/InstCombine/fputs-opt-size.ll b/llvm/test/Transforms/InstCombine/fputs-opt-size.ll
index 2584320178c9a..92b7277837176 100644
--- a/llvm/test/Transforms/InstCombine/fputs-opt-size.ll
+++ b/llvm/test/Transforms/InstCombine/fputs-opt-size.ll
@@ -3,7 +3,7 @@
;
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
; RUN: opt < %s -passes='require<profile-summary>,function(instcombine)' -pgso -S | FileCheck %s -check-prefix=PGSO
-; RUN: opt < %s -passes=instcombine -pgso=false -S | FileCheck %s -check-prefix=NPGSO
+; RUN: opt < %s -passes=instcombine -no-pgso -S | FileCheck %s -check-prefix=NPGSO
%struct._IO_FILE = type { i32, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, i32, i32, i32, i16, i8, [1 x i8], ptr, i64, ptr, ptr, ptr, ptr, i32, i32, [40 x i8] }
%struct._IO_marker = type { ptr, ptr, i32 }
diff --git a/llvm/test/Transforms/SimplifyCFG/switch_undef.ll b/llvm/test/Transforms/SimplifyCFG/switch_undef.ll
index 4de5ea948ed27..0a64e51123886 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch_undef.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch_undef.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt %s -keep-loops=false -switch-to-lookup=true -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s
+; RUN: opt %s -no-keep-loops -switch-to-lookup=true -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s
; RUN: opt %s -passes='simplifycfg<no-keep-loops;switch-to-lookup>' -S | FileCheck %s
define void @f6() #0 {
>From fd838d5133947dbca128ccf8f31b1973bca3fc8b Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 21 Sep 2026 01:22:25 -0700
Subject: [PATCH 3/3] remove redundant noLeadingLines from split-file
---
llvm/test/tools/split-file/help.test | 2 +-
llvm/utils/split-file/split-file.cpp | 4 ----
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/llvm/test/tools/split-file/help.test b/llvm/test/tools/split-file/help.test
index 27c450aeac3a7..511a4343293d2 100644
--- a/llvm/test/tools/split-file/help.test
+++ b/llvm/test/tools/split-file/help.test
@@ -3,4 +3,4 @@ CHECK: OVERVIEW: Split input {{.*}}
CHECK: USAGE: split-file [options] filename directory
CHECK: Generic Options:
CHECK: split-file Options:
-CHECK: --no-leading-lines
+CHECK: --leading-lines
diff --git a/llvm/utils/split-file/split-file.cpp b/llvm/utils/split-file/split-file.cpp
index c7e9ff4b2b682..7ccfde4cff883 100644
--- a/llvm/utils/split-file/split-file.cpp
+++ b/llvm/utils/split-file/split-file.cpp
@@ -39,10 +39,6 @@ static cl::opt<bool> leadingLines("leading-lines",
cl::desc("Preserve line numbers"),
cl::cat(cat));
-static cl::opt<bool> noLeadingLines("no-leading-lines",
- cl::desc("Don't preserve line numbers (default)"),
- cl::cat(cat));
-
static StringRef toolName;
static int errorCount;
More information about the llvm-commits
mailing list