//===- subzero/src/IceClFlags.cpp - Command line flags and parsing --------===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Defines commandline flags parsing of class Ice::ClFlags. /// /// This currently relies on llvm::cl to parse. In the future, the minimal build /// can have a simpler parser. /// //===----------------------------------------------------------------------===// #include "IceClFlags.h" #include "IceClFlags.def" #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #endif // __clang__ #include "llvm/Support/CommandLine.h" #ifdef __clang__ #pragma clang diagnostic pop #endif // __clang__ #include namespace { // cl is used to alias the llvm::cl types and functions that we need. namespace cl { using alias = llvm::cl::alias; using aliasopt = llvm::cl::aliasopt; using llvm::cl::CommaSeparated; using desc = llvm::cl::desc; template using initializer = llvm::cl::initializer; template initializer init(const T &Val) { return initializer(Val); } template using list = llvm::cl::list; using llvm::cl::NotHidden; template using opt = llvm::cl::opt; using llvm::cl::ParseCommandLineOptions; using llvm::cl::Positional; // LLVM commit 3ffe113e11168abcd809ec5ac539538ade5db0cb changed the internals of // llvm::cl that need to be mirrored here. That commit removed the clEnumValEnd // macro, so we can use that to determine which version of LLVM we're compiling // against. #if defined(clEnumValEnd) #define CLENUMVALEND , clEnumValEnd template using ValuesClass = llvm::cl::ValuesClass; template ValuesClass values(const char *Arg, T Val, const char *Desc, A &&... Args) { return llvm::cl::values(Arg, Val, Desc, std::forward(Args)..., nullptr); } #else // !defined(clEnumValEnd) #define CLENUMVALEND using llvm::cl::OptionEnumValue; template llvm::cl::ValuesClass values(A &&... Args) { return llvm::cl::values(std::forward(Args)...); } #endif // !defined(clEnumValEnd) using llvm::cl::value_desc; } // end of namespace cl // cl_type_traits is used to convert between a tuple of to // the appropriate (llvm::)cl object. template struct cl_type_traits {}; template struct cl_type_traits { using cl_type = cl::list; }; template struct cl_type_traits { using cl_type = cl::opt; }; template struct cl_type_traits { using cl_type = cl::opt; }; #define X(Name, Type, ClType, ...) \ cl_type_traits::cl_type Name##Obj(__VA_ARGS__); COMMAND_LINE_FLAGS #undef X // Add declarations that do not need to add members to ClFlags below. cl::alias AllowExternDefinedSymbolsA( "allow-extern", cl::desc("Alias for --allow-externally-defined-symbols"), cl::NotHidden, cl::aliasopt(AllowExternDefinedSymbolsObj)); std::string AppNameObj; } // end of anonymous namespace namespace Ice { ClFlags ClFlags::Flags; void ClFlags::parseFlags(int argc, const char *const *argv) { cl::ParseCommandLineOptions(argc, argv); AppNameObj = argv[0]; } namespace { // flagInitOrStorageTypeDefault is some template voodoo for peeling off the // llvm::cl modifiers from a flag's declaration, until its initial value is // found. If none is found, then the default value for the storage type is // returned. template Ty flagInitOrStorageTypeDefault() { return Ty(); } template Ty flagInitOrStorageTypeDefault(cl::initializer &&Value, A &&...) { return Value.Init; } // is_cl_initializer is used to prevent an ambiguous call between the previous // version of flagInitOrStorageTypeDefault, and the next, which is flagged by // g++. template struct is_cl_initializer { static constexpr bool value = false; }; template struct is_cl_initializer> { static constexpr bool value = true; }; template typename std::enable_if::value, Ty>::type flagInitOrStorageTypeDefault(T &&, A &&... Other) { return flagInitOrStorageTypeDefault(std::forward(Other)...); } } // end of anonymous namespace void ClFlags::resetClFlags() { #define X(Name, Type, ClType, ...) \ Name = flagInitOrStorageTypeDefault< \ detail::cl_type_traits::storage_type>( \ __VA_ARGS__); COMMAND_LINE_FLAGS #undef X } namespace { // toSetterParam is template magic that is needed to convert between (llvm::)cl // objects and the arguments to ClFlags' setters. ToSetterParam is a traits // object that we need in order for the multiple specializations to // toSetterParam to agree on their return type. template struct ToSetterParam { using ReturnType = const T &; }; template <> struct ToSetterParam> { using ReturnType = Ice::VerboseMask; }; template <> struct ToSetterParam> { using ReturnType = std::vector; }; template typename ToSetterParam::ReturnType toSetterParam(const T &Param) { return Param; } template <> ToSetterParam>::ReturnType toSetterParam(const cl::list &Param) { Ice::VerboseMask VMask = Ice::IceV_None; // Don't generate verbose messages if routines to dump messages are not // available. if (BuildDefs::dump()) { for (unsigned i = 0; i != Param.size(); ++i) VMask |= Param[i]; } return VMask; } template <> ToSetterParam>::ReturnType toSetterParam(const cl::list &Param) { return *&Param; } } // end of anonymous namespace void ClFlags::getParsedClFlags(ClFlags &OutFlags) { #define X(Name, Type, ClType, ...) OutFlags.set##Name(toSetterParam(Name##Obj)); COMMAND_LINE_FLAGS #undef X // If any value needs a non-trivial parsed value, set it below. OutFlags.setAllowExternDefinedSymbols(AllowExternDefinedSymbolsObj || DisableInternalObj); OutFlags.setDisableHybridAssembly(DisableHybridAssemblyObj || (OutFileTypeObj != Ice::FT_Iasm)); OutFlags.ForceO2.init(OutFlags.getForceO2String()); OutFlags.SplitInsts.init(OutFlags.getSplitInstString()); OutFlags.TestStatus.init(OutFlags.getTestStatusString()); OutFlags.TimingFocus.init(OutFlags.getTimingFocusOnString()); OutFlags.TranslateOnly.init(OutFlags.getTranslateOnlyString()); OutFlags.VerboseFocus.init(OutFlags.getVerboseFocusOnString()); } } // end of namespace Ice