diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -23,7 +23,8 @@ def target_feature : Separate<["-"], "target-feature">, HelpText<"Target specific attributes">; def triple : Separate<["-"], "triple">, - HelpText<"Specify target triple (e.g. i686-apple-darwin9)">; + HelpText<"Specify target triple (e.g. i686-apple-darwin9)">, + MarshallingInfoTriple", "llvm::sys::getDefaultTargetTriple()", "normalizeTriple", "">>; def target_abi : Separate<["-"], "target-abi">, HelpText<"Target a particular ABI type">; def target_sdk_version_EQ : Joined<["-"], "target-sdk-version=">, @@ -212,7 +213,8 @@ "Note this may change .s semantics and shouldn't generally be used " "on compiler-generated code.">; def mrelocation_model : Separate<["-"], "mrelocation-model">, - HelpText<"The relocation model to use">, Values<"static,pic,ropi,rwpi,ropi-rwpi,dynamic-no-pic">; + HelpText<"The relocation model to use">, Values<"static,pic,ropi,rwpi,ropi-rwpi,dynamic-no-pic">, + MarshallingInfo>; def fno_math_builtin : Flag<["-"], "fno-math-builtin">, HelpText<"Disable implicit builtin knowledge of math functions">; } diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -237,6 +237,16 @@ } /// @} + +private: + /// Parse options for flags that expose marshalling information in their + /// table-gen definition + /// + /// \param Args - The argument list containing the arguments to parse + /// \param Diags - The DiagnosticsEngine associated with CreateFromArgs + /// \returns - True if parsing was successful, false otherwise + bool parseSimpleArgs(const llvm::opt::ArgList &Args, + DiagnosticsEngine &Diags); }; IntrusiveRefCntPtr diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -118,6 +118,55 @@ CompilerInvocationBase::~CompilerInvocationBase() = default; +//===----------------------------------------------------------------------===// +// Normalizers +//===----------------------------------------------------------------------===// + +static std::string normalizeTriple(const Arg *Arg, const ArgList &ArgList, + DiagnosticsEngine &Diags, + StringRef DefaultTriple) { + return llvm::Triple::normalize(Arg->getValue()); +} + +static llvm::Reloc::Model +normalizeRelocationModel(const Arg *Arg, const ArgList &ArgList, + DiagnosticsEngine &Diags, + llvm::Reloc::Model DefaultValue) { + StringRef ArgValue = Arg->getValue(); + auto RM = llvm::StringSwitch>(ArgValue) + .Case("static", llvm::Reloc::Static) + .Case("pic", llvm::Reloc::PIC_) + .Case("ropi", llvm::Reloc::ROPI) + .Case("rwpi", llvm::Reloc::RWPI) + .Case("ropi-rwpi", llvm::Reloc::ROPI_RWPI) + .Case("dynamic-no-pic", llvm::Reloc::DynamicNoPIC) + .Default(None); + + if (RM.hasValue()) + return *RM; + + Diags.Report(diag::err_drv_invalid_value) + << Arg->getAsString(ArgList) << ArgValue; + return DefaultValue; +} + +static const char *denormalizeRelocationModel(llvm::Reloc::Model RM) { + switch (RM) { + case llvm::Reloc::Static: + return "static"; + case llvm::Reloc::PIC_: + return "pic"; + case llvm::Reloc::DynamicNoPIC: + return "dynamic-no-pic"; + case llvm::Reloc::ROPI: + return "ropi"; + case llvm::Reloc::RWPI: + return "rwpi"; + case llvm::Reloc::ROPI_RWPI: + return "ropi-rwpi"; + } +} + //===----------------------------------------------------------------------===// // Deserialization (from args) //===----------------------------------------------------------------------===// @@ -529,25 +578,6 @@ Opts.ParseAllComments = Args.hasArg(OPT_fparse_all_comments); } -static llvm::Reloc::Model getRelocModel(ArgList &Args, - DiagnosticsEngine &Diags) { - if (Arg *A = Args.getLastArg(OPT_mrelocation_model)) { - StringRef Value = A->getValue(); - auto RM = llvm::StringSwitch>(Value) - .Case("static", llvm::Reloc::Static) - .Case("pic", llvm::Reloc::PIC_) - .Case("ropi", llvm::Reloc::ROPI) - .Case("rwpi", llvm::Reloc::RWPI) - .Case("ropi-rwpi", llvm::Reloc::ROPI_RWPI) - .Case("dynamic-no-pic", llvm::Reloc::DynamicNoPIC) - .Default(None); - if (RM.hasValue()) - return *RM; - Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Value; - } - return llvm::Reloc::PIC_; -} - /// Create a new Regex instance out of the string value in \p RpassArg. /// It returns a pointer to the newly generated Regex instance. static std::shared_ptr @@ -928,7 +958,6 @@ Opts.StrictVTablePointers = Args.hasArg(OPT_fstrict_vtable_pointers); Opts.ForceEmitVTables = Args.hasArg(OPT_fforce_emit_vtables); Opts.UnwindTables = Args.hasArg(OPT_munwind_tables); - Opts.RelocationModel = getRelocModel(Args, Diags); Opts.ThreadModel = std::string(Args.getLastArgValue(OPT_mthread_model, "posix")); if (Opts.ThreadModel != "posix" && Opts.ThreadModel != "single") @@ -3613,6 +3642,32 @@ } } +bool CompilerInvocation::parseSimpleArgs(const ArgList &Args, + DiagnosticsEngine &Diags) { +#define OPTION_WITH_MARSHALLING +#define OPTION_WITH_MARSHALLING_FLAG(PREFIX_TYPE, NAME, ID, KIND, GROUP, \ + ALIAS, ALIASARGS, FLAGS, PARAM, HELPTEXT, \ + METAVAR, VALUES, ALWAYS_EMIT, KEYPATH, \ + IS_POSITIVE, DEFAULT_VALUE) \ + this->KEYPATH = Args.hasArg(OPT_##ID) && IS_POSITIVE; + +#define OPTION_WITH_MARSHALLING_STRING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + NORMALIZER, DENORMALIZER) \ + { \ + auto Arg = Args.getLastArg(OPT_##ID); \ + this->KEYPATH = \ + !Arg ? DEFAULT_VALUE : NORMALIZER(Arg, Args, Diags, DEFAULT_VALUE); \ + } + +#include "clang/Driver/Options.inc" +#undef OPTION_WITH_MARSHALLING_STRING +#undef OPTION_WITH_MARSHALLING_FLAG +#undef OPTION_WITH_MARSHALLING + return true; +} + bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ArrayRef CommandLineArgs, DiagnosticsEngine &Diags) { @@ -3624,15 +3679,6 @@ unsigned MissingArgIndex, MissingArgCount; InputArgList Args = Opts.ParseArgs(CommandLineArgs, MissingArgIndex, MissingArgCount, IncludedFlagsBitmask); - -#define OPTION_WITH_MARSHALLING(PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, \ - ALIASARGS, FLAGS, PARAM, HELPTEXT, METAVAR, \ - VALUES, KEYPATH, IS_POSITIVE, DEFAULT_VALUE) \ - if (Option::KIND##Class == Option::FlagClass) \ - Res.KEYPATH = Args.hasArg(OPT_##ID) && IS_POSITIVE; -#include "clang/Driver/Options.inc" -#undef OPTION_WITH_MARSHALLING - LangOptions &LangOpts = *Res.getLangOpts(); // Check for missing argument error. @@ -3654,6 +3700,7 @@ Success = false; } + Success &= Res.parseSimpleArgs(Args, Diags); Success &= ParseAnalyzerArgs(*Res.getAnalyzerOpts(), Args, Diags); Success &= ParseMigratorArgs(Res.getMigratorOpts(), Args); ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), Args); @@ -3861,13 +3908,31 @@ llvm::function_ref StringAllocator) const { #define PREFIX(PREFIX_TYPE, BRACED_INIT) \ const char *PREFIX_TYPE[4] = BRACED_INIT; -#define OPTION_WITH_MARSHALLING(PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, \ - ALIASARGS, FLAGS, PARAM, HELPTEXT, METAVAR, \ - VALUES, KEYPATH, IS_POSITIVE, DEFAULT_VALUE) \ - if (Option::KIND##Class == Option::FlagClass && \ - IS_POSITIVE != DEFAULT_VALUE && this->KEYPATH != DEFAULT_VALUE) \ + +#define OPTION_WITH_MARSHALLING +#define OPTION_WITH_MARSHALLING_FLAG(PREFIX_TYPE, NAME, ID, KIND, GROUP, \ + ALIAS, ALIASARGS, FLAGS, PARAM, HELPTEXT, \ + METAVAR, VALUES, ALWAYS_EMIT, KEYPATH, \ + IS_POSITIVE, DEFAULT_VALUE) \ + if (FLAGS & options::CC1Option && IS_POSITIVE != DEFAULT_VALUE && \ + (this->KEYPATH != DEFAULT_VALUE || ALWAYS_EMIT)) \ Args.push_back(StringAllocator(Twine(PREFIX_TYPE[0]) + NAME)); + +#define OPTION_WITH_MARSHALLING_STRING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + NORMALIZER, DENORMALIZER) \ + if ((FLAGS & options::CC1Option) && \ + (this->KEYPATH != DEFAULT_VALUE || ALWAYS_EMIT)) { \ + if (Option::KIND##Class == Option::SeparateClass) { \ + Args.push_back(StringAllocator(Twine(PREFIX_TYPE[0]) + NAME)); \ + Args.push_back(StringAllocator(DENORMALIZER(this->KEYPATH))); \ + } \ + } + #include "clang/Driver/Options.inc" +#undef OPTION_WITH_MARSHALLING_STRING +#undef OPTION_WITH_MARSHALLING_FLAG #undef OPTION_WITH_MARSHALLING #undef PREFIX } diff --git a/clang/unittests/Frontend/CompilerInvocationTest.cpp b/clang/unittests/Frontend/CompilerInvocationTest.cpp --- a/clang/unittests/Frontend/CompilerInvocationTest.cpp +++ b/clang/unittests/Frontend/CompilerInvocationTest.cpp @@ -7,32 +7,96 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/CompilerInvocation.h" +#include "clang/Basic/DiagnosticIDs.h" #include "clang/Frontend/CompilerInstance.h" +#include "llvm/Support/Host.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" using namespace llvm; using namespace clang; +using ::testing::Contains; +using ::testing::Each; +using ::testing::StrEq; +using ::testing::StrNe; + namespace { -TEST(CompilerInvocation, CanGenerateCC1CommandLine) { +class CC1CommandLineGenerationTest : public ::testing::Test { +public: + IntrusiveRefCntPtr Diags; + SmallVector GeneratedArgs; + SmallVector GeneratedArgsStorage; + + const char *operator()(const Twine &Arg) { + return GeneratedArgsStorage.emplace_back(Arg.str()).c_str(); + } + + CC1CommandLineGenerationTest() + : Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions())) {} +}; + +TEST_F(CC1CommandLineGenerationTest, CanGenerateCC1CommandLineFlag) { const char *Args[] = {"clang", "-xc++", "-fmodules-strict-context-hash", "-"}; - IntrusiveRefCntPtr Diags = - CompilerInstance::createDiagnostics(new DiagnosticOptions()); + CompilerInvocation CInvok; + CompilerInvocation::CreateFromArgs(CInvok, Args, *Diags); + + CInvok.generateCC1CommandLine(GeneratedArgs, *this); + + ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash"))); +} + +TEST_F(CC1CommandLineGenerationTest, CanGenerateCC1CommandLineSeparate) { + const char *TripleCStr = "i686-apple-darwin9"; + const char *Args[] = {"clang", "-xc++", "-triple", TripleCStr, "-"}; CompilerInvocation CInvok; CompilerInvocation::CreateFromArgs(CInvok, Args, *Diags); - SmallVector GeneratedArgs; - SmallVector GeneratedArgsStorage; - auto StringAlloc = [&GeneratedArgsStorage](const Twine &Arg) { - return GeneratedArgsStorage.emplace_back(Arg.str()).c_str(); - }; + CInvok.generateCC1CommandLine(GeneratedArgs, *this); + + ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr))); +} + +TEST_F(CC1CommandLineGenerationTest, + CanGenerateCC1CommandLineSeparateRequired) { + const char *TripleCStr = + llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()).c_str(); + const char *Args[] = {"clang", "-xc++", "-triple", TripleCStr, "-"}; + + CompilerInvocation CInvok; + CompilerInvocation::CreateFromArgs(CInvok, Args, *Diags); + + CInvok.generateCC1CommandLine(GeneratedArgs, *this); + + // Triple should always be emitted even if it is the default + ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr))); +} + +TEST_F(CC1CommandLineGenerationTest, CanGenerateCC1CommandLineSeparateEnum) { + const char *RelocationModelCStr = "static"; + const char *Args[] = {"clang", "-xc++", "-mrelocation-model", + RelocationModelCStr, "-"}; + + CompilerInvocation CInvok; + CompilerInvocation::CreateFromArgs(CInvok, Args, *Diags); + + CInvok.generateCC1CommandLine(GeneratedArgs, *this); + + // Non default relocation model + ASSERT_THAT(GeneratedArgs, Contains(StrEq(RelocationModelCStr))); + GeneratedArgs.clear(); + + RelocationModelCStr = "pic"; + Args[3] = RelocationModelCStr; - CInvok.generateCC1CommandLine(GeneratedArgs, StringAlloc); + CompilerInvocation CInvok1; + CompilerInvocation::CreateFromArgs(CInvok1, Args, *Diags); - ASSERT_STREQ(GeneratedArgs[0], "-fmodules-strict-context-hash"); + CInvok1.generateCC1CommandLine(GeneratedArgs, *this); + ASSERT_THAT(GeneratedArgs, Each(StrNe(RelocationModelCStr))); } } // anonymous namespace diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td --- a/llvm/include/llvm/Option/OptParser.td +++ b/llvm/include/llvm/Option/OptParser.td @@ -81,13 +81,15 @@ } // Add support for generating marshalling code - -class OptionMarshallingInfo { +class OptionMarshallingInfo { + string Kind = kind; + bit ShouldAlwaysEmit = 0; code KeyPath = keypath; // Used by the Flag option kind. bit IsPositive = ?; code DefaultValue = ?; - list EnumValues = ?; + code Normalizer = ?; + code Denormalizer = ?; } // Define the option class. @@ -143,20 +145,24 @@ class MarshallingInfo { OptionMarshallingInfo MarshallingInfo = info; } class MarshallingFlag - : OptionMarshallingInfo { + : OptionMarshallingInfo<"flag", keypath> { bit IsPositive = ispositive; code DefaultValue = defaultvalue; } -class MarshallingString - : OptionMarshallingInfo { - code DefaultValue = defaultvalue; +class MarshallingFlagAlwaysEmit + : MarshallingFlag { + let ShouldAlwaysEmit = 1; } -class MarshallingEnum enumvalues> - : OptionMarshallingInfo { +class MarshallingString + : OptionMarshallingInfo<"string", keypath> { code DefaultValue = defaultvalue; - listEnumValues = enumvalues; + code Normalizer = normalizer; + code Denormalizer = denormalizer; +} +class MarshallingStringAlwaysEmit + : MarshallingString { + let ShouldAlwaysEmit = 1; } - // Predefined options. // FIXME: Have generator validate that these appear in correct position (and diff --git a/llvm/utils/TableGen/OptParserEmitter.cpp b/llvm/utils/TableGen/OptParserEmitter.cpp --- a/llvm/utils/TableGen/OptParserEmitter.cpp +++ b/llvm/utils/TableGen/OptParserEmitter.cpp @@ -9,6 +9,7 @@ #include "OptEmitter.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" @@ -33,18 +34,18 @@ return OS; } -static void emitMarshallingInfo(raw_ostream &OS, const Record &R) { - OS << R.getValueAsString("KeyPath"); +static void emitMarshallingInfoFlag(raw_ostream &OS, const Record *R) { + OS << R->getValueAsBit("IsPositive"); + OS << ","; + OS << R->getValueAsString("DefaultValue"); +} + +static void emitMarshallingInfoString(raw_ostream &OS, const Record *R) { + OS << R->getValueAsString("DefaultValue"); OS << ", "; - if (!isa(R.getValueInit("IsPositive"))) - OS << R.getValueAsBit("IsPositive"); - else - OS << "INVALID"; + OS << R->getValueAsString("Normalizer"); OS << ", "; - if (!isa(R.getValueInit("DefaultValue"))) - OS << R.getValueAsString("DefaultValue"); - else - OS << "INVALID"; + OS << R->getValueAsString("Denormalizer"); } /// OptParserEmitter - This tablegen backend takes an input .td file @@ -246,15 +247,30 @@ OS << "#endif // OPTION\n"; OS << "#ifdef OPTION_WITH_MARSHALLING\n"; - for (unsigned i = 0, e = Opts.size(); i != e; ++i) { - const Record &R = *Opts[i]; + for (unsigned I = 0, E = Opts.size(); I != E; ++I) { + const Record &R = *Opts[I]; if (!isa(R.getValueInit("MarshallingInfo"))) { - OS << "OPTION_WITH_MARSHALLING("; + Record *MarshallingInfoRecord = + cast(R.getValueInit("MarshallingInfo"))->getDef(); + StringRef KindStr = MarshallingInfoRecord->getValueAsString("Kind"); + auto KindInfoPair = + StringSwitch>>( + KindStr) + .Case("flag", std::make_pair("OPTION_WITH_MARSHALLING_FLAG", + &emitMarshallingInfoFlag)) + .Case("string", std::make_pair("OPTION_WITH_MARSHALLING_STRING", + &emitMarshallingInfoString)) + .Default(std::make_pair("", nullptr)); + OS << KindInfoPair.first << "("; WriteOptRecordFields(OS, R); OS << ", "; - emitMarshallingInfo( - OS, *cast(R.getValueInit("MarshallingInfo"))->getDef()); + OS << MarshallingInfoRecord->getValueAsBit("ShouldAlwaysEmit"); + OS << ", "; + OS << MarshallingInfoRecord->getValueAsString("KeyPath"); + OS << ", "; + KindInfoPair.second(OS, MarshallingInfoRecord); OS << ")\n"; } }