[Option] Add FlagOrEq and SeparateOrEq option kinds (#228319)
FlagOrEq accepts -x and -x=v; SeparateOrEq accepts -x=v and -x v. The
name is stored without '=', and RenderEqStyle renders -x=v. BoolField
and ValueField use them, so a library option takes one table row instead
of a Joined twin plus a Flag or a Separate alias.
CGDataOptions.cpp.o shrinks from 4,977 to 4,370 bytes and
InstCombineCLOptions.cpp.o from 4,180 to 3,553.
Aided by Opus 5.5
diff --git a/clang-tools-extra/clangd/CompileCommands.cpp b/clang-tools-extra/clangd/CompileCommands.cpp
index 2ba9446..eb9e1df 100644
--- a/clang-tools-extra/clangd/CompileCommands.cpp
+++ b/clang-tools-extra/clangd/CompileCommands.cpp
@@ -416,7 +416,10 @@
case Option::MultiArgClass:
return {1 + Opt.getNumArgs(), 0};
case Option::JoinedOrSeparateClass:
+ case Option::SeparateOrEqClass:
return {2, 1};
+ case Option::FlagOrEqClass:
+ return {1, 1};
case Option::RemainingArgsClass:
return {Rest, 0};
case Option::RemainingArgsJoinedClass:
diff --git a/llvm/include/llvm/Option/LibraryOptions.td b/llvm/include/llvm/Option/LibraryOptions.td
index 68a92e5..588f212 100644
--- a/llvm/include/llvm/Option/LibraryOptions.td
+++ b/llvm/include/llvm/Option/LibraryOptions.td
@@ -22,30 +22,24 @@
string MemberPrefix = prefix;
}
-// The member a row sets, the member's type and default value, and the value a
-// Flag assigns. Other kinds parse their argument.
+// The member an option sets, and the member's type and default value.
class Field<string name, string type, code default> {
string FieldName = name;
string FieldType = type;
code FieldDefault = default;
}
-class FieldValue<code value> { code FieldValue = value; }
// A bool member set by -<name> or -<name>=<true|false|1|0>.
multiclass BoolField<string name, code default, string help> {
- def NAME : Flag<["-", "--"], name>, Field<NAME, "bool", default>,
- FieldValue<"true">, HelpText<help>;
- def NAME#_EQ : Joined<["-", "--"], name#"=">, Field<NAME, "bool", default>;
+ def NAME : FlagOrEq<["-", "--"], name>, Field<NAME, "bool", default>,
+ HelpText<help>;
}
// A std::optional<bool> member, std::nullopt unless -<name> or
// -<name>=<true|false|1|0> is given.
multiclass OptionalBoolField<string name, string help> {
- def NAME : Flag<["-", "--"], name>,
- Field<NAME, "std::optional<bool>", "std::nullopt">, FieldValue<"true">,
- HelpText<help>;
- def NAME#_EQ : Joined<["-", "--"], name#"=">,
- Field<NAME, "std::optional<bool>", "std::nullopt">;
+ def NAME : FlagOrEq<["-", "--"], name>,
+ Field<NAME, "std::optional<bool>", "std::nullopt">, HelpText<help>;
}
// A member of the enumeration <type> set by -<name>=<value> or -<name> <value>,
@@ -53,10 +47,9 @@
// the C++ <enumerators>.
multiclass EnumField<string name, string type, code default, string values,
list<code> enumerators, string help> {
- def NAME#_EQ : Joined<["-", "--"], name#"=">, Field<NAME, type, default>,
+ def NAME : SeparateOrEq<["-", "--"], name>, Field<NAME, type, default>,
Values<values>, NormalizedValues<enumerators>,
MetaVarName<"<"#!subst(",", "|", values)#">">, HelpText<help>;
- def NAME : Separate<["-", "--"], name>, Alias<!cast<Option>(NAME#"_EQ")>;
}
// A std::optional<bool> member, std::nullopt unless -<name>=Enable or
@@ -68,7 +61,6 @@
// A member of the C++ type <type> set by -<name>=<value> or -<name> <value>.
multiclass ValueField<string name, string type, code default, string help> {
- def NAME#_EQ : Joined<["-", "--"], name#"=">, Field<NAME, type, default>,
+ def NAME : SeparateOrEq<["-", "--"], name>, Field<NAME, type, default>,
HelpText<help>;
- def NAME : Separate<["-", "--"], name>, Alias<!cast<Option>(NAME#"_EQ")>;
}
diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td
index 7adb2cf..122a90e 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -32,10 +32,14 @@
def KIND_UNKNOWN : OptionKind<"Unknown", 2, true>;
// A flag with no values.
def KIND_FLAG : OptionKind<"Flag">;
+// A flag, or an option whose value follows '=' (--x or --x=v).
+def KIND_FLAG_OR_EQ : OptionKind<"FlagOrEq">;
// An option which prefixes its (single) value.
def KIND_JOINED : OptionKind<"Joined", 1>;
// An option which is followed by its value.
def KIND_SEPARATE : OptionKind<"Separate">;
+// An option whose value follows '=' or is the next argument (--x=v or --x v).
+def KIND_SEPARATE_OR_EQ : OptionKind<"SeparateOrEq">;
// An option followed by its values, which are separated by commas.
def KIND_COMMAJOINED : OptionKind<"CommaJoined">;
// An option which is which takes multiple (separate) arguments.
@@ -163,6 +167,12 @@
class JoinedAndSeparate<list<string> prefixes, string name,
list<SubCommand> subcommands = []>
: Option<prefixes, name, KIND_JOINED_AND_SEPARATE, subcommands>;
+class FlagOrEq<list<string> prefixes, string name,
+ list<SubCommand> subcommands = []>
+ : Option<prefixes, name, KIND_FLAG_OR_EQ, subcommands>;
+class SeparateOrEq<list<string> prefixes, string name,
+ list<SubCommand> subcommands = []>
+ : Option<prefixes, name, KIND_SEPARATE_OR_EQ, subcommands>;
// Mix-ins for adding optional attributes.
diff --git a/llvm/include/llvm/Option/Option.h b/llvm/include/llvm/Option/Option.h
index c1b24ef..4981a38 100644
--- a/llvm/include/llvm/Option/Option.h
+++ b/llvm/include/llvm/Option/Option.h
@@ -59,19 +59,22 @@
InputClass,
UnknownClass,
FlagClass,
+ FlagOrEqClass,
JoinedClass,
ValuesClass,
SeparateClass,
+ SeparateOrEqClass,
RemainingArgsClass,
RemainingArgsJoinedClass,
CommaJoinedClass,
MultiArgClass,
JoinedOrSeparateClass,
- JoinedAndSeparateClass
+ JoinedAndSeparateClass,
};
enum RenderStyleKind {
RenderCommaJoinedStyle,
+ RenderEqStyle,
RenderJoinedStyle,
RenderSeparateStyle,
RenderValuesStyle
@@ -153,6 +156,9 @@
case JoinedClass:
case JoinedAndSeparateClass:
return RenderJoinedStyle;
+ case FlagOrEqClass:
+ case SeparateOrEqClass:
+ return RenderEqStyle;
case CommaJoinedClass:
return RenderCommaJoinedStyle;
case FlagClass:
diff --git a/llvm/lib/Option/Arg.cpp b/llvm/lib/Option/Arg.cpp
index 3aab7c0..bae86d1 100644
--- a/llvm/lib/Option/Arg.cpp
+++ b/llvm/lib/Option/Arg.cpp
@@ -106,9 +106,15 @@
Output.append(Values.begin() + 1, Values.end());
break;
- case Option::RenderSeparateStyle:
- Output.push_back(Args.MakeArgString(getSpelling()));
- Output.append(Values.begin(), Values.end());
- break;
- }
+ case Option::RenderEqStyle:
+ Output.push_back(Values.empty()
+ ? Args.MakeArgString(getSpelling())
+ : Args.MakeArgString(getSpelling() + "=" + getValue()));
+ break;
+
+ case Option::RenderSeparateStyle:
+ Output.push_back(Args.MakeArgString(getSpelling()));
+ Output.append(Values.begin(), Values.end());
+ break;
+ }
}
diff --git a/llvm/lib/Option/LibraryOptions.cpp b/llvm/lib/Option/LibraryOptions.cpp
index 2b45d1a..3f54c85 100644
--- a/llvm/lib/Option/LibraryOptions.cpp
+++ b/llvm/lib/Option/LibraryOptions.cpp
@@ -22,12 +22,13 @@
const OptTable &T = Table();
for (unsigned ID = 1, E = T.getNumOptions(); ID <= E; ++ID) {
unsigned Kind = T.getOptionKind(ID);
- if (Kind != Option::FlagClass && Kind != Option::JoinedClass &&
- Kind != Option::SeparateClass)
+ if (Kind != Option::FlagOrEqClass && Kind != Option::SeparateOrEqClass)
continue;
- StringRef MetaVar = T.getOptionMetaVar(ID);
- if (MetaVar.empty() && Kind == Option::JoinedClass)
- MetaVar = "<value>";
+ std::string MetaVar;
+ if (Kind == Option::SeparateOrEqClass) {
+ StringRef V = T.getOptionMetaVar(ID);
+ MetaVar = ("=" + (V.empty() ? StringRef("<value>") : V)).str();
+ }
Fn(T.getOptionName(ID), MetaVar, T.getOptionHelpText(ID));
}
}
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
index dc17d4a..7fc9460a 100644
--- a/llvm/lib/Option/OptTable.cpp
+++ b/llvm/lib/Option/OptTable.cpp
@@ -299,6 +299,10 @@
std::tie(NormalizedName, RHS) = Option.split(Last);
if (Option.find(Last) == NormalizedName.size())
NormalizedName += Last;
+ } else if (CandidateInfo.Kind == opt::Option::FlagOrEqClass ||
+ CandidateInfo.Kind == opt::Option::SeparateOrEqClass) {
+ NormalizedName = Option.split('=').first;
+ RHS = Option.drop_front(NormalizedName.size());
} else
NormalizedName = Option;
@@ -629,6 +633,15 @@
break;
case Option::FlagClass:
+ case Option::FlagOrEqClass:
+ break;
+
+ case Option::SeparateOrEqClass:
+ Name += '=';
+ if (StringRef MetaVarName = Opts.getOptionMetaVar(Id); !MetaVarName.empty())
+ Name += MetaVarName;
+ else
+ Name += "<value>";
break;
case Option::ValuesClass:
diff --git a/llvm/lib/Option/Option.cpp b/llvm/lib/Option/Option.cpp
index 8e36384..9a0d82d 100644
--- a/llvm/lib/Option/Option.cpp
+++ b/llvm/lib/Option/Option.cpp
@@ -46,9 +46,11 @@
P(InputClass);
P(UnknownClass);
P(FlagClass);
+ P(FlagOrEqClass);
P(JoinedClass);
P(ValuesClass);
P(SeparateClass);
+ P(SeparateOrEqClass);
P(CommaJoinedClass);
P(MultiArgClass);
P(JoinedOrSeparateClass);
@@ -216,6 +218,23 @@
A->getValues().push_back(Args.getArgString(Index++));
return A;
}
+ case FlagOrEqClass:
+ case SeparateOrEqClass: {
+ const char *Rest = Args.getArgString(Index) + SpellingSize;
+ if (*Rest == '=')
+ return std::make_unique<Arg>(*this, CurArg, Index++, Rest + 1);
+ if (*Rest)
+ return nullptr;
+ if (getKind() == FlagOrEqClass)
+ return std::make_unique<Arg>(*this, CurArg, Index++);
+
+ Index += 2;
+ if (Index > Args.getNumInputArgStrings() ||
+ Args.getArgString(Index - 1) == nullptr)
+ return nullptr;
+ return std::make_unique<Arg>(*this, CurArg, Index - 2,
+ Args.getArgString(Index - 1));
+ }
case RemainingArgsJoinedClass: {
auto A = std::make_unique<Arg>(*this, CurArg, Index);
if (SpellingSize != ArgStringSize) {
diff --git a/llvm/unittests/Option/LibraryOptionsTest.cpp b/llvm/unittests/Option/LibraryOptionsTest.cpp
index 1ca9d33..e900840 100644
--- a/llvm/unittests/Option/LibraryOptionsTest.cpp
+++ b/llvm/unittests/Option/LibraryOptionsTest.cpp
@@ -97,15 +97,11 @@
});
EXPECT_THAT(Rows,
testing::ElementsAre(
- "lib-count=|<value>|An unsigned", "lib-count||",
- "lib-enable=|<value>|", "lib-enable||A bool",
- "lib-limit=|<value>|An optional", "lib-limit||",
- "lib-mode=|<a|b>|An enum", "lib-mode||",
- "lib-override=|<value>|", "lib-override||An optional bool",
- "lib-path=|<value>|A string", "lib-path||",
- "lib-ratio=|<value>|A double", "lib-ratio||",
- "lib-tristate=|<Default|Enable|Disable>|A tri-state",
- "lib-tristate||"));
+ "lib-count|=<value>|An unsigned", "lib-enable||A bool",
+ "lib-limit|=<value>|An optional", "lib-mode|=<a|b>|An enum",
+ "lib-override||An optional bool",
+ "lib-path|=<value>|A string", "lib-ratio|=<value>|A double",
+ "lib-tristate|=<Default|Enable|Disable>|A tri-state"));
auto Parse = [&](std::initializer_list<const char *> Argv) {
unsigned Consumed = 0;
@@ -119,6 +115,11 @@
EXPECT_EQ(Parse({"-lib-count"}),
"1 option '-lib-count' requires an argument");
EXPECT_EQ(Parse({"-lib-other"}), "1 unknown argument '-lib-other'");
+ EXPECT_EQ(Parse({"-lib-counts=1"}), "1 unknown argument '-lib-counts=1'");
+ EXPECT_EQ(Parse({"-lib-enable", "0"}), "1 ");
+ EXPECT_EQ(Parse({"-lib-count", "x"}),
+ "2 invalid value 'x' in '-lib-count=x'");
+ EXPECT_EQ(Parse({"-lib-enable=x"}), "1 invalid value 'x' in '-lib-enable=x'");
P.reset();
EXPECT_EQ(TestLibraryOptions::Global.count, 3u);
}
diff --git a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
index b4487c4..b07de94 100644
--- a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
@@ -229,10 +229,10 @@
return Ret;
}
-// -foo-bar and -foo-bar= become foo_bar, or bar if Prefix is "foo-".
+// -foo-bar becomes foo_bar, or bar if Prefix is "foo-".
static std::string getSpellingIdentifier(const Record &R,
StringRef Prefix = "") {
- StringRef Spelling = R.getValueAsString("Name").rtrim('=');
+ StringRef Spelling = R.getValueAsString("Name");
Spelling.consume_front(Prefix);
std::string ID = Spelling.str();
llvm::replace(ID, '-', '_');
@@ -252,14 +252,11 @@
}
// The OPT_ name of an option of an OptionsStruct. `defm :` rows are named
-// after the spelling: -foo-bar= is OPT_foo_bar_EQ.
+// after the spelling: -foo-bar is OPT_foo_bar.
static std::string getStructOptionID(const Record &R) {
if (!R.getName().starts_with("anonymous_"))
return getOptionName(R);
- std::string ID = getSpellingIdentifier(R);
- if (R.getValueAsString("Name").ends_with('='))
- ID += "_EQ";
- return ID;
+ return getSpellingIdentifier(R);
}
// Emits the struct an OptionsStruct def declares: its declaration under
@@ -282,11 +279,12 @@
"declared with BoolField or ValueField");
continue;
}
- bool HasValue = R->getValue("FieldValue");
- if ((Kind == "Flag") != HasValue ||
- (Kind != "Flag" && Kind != "Joined" && Kind != "Separate"))
- PrintFatalError(R->getLoc(), "a member is set by a Flag with a "
- "FieldValue, or by a Joined or Separate");
+ if (Kind != "FlagOrEq" && Kind != "SeparateOrEq")
+ PrintFatalError(R->getLoc(),
+ "a member is set by a FlagOrEq or SeparateOrEq");
+ StringRef Type = R->getValueAsString("FieldType");
+ if (Kind == "FlagOrEq" && Type != "bool" && Type != "std::optional<bool>")
+ PrintFatalError(R->getLoc(), "a FlagOrEq sets a bool member");
Fields.push_back(R);
}
// Members in declaration order.
@@ -296,21 +294,15 @@
});
StringRef Prefix = Struct.getValueAsString("MemberPrefix");
std::vector<Member> Members;
- StringMap<unsigned> MemberIndex;
+ StringMap<StringRef> MemberSpelling;
for (const Record *R : ByID) {
Member M{getMemberName(*R, Prefix), R->getValueAsString("FieldType"),
- R->getValueAsString("FieldDefault"),
- R->getValueAsString("Name").rtrim('=')};
- auto [It, Inserted] = MemberIndex.try_emplace(M.Name, Members.size());
- if (Inserted) {
- Members.push_back(M);
- continue;
- }
- // The rows of one BoolField or ValueField share the member.
- Member &Prev = Members[It->second];
- if (Prev.Spelling != M.Spelling)
- PrintFatalError(R->getLoc(), "member '" + M.Name + "' is also set by -" +
- Prev.Spelling);
+ R->getValueAsString("FieldDefault"), R->getValueAsString("Name")};
+ auto [It, Inserted] = MemberSpelling.try_emplace(M.Name, M.Spelling);
+ if (!Inserted)
+ PrintFatalError(R->getLoc(),
+ "member '" + M.Name + "' is also set by -" + It->second);
+ Members.push_back(M);
}
StringRef Name = Struct.getName();
@@ -364,12 +356,13 @@
OS << " if (V == \"" << Value << "\") {\n " << Member
<< " = " << Enumerator << ";\n return true;\n }\n";
OS << " return false;\n }\n";
- } else if (!R->getValue("FieldValue"))
- OS << " return llvm::opt::parseArgValue(A.getValue(), " << Member
- << ");\n";
- else
- OS << " " << Member << " = " << R->getValueAsString("FieldValue")
- << ";\n return true;\n";
+ continue;
+ }
+ if (R->getValueAsDef("Kind")->getValueAsString("Name") == "FlagOrEq")
+ OS << " if (!A.getNumValues()) {\n " << Member
+ << " = true;\n return true;\n }\n";
+ OS << " return llvm::opt::parseArgValue(A.getValue(), " << Member
+ << ");\n";
}
OS << " }\n llvm_unreachable(\"option without a member\");\n}\n";
OS << "#endif // OPTIONS_STRUCT_DEFS\n";