//===- 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 <utility>
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 <typename T> using initializer = llvm::cl::initializer<T>;
template <typename T> initializer<T> init(const T &Val) {
return initializer<T>(Val);
}
template <typename T> using list = llvm::cl::list<T>;
using llvm::cl::NotHidden;
template <typename T> using opt = llvm::cl::opt<T>;
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 <typename T> using ValuesClass = llvm::cl::ValuesClass<T>;
template <typename T, typename... A>
ValuesClass<T> values(const char *Arg, T Val, const char *Desc, A &&... Args) {
return llvm::cl::values(Arg, Val, Desc, std::forward<A>(Args)..., nullptr);
}
#else // !defined(clEnumValEnd)
#define CLENUMVALEND
using llvm::cl::OptionEnumValue;
template <typename... A> llvm::cl::ValuesClass values(A &&... Args) {
return llvm::cl::values(std::forward<A>(Args)...);
}
#endif // !defined(clEnumValEnd)
using llvm::cl::value_desc;
} // end of namespace cl
// cl_type_traits is used to convert between a tuple of <T, cl_detail::*flag> to
// the appropriate (llvm::)cl object.
template <typename B, typename CL> struct cl_type_traits {};
template <typename T>
struct cl_type_traits<T, ::Ice::cl_detail::dev_list_flag> {
using cl_type = cl::list<T>;
};
template <typename T> struct cl_type_traits<T, ::Ice::cl_detail::dev_opt_flag> {
using cl_type = cl::opt<T>;
};
template <typename T>
struct cl_type_traits<T, ::Ice::cl_detail::release_opt_flag> {
using cl_type = cl::opt<T>;
};
#define X(Name, Type, ClType, ...) \
cl_type_traits<Type, Ice::cl_detail::ClType>::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 <typename Ty> Ty flagInitOrStorageTypeDefault() { return Ty(); }
template <typename Ty, typename T, typename... A>
Ty flagInitOrStorageTypeDefault(cl::initializer<T> &&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 <typename T> struct is_cl_initializer {
static constexpr bool value = false;
};
template <typename T> struct is_cl_initializer<cl::initializer<T>> {
static constexpr bool value = true;
};
template <typename Ty, typename T, typename... A>
typename std::enable_if<!is_cl_initializer<T>::value, Ty>::type
flagInitOrStorageTypeDefault(T &&, A &&... Other) {
return flagInitOrStorageTypeDefault<Ty>(std::forward<A>(Other)...);
}
} // end of anonymous namespace
void ClFlags::resetClFlags() {
#define X(Name, Type, ClType, ...) \
Name = flagInitOrStorageTypeDefault< \
detail::cl_type_traits<Type, cl_detail::ClType>::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 <typename T> struct ToSetterParam { using ReturnType = const T &; };
template <> struct ToSetterParam<cl::list<Ice::VerboseItem>> {
using ReturnType = Ice::VerboseMask;
};
template <> struct ToSetterParam<cl::list<std::string>> {
using ReturnType = std::vector<std::string>;
};
template <typename T>
typename ToSetterParam<T>::ReturnType toSetterParam(const T &Param) {
return Param;
}
template <>
ToSetterParam<cl::list<Ice::VerboseItem>>::ReturnType
toSetterParam(const cl::list<Ice::VerboseItem> &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<cl::list<std::string>>::ReturnType
toSetterParam(const cl::list<std::string> &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