diff --git a/include/llvm/Option/OptParser.td b/include/llvm/Option/OptParser.td index e32355444d7..47b4788b220 100644 --- a/include/llvm/Option/OptParser.td +++ b/include/llvm/Option/OptParser.td @@ -144,6 +144,11 @@ class ValuesCode { code ValuesCode = valuecode; } // Helpers for defining marshalling information. +class DefaultAnyOf defaults> { + code DefaultValue = !foldl("false", defaults, accumulator, option, + !strconcat(accumulator, " || ", !cast(option.KeyPath))); +} + class MarshallingInfo { code KeyPath = keypath; code DefaultValue = defaultvalue; @@ -154,8 +159,8 @@ class MarshallingInfoString - : MarshallingInfo { +class MarshallingInfoFlag> + : MarshallingInfo { string MarshallingKind = "flag"; } diff --git a/unittests/Option/CMakeLists.txt b/unittests/Option/CMakeLists.txt index 07f7b91d5f5..7be4300c0f3 100644 --- a/unittests/Option/CMakeLists.txt +++ b/unittests/Option/CMakeLists.txt @@ -10,4 +10,5 @@ add_public_tablegen_target(OptsTestTableGen) add_llvm_unittest(OptionTests OptionParsingTest.cpp + OptionMarshallingTest.cpp ) diff --git a/unittests/Option/OptionMarshallingTest.cpp b/unittests/Option/OptionMarshallingTest.cpp new file mode 100644 index 00000000000..bf26a767bba --- /dev/null +++ b/unittests/Option/OptionMarshallingTest.cpp @@ -0,0 +1,47 @@ +//===- unittest/Support/OptionMarshallingTest.cpp - OptParserEmitter tests ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +struct OptionWithMarshallingInfo { + const char *Name; + const char *KeyPath; + const char *DefaultValue; +}; + +static const OptionWithMarshallingInfo MarshallingTable[] = { +#define OPTION_WITH_MARSHALLING_FLAG(PREFIX_TYPE, NAME, ID, KIND, GROUP, \ + ALIAS, ALIASARGS, FLAGS, PARAM, HELPTEXT, \ + METAVAR, VALUES, SPELLING, ALWAYS_EMIT, \ + KEYPATH, DEFAULT_VALUE, IS_POSITIVE) \ + { NAME, #KEYPATH, #DEFAULT_VALUE }, +#include "Opts.inc" +#undef OPTION_WITH_MARSHALLING_FLAG +}; + +TEST(OptionMarshalling, EmittedOrderSameAsDefinitionOrder) { + ASSERT_STREQ(MarshallingTable[0].Name, "marshalled-flag-0"); + ASSERT_STREQ(MarshallingTable[1].Name, "marshalled-flag-1"); + ASSERT_STREQ(MarshallingTable[2].Name, "marshalled-flag-2"); + ASSERT_STREQ(MarshallingTable[3].Name, "marshalled-flag-3"); +} + +TEST(OptionMarshalling, EmittedSpecifiedKeyPath) { + ASSERT_STREQ(MarshallingTable[0].KeyPath, "MarshalledFlag0"); + ASSERT_STREQ(MarshallingTable[1].KeyPath, "MarshalledFlag1"); + ASSERT_STREQ(MarshallingTable[2].KeyPath, "MarshalledFlag2"); + ASSERT_STREQ(MarshallingTable[3].KeyPath, "MarshalledFlag3"); +} + +TEST(OptionMarshalling, DefaultAnyOfConstructedDisjunctionOfKeypaths) { + ASSERT_STREQ(MarshallingTable[0].DefaultValue, "false"); + ASSERT_STREQ(MarshallingTable[1].DefaultValue, "false || MarshalledFlag0"); + ASSERT_STREQ(MarshallingTable[2].DefaultValue, "false || MarshalledFlag0"); + ASSERT_STREQ(MarshallingTable[3].DefaultValue, + "false || MarshalledFlag1 || MarshalledFlag2"); +} diff --git a/unittests/Option/Opts.td b/unittests/Option/Opts.td index e1ebffd1881..3bbf210b0b7 100644 --- a/unittests/Option/Opts.td +++ b/unittests/Option/Opts.td @@ -44,3 +44,12 @@ def Blurmpq : Flag<["--"], "blurmp">; def Blurmpq_eq : Flag<["--"], "blurmp=">; def DashDash : Option<["--"], "", KIND_REMAINING_ARGS>; + +def marshalled_flag_0 : Flag<["-"], "marshalled-flag-0">, + MarshallingInfoFlag<"MarshalledFlag0", DefaultAnyOf<[]>>; +def marshalled_flag_1 : Flag<["-"], "marshalled-flag-1">, + MarshallingInfoFlag<"MarshalledFlag1", DefaultAnyOf<[marshalled_flag_0]>>; +def marshalled_flag_2 : Flag<["-"], "marshalled-flag-2">, + MarshallingInfoFlag<"MarshalledFlag2", DefaultAnyOf<[marshalled_flag_0]>>; +def marshalled_flag_3 : Flag<["-"], "marshalled-flag-3">, + MarshallingInfoFlag<"MarshalledFlag3", DefaultAnyOf<[marshalled_flag_1, marshalled_flag_2]>>; diff --git a/utils/TableGen/OptParserEmitter.cpp b/utils/TableGen/OptParserEmitter.cpp index 6e49e248e4b..0c30a2dd28d 100644 --- a/utils/TableGen/OptParserEmitter.cpp +++ b/utils/TableGen/OptParserEmitter.cpp @@ -435,7 +435,12 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) { OS << "nullptr"; }; - std::vector> OptsWithMarshalling; + auto IsMarshallingOption = [](const Record &R) { + return !isa(R.getValueInit("MarshallingKind")) && + !R.getValueAsString("KeyPath").empty(); + }; + + std::vector OptsWithMarshalling; for (unsigned I = 0, E = Opts.size(); I != E; ++I) { const Record &R = *Opts[I]; @@ -443,12 +448,33 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) { OS << "OPTION("; WriteOptRecordFields(OS, R); OS << ")\n"; - if (!isa(R.getValueInit("MarshallingKind"))) - OptsWithMarshalling.push_back(MarshallingKindInfo::create(R)); + if (IsMarshallingOption(R)) + OptsWithMarshalling.push_back(&R); } OS << "#endif // OPTION\n"; - for (const auto &KindInfo : OptsWithMarshalling) { + auto CmpMarshallingOpts = [](const Record *const *A, const Record *const *B) { + unsigned AID = (*A)->getID(); + unsigned BID = (*B)->getID(); + + if (AID < BID) + return -1; + if (AID > BID) + return 1; + return 0; + }; + // The RecordKeeper stores records (options) in lexicographical order, and we + // have reordered the options again when generating prefix groups. We need to + // restore the original definition order of options with marshalling to honor + // the topology of the dependency graph implied by `DefaultAnyOf`. + array_pod_sort(OptsWithMarshalling.begin(), OptsWithMarshalling.end(), + CmpMarshallingOpts); + + std::vector> MarshallingKindInfos; + for (const auto *R : OptsWithMarshalling) + MarshallingKindInfos.push_back(MarshallingKindInfo::create(*R)); + + for (const auto &KindInfo : MarshallingKindInfos) { OS << "#ifdef " << KindInfo->MacroName << "\n"; OS << KindInfo->MacroName << "("; WriteOptRecordFields(OS, KindInfo->R); @@ -463,7 +489,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) { OS << "\n"; OS << MarshallingStringInfo::ValueTablePreamble; std::vector ValueTableNames; - for (const auto &KindInfo : OptsWithMarshalling) + for (const auto &KindInfo : MarshallingKindInfos) if (auto MaybeValueTableName = KindInfo->emitValueTable(OS)) ValueTableNames.push_back(*MaybeValueTableName);