llvm-project/clang/lib/Frontend/CompilerInvocation.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

4819 lines
194 KiB
C++
Raw Normal View History

//===- CompilerInvocation.cpp ---------------------------------------------===//
//
// 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 "clang/Frontend/CompilerInvocation.h"
#include "TestModuleFileExtension.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/CommentOptions.h"
#include "clang/Basic/DebugInfoOptions.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticDriver.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/LangStandard.h"
#include "clang/Basic/ObjCRuntime.h"
#include "clang/Basic/Sanitizers.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/Version.h"
#include "clang/Basic/Visibility.h"
#include "clang/Basic/XRayInstr.h"
#include "clang/Config/config.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/Options.h"
#include "clang/Frontend/CommandLineSourceLoc.h"
#include "clang/Frontend/DependencyOutputOptions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/FrontendOptions.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/Frontend/MigratorOptions.h"
#include "clang/Frontend/PreprocessorOutputOptions.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
#include "clang/Frontend/Utils.h"
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Sema/CodeCompleteOptions.h"
#include "clang/Serialization/ASTBitCodes.h"
#include "clang/Serialization/ModuleFileExtension.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
[FPEnv] Use single enum to represent rounding mode Now compiler defines 5 sets of constants to represent rounding mode. These are: 1. `llvm::APFloatBase::roundingMode`. It specifies all 5 rounding modes defined by IEEE-754 and is used in `APFloat` implementation. 2. `clang::LangOptions::FPRoundingModeKind`. It specifies 4 of 5 IEEE-754 rounding modes and a special value for dynamic rounding mode. It is used in clang frontend. 3. `llvm::fp::RoundingMode`. Defines the same values as `clang::LangOptions::FPRoundingModeKind` but in different order. It is used to specify rounding mode in in IR and functions that operate IR. 4. Rounding mode representation used by `FLT_ROUNDS` (C11, 5.2.4.2.2p7). Besides constants for rounding mode it also uses a special value to indicate error. It is convenient to use in intrinsic functions, as it represents platform-independent representation for rounding mode. In this role it is used in some pending patches. 5. Values like `FE_DOWNWARD` and other, which specify rounding mode in library calls `fesetround` and `fegetround`. Often they represent bits of some control register, so they are target-dependent. The same names (not values) and a special name `FE_DYNAMIC` are used in `#pragma STDC FENV_ROUND`. The first 4 sets of constants are target independent and could have the same numerical representation. It would simplify conversion between the representations. Also now `clang::LangOptions::FPRoundingModeKind` and `llvm::fp::RoundingMode` do not contain the value for IEEE-754 rounding direction `roundTiesToAway`, although it is supported natively on some targets. This change defines all the rounding mode type via one `llvm::RoundingMode`, which also contains rounding mode for IEEE rounding direction `roundTiesToAway`. Differential Revision: https://reviews.llvm.org/D77379
2020-03-26 14:51:09 +07:00
#include "llvm/ADT/FloatingPointMode.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/Linker/Linker.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptSpecifier.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Option/Option.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Remarks/HotnessThresholdParser.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/VersionTuple.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetOptions.h"
#include <algorithm>
#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstring>
#include <memory>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
using namespace clang;
using namespace driver;
using namespace options;
using namespace llvm::opt;
//===----------------------------------------------------------------------===//
// Initialization.
//===----------------------------------------------------------------------===//
CompilerInvocationBase::CompilerInvocationBase()
: LangOpts(new LangOptions()), TargetOpts(new TargetOptions()),
DiagnosticOpts(new DiagnosticOptions()),
HeaderSearchOpts(new HeaderSearchOptions()),
PreprocessorOpts(new PreprocessorOptions()) {}
CompilerInvocationBase::CompilerInvocationBase(const CompilerInvocationBase &X)
: LangOpts(new LangOptions(*X.getLangOpts())),
TargetOpts(new TargetOptions(X.getTargetOpts())),
DiagnosticOpts(new DiagnosticOptions(X.getDiagnosticOpts())),
HeaderSearchOpts(new HeaderSearchOptions(X.getHeaderSearchOpts())),
PreprocessorOpts(new PreprocessorOptions(X.getPreprocessorOpts())) {}
CompilerInvocationBase::~CompilerInvocationBase() = default;
//===----------------------------------------------------------------------===//
// Normalizers
//===----------------------------------------------------------------------===//
#define SIMPLE_ENUM_VALUE_TABLE
#include "clang/Driver/Options.inc"
#undef SIMPLE_ENUM_VALUE_TABLE
static llvm::Optional<bool>
normalizeSimpleFlag(OptSpecifier Opt, unsigned TableIndex, const ArgList &Args,
DiagnosticsEngine &Diags, bool &Success) {
if (Args.hasArg(Opt))
return true;
return None;
}
static Optional<bool> normalizeSimpleNegativeFlag(OptSpecifier Opt, unsigned,
const ArgList &Args,
DiagnosticsEngine &,
bool &Success) {
if (Args.hasArg(Opt))
return false;
return None;
}
/// The tblgen-erated code passes in a fifth parameter of an arbitrary type, but
/// denormalizeSimpleFlags never looks at it. Avoid bloating compile-time with
/// unnecessary template instantiations and just ignore it with a variadic
/// argument.
static void denormalizeSimpleFlag(SmallVectorImpl<const char *> &Args,
const char *Spelling,
CompilerInvocation::StringAllocator,
Option::OptionClass, unsigned, /*T*/...) {
Args.push_back(Spelling);
}
template <typename T> static constexpr bool is_uint64_t_convertible() {
return !std::is_same<T, uint64_t>::value &&
llvm::is_integral_or_enum<T>::value;
}
template <typename T,
std::enable_if_t<!is_uint64_t_convertible<T>(), bool> = false>
static auto makeFlagToValueNormalizer(T Value) {
return [Value](OptSpecifier Opt, unsigned, const ArgList &Args,
DiagnosticsEngine &, bool &Success) -> Optional<T> {
if (Args.hasArg(Opt))
return Value;
return None;
};
}
template <typename T,
std::enable_if_t<is_uint64_t_convertible<T>(), bool> = false>
static auto makeFlagToValueNormalizer(T Value) {
return makeFlagToValueNormalizer(uint64_t(Value));
}
static auto makeBooleanOptionNormalizer(bool Value, bool OtherValue,
OptSpecifier OtherOpt) {
return [Value, OtherValue, OtherOpt](OptSpecifier Opt, unsigned,
const ArgList &Args, DiagnosticsEngine &,
bool &Success) -> Optional<bool> {
if (const Arg *A = Args.getLastArg(Opt, OtherOpt)) {
return A->getOption().matches(Opt) ? Value : OtherValue;
}
return None;
};
}
static auto makeBooleanOptionDenormalizer(bool Value) {
return [Value](SmallVectorImpl<const char *> &Args, const char *Spelling,
CompilerInvocation::StringAllocator, Option::OptionClass,
unsigned, bool KeyPath) {
if (KeyPath == Value)
Args.push_back(Spelling);
};
}
static void denormalizeStringImpl(SmallVectorImpl<const char *> &Args,
const char *Spelling,
CompilerInvocation::StringAllocator SA,
Option::OptionClass OptClass, unsigned,
const Twine &Value) {
switch (OptClass) {
case Option::SeparateClass:
case Option::JoinedOrSeparateClass:
Args.push_back(Spelling);
Args.push_back(SA(Value));
break;
case Option::JoinedClass:
case Option::CommaJoinedClass:
Args.push_back(SA(Twine(Spelling) + Value));
break;
default:
llvm_unreachable("Cannot denormalize an option with option class "
"incompatible with string denormalization.");
}
}
template <typename T>
static void
denormalizeString(SmallVectorImpl<const char *> &Args, const char *Spelling,
CompilerInvocation::StringAllocator SA,
Option::OptionClass OptClass, unsigned TableIndex, T Value) {
denormalizeStringImpl(Args, Spelling, SA, OptClass, TableIndex, Twine(Value));
}
static Optional<SimpleEnumValue>
findValueTableByName(const SimpleEnumValueTable &Table, StringRef Name) {
for (int I = 0, E = Table.Size; I != E; ++I)
if (Name == Table.Table[I].Name)
return Table.Table[I];
return None;
}
static Optional<SimpleEnumValue>
findValueTableByValue(const SimpleEnumValueTable &Table, unsigned Value) {
for (int I = 0, E = Table.Size; I != E; ++I)
if (Value == Table.Table[I].Value)
return Table.Table[I];
return None;
}
static llvm::Optional<unsigned>
normalizeSimpleEnum(OptSpecifier Opt, unsigned TableIndex, const ArgList &Args,
DiagnosticsEngine &Diags, bool &Success) {
assert(TableIndex < SimpleEnumValueTablesSize);
const SimpleEnumValueTable &Table = SimpleEnumValueTables[TableIndex];
auto *Arg = Args.getLastArg(Opt);
if (!Arg)
return None;
StringRef ArgValue = Arg->getValue();
if (auto MaybeEnumVal = findValueTableByName(Table, ArgValue))
return MaybeEnumVal->Value;
Success = false;
Diags.Report(diag::err_drv_invalid_value)
<< Arg->getAsString(Args) << ArgValue;
return None;
}
static void denormalizeSimpleEnumImpl(SmallVectorImpl<const char *> &Args,
const char *Spelling,
CompilerInvocation::StringAllocator SA,
Option::OptionClass OptClass,
unsigned TableIndex, unsigned Value) {
assert(TableIndex < SimpleEnumValueTablesSize);
const SimpleEnumValueTable &Table = SimpleEnumValueTables[TableIndex];
if (auto MaybeEnumVal = findValueTableByValue(Table, Value)) {
denormalizeString(Args, Spelling, SA, OptClass, TableIndex,
MaybeEnumVal->Name);
} else {
llvm_unreachable("The simple enum value was not correctly defined in "
"the tablegen option description");
}
}
template <typename T>
static void denormalizeSimpleEnum(SmallVectorImpl<const char *> &Args,
const char *Spelling,
CompilerInvocation::StringAllocator SA,
Option::OptionClass OptClass,
unsigned TableIndex, T Value) {
return denormalizeSimpleEnumImpl(Args, Spelling, SA, OptClass, TableIndex,
static_cast<unsigned>(Value));
}
static Optional<std::string> normalizeString(OptSpecifier Opt, int TableIndex,
const ArgList &Args,
DiagnosticsEngine &Diags,
bool &Success) {
auto *Arg = Args.getLastArg(Opt);
if (!Arg)
return None;
return std::string(Arg->getValue());
}
template <typename IntTy>
static Optional<IntTy>
normalizeStringIntegral(OptSpecifier Opt, int, const ArgList &Args,
DiagnosticsEngine &Diags, bool &Success) {
auto *Arg = Args.getLastArg(Opt);
if (!Arg)
return None;
IntTy Res;
if (StringRef(Arg->getValue()).getAsInteger(0, Res)) {
Success = false;
Diags.Report(diag::err_drv_invalid_int_value)
<< Arg->getAsString(Args) << Arg->getValue();
return None;
}
return Res;
}
static Optional<std::vector<std::string>>
normalizeStringVector(OptSpecifier Opt, int, const ArgList &Args,
DiagnosticsEngine &, bool &Success) {
return Args.getAllArgValues(Opt);
}
static void denormalizeStringVector(SmallVectorImpl<const char *> &Args,
const char *Spelling,
CompilerInvocation::StringAllocator SA,
Option::OptionClass OptClass,
unsigned TableIndex,
const std::vector<std::string> &Values) {
switch (OptClass) {
case Option::CommaJoinedClass: {
std::string CommaJoinedValue;
if (!Values.empty()) {
CommaJoinedValue.append(Values.front());
for (const std::string &Value : llvm::drop_begin(Values, 1)) {
CommaJoinedValue.append(",");
CommaJoinedValue.append(Value);
}
}
denormalizeString(Args, Spelling, SA, Option::OptionClass::JoinedClass,
TableIndex, CommaJoinedValue);
break;
}
case Option::JoinedClass:
case Option::SeparateClass:
case Option::JoinedOrSeparateClass:
for (const std::string &Value : Values)
denormalizeString(Args, Spelling, SA, OptClass, TableIndex, Value);
break;
default:
llvm_unreachable("Cannot denormalize an option with option class "
"incompatible with string vector denormalization.");
}
}
static Optional<std::string> normalizeTriple(OptSpecifier Opt, int TableIndex,
const ArgList &Args,
DiagnosticsEngine &Diags,
bool &Success) {
auto *Arg = Args.getLastArg(Opt);
if (!Arg)
return None;
return llvm::Triple::normalize(Arg->getValue());
}
template <typename T, typename U>
static T mergeForwardValue(T KeyPath, U Value) {
return static_cast<T>(Value);
}
template <typename T, typename U> static T mergeMaskValue(T KeyPath, U Value) {
return KeyPath | Value;
}
template <typename T> static T extractForwardValue(T KeyPath) {
return KeyPath;
}
template <typename T, typename U, U Value>
static T extractMaskValue(T KeyPath) {
return ((KeyPath & Value) == Value) ? static_cast<T>(Value) : T();
}
#define PARSE_OPTION_WITH_MARSHALLING(ARGS, DIAGS, SUCCESS, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, \
NORMALIZER, MERGER, TABLE_INDEX) \
if ((FLAGS)&options::CC1Option) { \
KEYPATH = MERGER(KEYPATH, DEFAULT_VALUE); \
if (IMPLIED_CHECK) \
KEYPATH = MERGER(KEYPATH, IMPLIED_VALUE); \
if (SHOULD_PARSE) \
if (auto MaybeValue = \
NORMALIZER(OPT_##ID, TABLE_INDEX, ARGS, DIAGS, SUCCESS)) \
KEYPATH = \
MERGER(KEYPATH, static_cast<decltype(KEYPATH)>(*MaybeValue)); \
}
// Capture the extracted value as a lambda argument to avoid potential issues
// with lifetime extension of the reference.
#define GENERATE_OPTION_WITH_MARSHALLING( \
ARGS, STRING_ALLOCATOR, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, \
TABLE_INDEX) \
if ((FLAGS)&options::CC1Option) { \
[&](const auto &Extracted) { \
if (ALWAYS_EMIT || \
(Extracted != \
static_cast<decltype(KEYPATH)>((IMPLIED_CHECK) ? (IMPLIED_VALUE) \
: (DEFAULT_VALUE)))) \
DENORMALIZER(ARGS, SPELLING, STRING_ALLOCATOR, Option::KIND##Class, \
TABLE_INDEX, Extracted); \
}(EXTRACTOR(KEYPATH)); \
}
static const StringRef GetInputKindName(InputKind IK);
static void FixupInvocation(CompilerInvocation &Invocation,
DiagnosticsEngine &Diags, const InputArgList &Args,
InputKind IK) {
LangOptions &LangOpts = *Invocation.getLangOpts();
CodeGenOptions &CodeGenOpts = Invocation.getCodeGenOpts();
TargetOptions &TargetOpts = Invocation.getTargetOpts();
FrontendOptions &FrontendOpts = Invocation.getFrontendOpts();
CodeGenOpts.XRayInstrumentFunctions = LangOpts.XRayInstrument;
CodeGenOpts.XRayAlwaysEmitCustomEvents = LangOpts.XRayAlwaysEmitCustomEvents;
CodeGenOpts.XRayAlwaysEmitTypedEvents = LangOpts.XRayAlwaysEmitTypedEvents;
CodeGenOpts.DisableFree = FrontendOpts.DisableFree;
FrontendOpts.GenerateGlobalModuleIndex = FrontendOpts.UseGlobalModuleIndex;
LangOpts.ForceEmitVTables = CodeGenOpts.ForceEmitVTables;
LangOpts.SpeculativeLoadHardening = CodeGenOpts.SpeculativeLoadHardening;
LangOpts.CurrentModule = LangOpts.ModuleName;
llvm::Triple T(TargetOpts.Triple);
llvm::Triple::ArchType Arch = T.getArch();
CodeGenOpts.CodeModel = TargetOpts.CodeModel;
if (LangOpts.getExceptionHandling() != llvm::ExceptionHandling::None &&
T.isWindowsMSVCEnvironment())
Diags.Report(diag::err_fe_invalid_exception_model)
<< static_cast<unsigned>(LangOpts.getExceptionHandling()) << T.str();
if (LangOpts.AppleKext && !LangOpts.CPlusPlus)
Diags.Report(diag::warn_c_kext);
if (Args.hasArg(OPT_fconcepts_ts))
Diags.Report(diag::warn_fe_concepts_ts_flag);
if (LangOpts.NewAlignOverride &&
!llvm::isPowerOf2_32(LangOpts.NewAlignOverride)) {
Arg *A = Args.getLastArg(OPT_fnew_alignment_EQ);
Diags.Report(diag::err_fe_invalid_alignment)
<< A->getAsString(Args) << A->getValue();
LangOpts.NewAlignOverride = 0;
}
if (Args.hasArg(OPT_fgnu89_inline) && LangOpts.CPlusPlus)
Diags.Report(diag::err_drv_argument_not_allowed_with)
<< "-fgnu89-inline" << GetInputKindName(IK);
if (Args.hasArg(OPT_fgpu_allow_device_init) && !LangOpts.HIP)
Diags.Report(diag::warn_ignored_hip_only_option)
<< Args.getLastArg(OPT_fgpu_allow_device_init)->getAsString(Args);
if (Args.hasArg(OPT_gpu_max_threads_per_block_EQ) && !LangOpts.HIP)
Diags.Report(diag::warn_ignored_hip_only_option)
<< Args.getLastArg(OPT_gpu_max_threads_per_block_EQ)->getAsString(Args);
// -cl-strict-aliasing needs to emit diagnostic in the case where CL > 1.0.
// This option should be deprecated for CL > 1.0 because
// this option was added for compatibility with OpenCL 1.0.
if (Args.getLastArg(OPT_cl_strict_aliasing) && LangOpts.OpenCLVersion > 100)
Diags.Report(diag::warn_option_invalid_ocl_version)
<< LangOpts.getOpenCLVersionTuple().getAsString()
<< Args.getLastArg(OPT_cl_strict_aliasing)->getAsString(Args);
if (Arg *A = Args.getLastArg(OPT_fdefault_calling_conv_EQ)) {
auto DefaultCC = LangOpts.getDefaultCallingConv();
bool emitError = (DefaultCC == LangOptions::DCC_FastCall ||
DefaultCC == LangOptions::DCC_StdCall) &&
Arch != llvm::Triple::x86;
emitError |= (DefaultCC == LangOptions::DCC_VectorCall ||
DefaultCC == LangOptions::DCC_RegCall) &&
!T.isX86();
if (emitError)
Diags.Report(diag::err_drv_argument_not_allowed_with)
<< A->getSpelling() << T.getTriple();
}
if (!CodeGenOpts.ProfileRemappingFile.empty() && CodeGenOpts.LegacyPassManager)
Diags.Report(diag::err_drv_argument_only_allowed_with)
<< Args.getLastArg(OPT_fprofile_remapping_file_EQ)->getAsString(Args)
<< "-fno-legacy-pass-manager";
}
//===----------------------------------------------------------------------===//
// Deserialization (from args)
//===----------------------------------------------------------------------===//
static unsigned getOptimizationLevel(ArgList &Args, InputKind IK,
DiagnosticsEngine &Diags) {
unsigned DefaultOpt = llvm::CodeGenOpt::None;
if (IK.getLanguage() == Language::OpenCL && !Args.hasArg(OPT_cl_opt_disable))
DefaultOpt = llvm::CodeGenOpt::Default;
if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
if (A->getOption().matches(options::OPT_O0))
return llvm::CodeGenOpt::None;
if (A->getOption().matches(options::OPT_Ofast))
return llvm::CodeGenOpt::Aggressive;
assert(A->getOption().matches(options::OPT_O));
StringRef S(A->getValue());
if (S == "s" || S == "z")
return llvm::CodeGenOpt::Default;
if (S == "g")
return llvm::CodeGenOpt::Less;
return getLastArgIntValue(Args, OPT_O, DefaultOpt, Diags);
}
return DefaultOpt;
}
static unsigned getOptimizationLevelSize(ArgList &Args) {
if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
if (A->getOption().matches(options::OPT_O)) {
switch (A->getValue()[0]) {
default:
return 0;
case 's':
return 1;
case 'z':
return 2;
}
}
}
return 0;
}
static void GenerateArg(SmallVectorImpl<const char *> &Args,
llvm::opt::OptSpecifier OptSpecifier,
CompilerInvocation::StringAllocator SA) {
Option Opt = getDriverOptTable().getOption(OptSpecifier);
denormalizeSimpleFlag(Args, SA(Opt.getPrefix() + Opt.getName()), SA,
Option::OptionClass::FlagClass, 0);
}
static void GenerateArg(SmallVectorImpl<const char *> &Args,
llvm::opt::OptSpecifier OptSpecifier,
const Twine &Value,
CompilerInvocation::StringAllocator SA) {
Option Opt = getDriverOptTable().getOption(OptSpecifier);
denormalizeString(Args, SA(Opt.getPrefix() + Opt.getName()), SA,
Opt.getKind(), 0, Value);
}
// Parse subset of command line arguments into a member of CompilerInvocation.
using ParseFn = llvm::function_ref<bool(CompilerInvocation &, ArgList &,
DiagnosticsEngine &)>;
// Generate part of command line arguments from a member of CompilerInvocation.
using GenerateFn = llvm::function_ref<void(
CompilerInvocation &, SmallVectorImpl<const char *> &,
CompilerInvocation::StringAllocator)>;
// Swap between dummy/real instance of a CompilerInvocation member.
using SwapOptsFn = llvm::function_ref<void(CompilerInvocation &)>;
// Performs round-trip of command line arguments if OriginalArgs contain
// "-round-trip-args". Effectively runs the Parse function for a part of
// CompilerInvocation on command line arguments that were already once parsed
// and generated. This is used to check the Generate function produces arguments
// that are semantically equivalent to those that were used to create
// CompilerInvocation.
static bool RoundTrip(ParseFn Parse, GenerateFn Generate, SwapOptsFn SwapOpts,
CompilerInvocation &Res, ArgList &OriginalArgs,
DiagnosticsEngine &Diags, StringRef OptsName) {
// FIXME: Switch to '#ifndef NDEBUG' when possible.
#ifdef CLANG_ROUND_TRIP_CC1_ARGS
bool DoRoundTripDefault = true;
#else
bool DoRoundTripDefault = false;
#endif
bool DoRoundTrip = OriginalArgs.hasFlag(
OPT_round_trip_args, OPT_no_round_trip_args, DoRoundTripDefault);
// If round-trip was not requested, simply run the parser with the original
// options and diagnostics.
if (!DoRoundTrip)
return Parse(Res, OriginalArgs, Diags);
// Serializes quoted (and potentially escaped) arguments.
auto SerializeArgs = [](ArgStringList &Args) {
std::string Buffer;
llvm::raw_string_ostream OS(Buffer);
for (const char *Arg : Args) {
llvm::sys::printArg(OS, Arg, /*Quote=*/true);
OS << ' ';
}
OS.flush();
return Buffer;
};
OriginalArgs.clearQueriedOpts();
// Setup a dummy DiagnosticsEngine.
DiagnosticsEngine DummyDiags(new DiagnosticIDs(), new DiagnosticOptions());
DummyDiags.setClient(new TextDiagnosticBuffer());
// Run the first parse on the original arguments with dummy options and
// diagnostics.
SwapOpts(Res);
if (!Parse(Res, OriginalArgs, DummyDiags) ||
DummyDiags.getNumWarnings() != 0) {
// If the first parse did not succeed, it must be user mistake (invalid
// command line arguments). We won't be able to generate arguments that
// would reproduce the same result. Let's fail again with the original
// options and diagnostics, so all side-effects of parsing are visible.
unsigned NumWarningsBefore = Diags.getNumWarnings();
SwapOpts(Res);
auto Success = Parse(Res, OriginalArgs, Diags);
if (!Success || Diags.getNumWarnings() != NumWarningsBefore)
return Success;
// Parse with original options and diagnostics succeeded even though it
// shouldn't have. Something is off.
Diags.Report(diag::err_cc1_round_trip_fail_then_ok) << OptsName;
ArgStringList OriginalStrings;
OriginalArgs.AddAllArgsExcept(OriginalStrings, {});
Diags.Report(diag::note_cc1_round_trip_original)
<< OptsName << SerializeArgs(OriginalStrings);
return false;
}
// Setup string allocator.
llvm::BumpPtrAllocator Alloc;
llvm::StringSaver StringPool(Alloc);
auto SA = [&StringPool](const Twine &Arg) {
return StringPool.save(Arg).data();
};
// Generate arguments. First simply copy any arguments the parser did not
// query. Then, use the Generate function that uses the CompilerInvocation
// options instance as the source of truth. If Generate is the inverse of
// Parse, the newly generated arguments must have the same semantics as the
// original.
ArgStringList GeneratedStrings1;
OriginalArgs.AddAllArgsExcept(GeneratedStrings1,
OriginalArgs.getQueriedOpts());
Generate(Res, GeneratedStrings1, SA);
// Process the generated arguments.
unsigned MissingArgIndex1, MissingArgCount1;
InputArgList GeneratedArgs1 =
getDriverOptTable().ParseArgs(GeneratedStrings1, MissingArgIndex1,
MissingArgCount1, options::CC1Option);
// TODO: Once we're responsible for generating all arguments, check that we
// didn't create any unknown options or omitted required values.
// Run the second parse, now on the generated arguments, and with the original
// options and diagnostics. The result is what we will end up using for the
// rest of compilation, so if Generate is not inverse of Parse, something down
// the line will break.
SwapOpts(Res);
bool Success2 = Parse(Res, GeneratedArgs1, Diags);
// The first parse on original arguments succeeded, but second parse of
// generated arguments failed. Something must be wrong with the generator.
if (!Success2) {
Diags.Report(diag::err_cc1_round_trip_ok_then_fail) << OptsName;
Diags.Report(diag::note_cc1_round_trip_generated)
<< OptsName << 1 << SerializeArgs(GeneratedStrings1);
return false;
}
// Generate arguments again, this time from the options we will end up using
// for the rest of the compilation.
ArgStringList GeneratedStrings2;
GeneratedArgs1.AddAllArgsExcept(GeneratedStrings2,
GeneratedArgs1.getQueriedOpts());
Generate(Res, GeneratedStrings2, SA);
// Compares two lists of generated arguments.
auto Equal = [](const ArgStringList &A, const ArgStringList &B) {
return std::equal(A.begin(), A.end(), B.begin(), B.end(),
[](const char *AElem, const char *BElem) {
return StringRef(AElem) == StringRef(BElem);
});
};
// If we generated different arguments from what we assume are two
// semantically equivalent CompilerInvocations, the Generate function may
// be non-deterministic.
if (!Equal(GeneratedStrings1, GeneratedStrings2)) {
Diags.Report(diag::err_cc1_round_trip_mismatch) << OptsName;
Diags.Report(diag::note_cc1_round_trip_generated)
<< OptsName << 1 << SerializeArgs(GeneratedStrings1);
Diags.Report(diag::note_cc1_round_trip_generated)
<< OptsName << 2 << SerializeArgs(GeneratedStrings2);
return false;
}
Diags.Report(diag::remark_cc1_round_trip_generated)
<< OptsName << 1 << SerializeArgs(GeneratedStrings1);
Diags.Report(diag::remark_cc1_round_trip_generated)
<< OptsName << 2 << SerializeArgs(GeneratedStrings2);
return Success2;
}
static void addDiagnosticArgs(ArgList &Args, OptSpecifier Group,
OptSpecifier GroupWithValue,
std::vector<std::string> &Diagnostics) {
for (auto *A : Args.filtered(Group)) {
if (A->getOption().getKind() == Option::FlagClass) {
// The argument is a pure flag (such as OPT_Wall or OPT_Wdeprecated). Add
// its name (minus the "W" or "R" at the beginning) to the diagnostics.
Diagnostics.push_back(
std::string(A->getOption().getName().drop_front(1)));
} else if (A->getOption().matches(GroupWithValue)) {
// This is -Wfoo= or -Rfoo=, where foo is the name of the diagnostic
// group. Add only the group name to the diagnostics.
Diagnostics.push_back(
std::string(A->getOption().getName().drop_front(1).rtrim("=-")));
} else {
// Otherwise, add its value (for OPT_W_Joined and similar).
Diagnostics.push_back(A->getValue());
}
}
}
// Parse the Static Analyzer configuration. If \p Diags is set to nullptr,
// it won't verify the input.
static void parseAnalyzerConfigs(AnalyzerOptions &AnOpts,
DiagnosticsEngine *Diags);
static void getAllNoBuiltinFuncValues(ArgList &Args,
std::vector<std::string> &Funcs) {
std::vector<std::string> Values = Args.getAllArgValues(OPT_fno_builtin_);
auto BuiltinEnd = llvm::partition(Values, [](const std::string FuncName) {
return Builtin::Context::isBuiltinFunc(FuncName);
});
Funcs.insert(Funcs.end(), Values.begin(), BuiltinEnd);
}
static void GenerateAnalyzerArgs(AnalyzerOptions &Opts,
SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA) {
const AnalyzerOptions *AnalyzerOpts = &Opts;
#define ANALYZER_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef ANALYZER_OPTION_WITH_MARSHALLING
if (Opts.AnalysisStoreOpt != RegionStoreModel) {
switch (Opts.AnalysisStoreOpt) {
#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) \
case NAME##Model: \
GenerateArg(Args, OPT_analyzer_store, CMDFLAG, SA); \
break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
default:
llvm_unreachable("Tried to generate unknown analysis store.");
}
}
if (Opts.AnalysisConstraintsOpt != RangeConstraintsModel) {
switch (Opts.AnalysisConstraintsOpt) {
#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) \
case NAME##Model: \
GenerateArg(Args, OPT_analyzer_constraints, CMDFLAG, SA); \
break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
default:
llvm_unreachable("Tried to generate unknown analysis constraint.");
}
}
if (Opts.AnalysisDiagOpt != PD_HTML) {
switch (Opts.AnalysisDiagOpt) {
#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN) \
case PD_##NAME: \
GenerateArg(Args, OPT_analyzer_output, CMDFLAG, SA); \
break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
default:
llvm_unreachable("Tried to generate unknown analysis diagnostic client.");
}
}
if (Opts.AnalysisPurgeOpt != PurgeStmt) {
switch (Opts.AnalysisPurgeOpt) {
#define ANALYSIS_PURGE(NAME, CMDFLAG, DESC) \
case NAME: \
GenerateArg(Args, OPT_analyzer_purge, CMDFLAG, SA); \
break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
default:
llvm_unreachable("Tried to generate unknown analysis purge mode.");
}
}
if (Opts.InliningMode != NoRedundancy) {
switch (Opts.InliningMode) {
#define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC) \
case NAME: \
GenerateArg(Args, OPT_analyzer_inlining_mode, CMDFLAG, SA); \
break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
default:
llvm_unreachable("Tried to generate unknown analysis inlining mode.");
}
}
for (const auto &CP : Opts.CheckersAndPackages) {
OptSpecifier Opt =
CP.second ? OPT_analyzer_checker : OPT_analyzer_disable_checker;
GenerateArg(Args, Opt, CP.first, SA);
}
AnalyzerOptions ConfigOpts;
parseAnalyzerConfigs(ConfigOpts, nullptr);
for (const auto &C : Opts.Config) {
// Don't generate anything that came from parseAnalyzerConfigs. It would be
// redundant and may not be valid on the command line.
auto Entry = ConfigOpts.Config.find(C.getKey());
if (Entry != ConfigOpts.Config.end() && Entry->getValue() == C.getValue())
continue;
GenerateArg(Args, OPT_analyzer_config, C.getKey() + "=" + C.getValue(), SA);
}
// Nothing to generate for FullCompilerInvocation.
}
static bool ParseAnalyzerArgsImpl(AnalyzerOptions &Opts, ArgList &Args,
DiagnosticsEngine &Diags) {
AnalyzerOptions *AnalyzerOpts = &Opts;
bool Success = true;
#define ANALYZER_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef ANALYZER_OPTION_WITH_MARSHALLING
if (Arg *A = Args.getLastArg(OPT_analyzer_store)) {
StringRef Name = A->getValue();
AnalysisStores Value = llvm::StringSwitch<AnalysisStores>(Name)
#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) \
.Case(CMDFLAG, NAME##Model)
#include "clang/StaticAnalyzer/Core/Analyses.def"
.Default(NumStores);
if (Value == NumStores) {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << Name;
Success = false;
} else {
Opts.AnalysisStoreOpt = Value;
}
}
if (Arg *A = Args.getLastArg(OPT_analyzer_constraints)) {
StringRef Name = A->getValue();
AnalysisConstraints Value = llvm::StringSwitch<AnalysisConstraints>(Name)
#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) \
.Case(CMDFLAG, NAME##Model)
#include "clang/StaticAnalyzer/Core/Analyses.def"
.Default(NumConstraints);
if (Value == NumConstraints) {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << Name;
Success = false;
} else {
Opts.AnalysisConstraintsOpt = Value;
}
}
if (Arg *A = Args.getLastArg(OPT_analyzer_output)) {
StringRef Name = A->getValue();
AnalysisDiagClients Value = llvm::StringSwitch<AnalysisDiagClients>(Name)
#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN) \
.Case(CMDFLAG, PD_##NAME)
#include "clang/StaticAnalyzer/Core/Analyses.def"
.Default(NUM_ANALYSIS_DIAG_CLIENTS);
if (Value == NUM_ANALYSIS_DIAG_CLIENTS) {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << Name;
Success = false;
} else {
Opts.AnalysisDiagOpt = Value;
}
}
if (Arg *A = Args.getLastArg(OPT_analyzer_purge)) {
StringRef Name = A->getValue();
AnalysisPurgeMode Value = llvm::StringSwitch<AnalysisPurgeMode>(Name)
#define ANALYSIS_PURGE(NAME, CMDFLAG, DESC) \
.Case(CMDFLAG, NAME)
#include "clang/StaticAnalyzer/Core/Analyses.def"
.Default(NumPurgeModes);
if (Value == NumPurgeModes) {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << Name;
Success = false;
} else {
Opts.AnalysisPurgeOpt = Value;
}
}
if (Arg *A = Args.getLastArg(OPT_analyzer_inlining_mode)) {
StringRef Name = A->getValue();
AnalysisInliningMode Value = llvm::StringSwitch<AnalysisInliningMode>(Name)
#define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC) \
.Case(CMDFLAG, NAME)
#include "clang/StaticAnalyzer/Core/Analyses.def"
.Default(NumInliningModes);
if (Value == NumInliningModes) {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << Name;
Success = false;
} else {
Opts.InliningMode = Value;
}
}
Opts.CheckersAndPackages.clear();
for (const Arg *A :
Args.filtered(OPT_analyzer_checker, OPT_analyzer_disable_checker)) {
A->claim();
bool IsEnabled = A->getOption().getID() == OPT_analyzer_checker;
// We can have a list of comma separated checker names, e.g:
// '-analyzer-checker=cocoa,unix'
StringRef CheckerAndPackageList = A->getValue();
SmallVector<StringRef, 16> CheckersAndPackages;
CheckerAndPackageList.split(CheckersAndPackages, ",");
for (const StringRef &CheckerOrPackage : CheckersAndPackages)
2020-01-29 02:48:15 +01:00
Opts.CheckersAndPackages.emplace_back(std::string(CheckerOrPackage),
IsEnabled);
}
// Go through the analyzer configuration options.
for (const auto *A : Args.filtered(OPT_analyzer_config)) {
// We can have a list of comma separated config names, e.g:
// '-analyzer-config key1=val1,key2=val2'
StringRef configList = A->getValue();
SmallVector<StringRef, 4> configVals;
configList.split(configVals, ",");
for (const auto &configVal : configVals) {
StringRef key, val;
std::tie(key, val) = configVal.split("=");
if (val.empty()) {
Diags.Report(SourceLocation(),
diag::err_analyzer_config_no_value) << configVal;
Success = false;
break;
}
if (val.find('=') != StringRef::npos) {
Diags.Report(SourceLocation(),
diag::err_analyzer_config_multiple_values)
<< configVal;
Success = false;
break;
}
// TODO: Check checker options too, possibly in CheckerRegistry.
// Leave unknown non-checker configs unclaimed.
if (!key.contains(":") && Opts.isUnknownAnalyzerConfig(key)) {
if (Opts.ShouldEmitErrorsOnInvalidConfigValue) {
Diags.Report(diag::err_analyzer_config_unknown) << key;
Success = false;
}
continue;
}
A->claim();
Opts.Config[key] = std::string(val);
}
}
if (Opts.ShouldEmitErrorsOnInvalidConfigValue)
parseAnalyzerConfigs(Opts, &Diags);
else
parseAnalyzerConfigs(Opts, nullptr);
llvm::raw_string_ostream os(Opts.FullCompilerInvocation);
for (unsigned i = 0; i < Args.getNumInputArgStrings(); ++i) {
if (i != 0)
os << " ";
os << Args.getArgString(i);
}
os.flush();
return Success;
}
static bool ParseAnalyzerArgs(CompilerInvocation &Res, AnalyzerOptions &Opts,
ArgList &Args, DiagnosticsEngine &Diags) {
auto DummyOpts = IntrusiveRefCntPtr<AnalyzerOptions>(new AnalyzerOptions());
return RoundTrip(
[](CompilerInvocation &Res, ArgList &Args, DiagnosticsEngine &Diags) {
return ParseAnalyzerArgsImpl(*Res.getAnalyzerOpts(), Args, Diags);
},
[](CompilerInvocation &Res, SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA) {
GenerateAnalyzerArgs(*Res.getAnalyzerOpts(), Args, SA);
},
[&DummyOpts](CompilerInvocation &Res) {
Res.getAnalyzerOpts().swap(DummyOpts);
},
Res, Args, Diags, "AnalyzerOptions");
}
static StringRef getStringOption(AnalyzerOptions::ConfigTable &Config,
StringRef OptionName, StringRef DefaultVal) {
return Config.insert({OptionName, std::string(DefaultVal)}).first->second;
}
static void initOption(AnalyzerOptions::ConfigTable &Config,
DiagnosticsEngine *Diags,
StringRef &OptionField, StringRef Name,
StringRef DefaultVal) {
// String options may be known to invalid (e.g. if the expected string is a
// file name, but the file does not exist), those will have to be checked in
// parseConfigs.
OptionField = getStringOption(Config, Name, DefaultVal);
}
static void initOption(AnalyzerOptions::ConfigTable &Config,
DiagnosticsEngine *Diags,
bool &OptionField, StringRef Name, bool DefaultVal) {
auto PossiblyInvalidVal = llvm::StringSwitch<Optional<bool>>(
getStringOption(Config, Name, (DefaultVal ? "true" : "false")))
.Case("true", true)
.Case("false", false)
.Default(None);
if (!PossiblyInvalidVal) {
if (Diags)
Diags->Report(diag::err_analyzer_config_invalid_input)
<< Name << "a boolean";
else
OptionField = DefaultVal;
} else
OptionField = PossiblyInvalidVal.getValue();
}
static void initOption(AnalyzerOptions::ConfigTable &Config,
DiagnosticsEngine *Diags,
unsigned &OptionField, StringRef Name,
unsigned DefaultVal) {
OptionField = DefaultVal;
bool HasFailed = getStringOption(Config, Name, std::to_string(DefaultVal))
[analyzer][NFC] Reimplement checker options TL;DR: * Add checker and package options to the TableGen files * Added a new class called CmdLineOption, and both Package and Checker recieved a list<CmdLineOption> field. * Added every existing checker and package option to Checkers.td. * The CheckerRegistry class * Received some comments to most of it's inline classes * Received the CmdLineOption and PackageInfo inline classes, a list of CmdLineOption was added to CheckerInfo and PackageInfo * Added addCheckerOption and addPackageOption * Added a new field called Packages, used in addPackageOptions, filled up in addPackage Detailed description: In the last couple months, a lot of effort was put into tightening the analyzer's command line interface. The main issue is that it's spectacularly easy to mess up a lenghty enough invocation of the analyzer, and the user was given no warnings or errors at all in that case. We can divide the effort of resolving this into several chapters: * Non-checker analyzer configurations: Gather every analyzer configuration into a dedicated file. Emit errors for non-existent configurations or incorrect values. Be able to list these configurations. Tighten AnalyzerOptions interface to disallow making such a mistake in the future. * Fix the "Checker Naming Bug" by reimplementing checker dependencies: When cplusplus.InnerPointer was enabled, it implicitly registered unix.Malloc, which implicitly registered some sort of a modeling checker from the CStringChecker family. This resulted in all of these checker objects recieving the name "cplusplus.InnerPointer", making AnalyzerOptions asking for the wrong checker options from the command line: cplusplus.InnerPointer:Optimisic istead of unix.Malloc:Optimistic. This was resolved by making CheckerRegistry responsible for checker dependency handling, instead of checkers themselves. * Checker options: (this patch included!) Same as the first item, but for checkers. (+ minor fixes here and there, and everything else that is yet to come) There were several issues regarding checker options, that non-checker configurations didn't suffer from: checker plugins are loaded runtime, and they could add new checkers and new options, meaning that unlike for non-checker configurations, we can't collect every checker option purely by generating code. Also, as seen from the "Checker Naming Bug" issue raised above, they are very rarely used in practice, and all sorts of skeletons fell out of the closet while working on this project. They were extremely problematic for users as well, purely because of how long they were. Consider the following monster of a checker option: alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=false While we were able to verify whether the checker itself (the part before the colon) existed, any errors past that point were unreported, easily resulting in 7+ hours of analyses going to waste. This patch, similarly to how dependencies were reimplemented, uses TableGen to register checker options into Checkers.td, so that Checkers.inc now contains entries for both checker and package options. Using the preprocessor, Checkers.inc is converted into code in CheckerRegistry, adding every builtin (checkers and packages that have an entry in the Checkers.td file) checker and package option to the registry. The new addPackageOption and addCheckerOption functions expose the same functionality to statically-linked non-builtin and plugin checkers and packages as well. Emitting errors for incorrect user input, being able to list these options, and some other functionalies will land in later patches. Differential Revision: https://reviews.llvm.org/D57855 llvm-svn: 358752
2019-04-19 12:32:10 +00:00
.getAsInteger(0, OptionField);
if (Diags && HasFailed)
Diags->Report(diag::err_analyzer_config_invalid_input)
<< Name << "an unsigned";
}
static void parseAnalyzerConfigs(AnalyzerOptions &AnOpts,
DiagnosticsEngine *Diags) {
// TODO: There's no need to store the entire configtable, it'd be plenty
// enough tostore checker options.
#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \
initOption(AnOpts.Config, Diags, AnOpts.NAME, CMDFLAG, DEFAULT_VAL);
#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \
SHALLOW_VAL, DEEP_VAL) \
switch (AnOpts.getUserMode()) { \
case UMK_Shallow: \
initOption(AnOpts.Config, Diags, AnOpts.NAME, CMDFLAG, SHALLOW_VAL); \
break; \
case UMK_Deep: \
initOption(AnOpts.Config, Diags, AnOpts.NAME, CMDFLAG, DEEP_VAL); \
break; \
} \
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def"
#undef ANALYZER_OPTION
#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE
// At this point, AnalyzerOptions is configured. Let's validate some options.
// FIXME: Here we try to validate the silenced checkers or packages are valid.
// The current approach only validates the registered checkers which does not
// contain the runtime enabled checkers and optimally we would validate both.
if (!AnOpts.RawSilencedCheckersAndPackages.empty()) {
std::vector<StringRef> Checkers =
AnOpts.getRegisteredCheckers(/*IncludeExperimental=*/true);
std::vector<StringRef> Packages =
AnOpts.getRegisteredPackages(/*IncludeExperimental=*/true);
SmallVector<StringRef, 16> CheckersAndPackages;
AnOpts.RawSilencedCheckersAndPackages.split(CheckersAndPackages, ";");
for (const StringRef &CheckerOrPackage : CheckersAndPackages) {
if (Diags) {
bool IsChecker = CheckerOrPackage.contains('.');
bool IsValidName =
IsChecker
? llvm::find(Checkers, CheckerOrPackage) != Checkers.end()
: llvm::find(Packages, CheckerOrPackage) != Packages.end();
if (!IsValidName)
Diags->Report(diag::err_unknown_analyzer_checker_or_package)
<< CheckerOrPackage;
}
AnOpts.SilencedCheckersAndPackages.emplace_back(CheckerOrPackage);
}
}
if (!Diags)
return;
if (AnOpts.ShouldTrackConditionsDebug && !AnOpts.ShouldTrackConditions)
Diags->Report(diag::err_analyzer_config_invalid_input)
<< "track-conditions-debug" << "'track-conditions' to also be enabled";
if (!AnOpts.CTUDir.empty() && !llvm::sys::fs::is_directory(AnOpts.CTUDir))
Diags->Report(diag::err_analyzer_config_invalid_input) << "ctu-dir"
<< "a filename";
if (!AnOpts.ModelPath.empty() &&
!llvm::sys::fs::is_directory(AnOpts.ModelPath))
Diags->Report(diag::err_analyzer_config_invalid_input) << "model-path"
<< "a filename";
}
/// Create a new Regex instance out of the string value in \p RpassArg.
/// It returns the string and a pointer to the newly generated Regex instance.
static CodeGenOptions::RemarkPattern
GenerateOptimizationRemarkRegex(DiagnosticsEngine &Diags, ArgList &Args,
Arg *RpassArg) {
StringRef Val = RpassArg->getValue();
std::string RegexError;
std::shared_ptr<llvm::Regex> Pattern = std::make_shared<llvm::Regex>(Val);
if (!Pattern->isValid(RegexError)) {
Diags.Report(diag::err_drv_optimization_remark_pattern)
<< RegexError << RpassArg->getAsString(Args);
Pattern.reset();
}
return {std::string(Val), Pattern};
}
static bool parseDiagnosticLevelMask(StringRef FlagName,
const std::vector<std::string> &Levels,
DiagnosticsEngine &Diags,
DiagnosticLevelMask &M) {
bool Success = true;
for (const auto &Level : Levels) {
DiagnosticLevelMask const PM =
llvm::StringSwitch<DiagnosticLevelMask>(Level)
.Case("note", DiagnosticLevelMask::Note)
.Case("remark", DiagnosticLevelMask::Remark)
.Case("warning", DiagnosticLevelMask::Warning)
.Case("error", DiagnosticLevelMask::Error)
.Default(DiagnosticLevelMask::None);
if (PM == DiagnosticLevelMask::None) {
Success = false;
Diags.Report(diag::err_drv_invalid_value) << FlagName << Level;
}
M = M | PM;
}
return Success;
}
static void parseSanitizerKinds(StringRef FlagName,
const std::vector<std::string> &Sanitizers,
DiagnosticsEngine &Diags, SanitizerSet &S) {
for (const auto &Sanitizer : Sanitizers) {
SanitizerMask K = parseSanitizerValue(Sanitizer, /*AllowGroups=*/false);
if (K == SanitizerMask())
Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer;
else
S.set(K, true);
}
}
static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) {
SmallVector<StringRef, 4> Values;
serializeSanitizerSet(S, Values);
return Values;
}
static void parseXRayInstrumentationBundle(StringRef FlagName, StringRef Bundle,
ArgList &Args, DiagnosticsEngine &D,
XRayInstrSet &S) {
llvm::SmallVector<StringRef, 2> BundleParts;
llvm::SplitString(Bundle, BundleParts, ",");
for (const auto &B : BundleParts) {
auto Mask = parseXRayInstrValue(B);
if (Mask == XRayInstrKind::None)
if (B != "none")
D.Report(diag::err_drv_invalid_value) << FlagName << Bundle;
else
S.Mask = Mask;
else if (Mask == XRayInstrKind::All)
S.Mask = Mask;
else
S.set(Mask, true);
}
}
static std::string serializeXRayInstrumentationBundle(const XRayInstrSet &S) {
llvm::SmallVector<StringRef, 2> BundleParts;
serializeXRayInstrValue(S, BundleParts);
std::string Buffer;
llvm::raw_string_ostream OS(Buffer);
llvm::interleave(BundleParts, OS, [&OS](StringRef Part) { OS << Part; }, ",");
return OS.str();
}
// Set the profile kind using fprofile-instrument-use-path.
static void setPGOUseInstrumentor(CodeGenOptions &Opts,
const Twine &ProfileName) {
auto ReaderOrErr = llvm::IndexedInstrProfReader::create(ProfileName);
// In error, return silently and let Clang PGOUse report the error message.
if (auto E = ReaderOrErr.takeError()) {
llvm::consumeError(std::move(E));
Opts.setProfileUse(CodeGenOptions::ProfileClangInstr);
return;
}
std::unique_ptr<llvm::IndexedInstrProfReader> PGOReader =
std::move(ReaderOrErr.get());
if (PGOReader->isIRLevelProfile()) {
if (PGOReader->hasCSIRLevelProfile())
Opts.setProfileUse(CodeGenOptions::ProfileCSIRInstr);
else
Opts.setProfileUse(CodeGenOptions::ProfileIRInstr);
} else
Opts.setProfileUse(CodeGenOptions::ProfileClangInstr);
}
void CompilerInvocation::GenerateCodeGenArgs(
const CodeGenOptions &Opts, SmallVectorImpl<const char *> &Args,
StringAllocator SA, const llvm::Triple &T, const std::string &OutputFile,
const LangOptions *LangOpts) {
const CodeGenOptions &CodeGenOpts = Opts;
if (Opts.OptimizationLevel == 0)
GenerateArg(Args, OPT_O0, SA);
else
GenerateArg(Args, OPT_O, Twine(Opts.OptimizationLevel), SA);
#define CODEGEN_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef CODEGEN_OPTION_WITH_MARSHALLING
if (Opts.OptimizationLevel > 0) {
if (Opts.Inlining == CodeGenOptions::NormalInlining)
GenerateArg(Args, OPT_finline_functions, SA);
else if (Opts.Inlining == CodeGenOptions::OnlyHintInlining)
GenerateArg(Args, OPT_finline_hint_functions, SA);
else if (Opts.Inlining == CodeGenOptions::OnlyAlwaysInlining)
GenerateArg(Args, OPT_fno_inline, SA);
}
if (Opts.DirectAccessExternalData && LangOpts->PICLevel != 0)
GenerateArg(Args, OPT_fdirect_access_external_data, SA);
else if (!Opts.DirectAccessExternalData && LangOpts->PICLevel == 0)
GenerateArg(Args, OPT_fno_direct_access_external_data, SA);
Optional<StringRef> DebugInfoVal;
switch (Opts.DebugInfo) {
case codegenoptions::DebugLineTablesOnly:
DebugInfoVal = "line-tables-only";
break;
case codegenoptions::DebugDirectivesOnly:
DebugInfoVal = "line-directives-only";
break;
case codegenoptions::DebugInfoConstructor:
DebugInfoVal = "constructor";
break;
case codegenoptions::LimitedDebugInfo:
DebugInfoVal = "limited";
break;
case codegenoptions::FullDebugInfo:
DebugInfoVal = "standalone";
break;
case codegenoptions::UnusedTypeInfo:
DebugInfoVal = "unused-types";
break;
case codegenoptions::NoDebugInfo: // default value
DebugInfoVal = None;
break;
case codegenoptions::LocTrackingOnly: // implied value
DebugInfoVal = None;
break;
}
if (DebugInfoVal)
GenerateArg(Args, OPT_debug_info_kind_EQ, *DebugInfoVal, SA);
if (Opts.DebugInfo == codegenoptions::DebugInfoConstructor)
GenerateArg(Args, OPT_fuse_ctor_homing, SA);
for (const auto &Prefix : Opts.DebugPrefixMap)
GenerateArg(Args, OPT_fdebug_prefix_map_EQ,
Prefix.first + "=" + Prefix.second, SA);
for (const auto &Prefix : Opts.ProfilePrefixMap)
GenerateArg(Args, OPT_fprofile_prefix_map_EQ,
Prefix.first + "=" + Prefix.second, SA);
if (Opts.NewStructPathTBAA)
GenerateArg(Args, OPT_new_struct_path_tbaa, SA);
if (Opts.OptimizeSize == 1)
GenerateArg(Args, OPT_O, "s", SA);
else if (Opts.OptimizeSize == 2)
GenerateArg(Args, OPT_O, "z", SA);
// SimplifyLibCalls is set only in the absence of -fno-builtin and
// -ffreestanding. We'll consider that when generating them.
// NoBuiltinFuncs are generated by LangOptions.
if (Opts.UnrollLoops && Opts.OptimizationLevel <= 1)
GenerateArg(Args, OPT_funroll_loops, SA);
else if (!Opts.UnrollLoops && Opts.OptimizationLevel > 1)
GenerateArg(Args, OPT_fno_unroll_loops, SA);
if (!Opts.BinutilsVersion.empty())
GenerateArg(Args, OPT_fbinutils_version_EQ, Opts.BinutilsVersion, SA);
if (Opts.DebugNameTable ==
static_cast<unsigned>(llvm::DICompileUnit::DebugNameTableKind::GNU))
GenerateArg(Args, OPT_ggnu_pubnames, SA);
else if (Opts.DebugNameTable ==
static_cast<unsigned>(
llvm::DICompileUnit::DebugNameTableKind::Default))
GenerateArg(Args, OPT_gpubnames, SA);
// ProfileInstrumentUsePath is marshalled automatically, no need to generate
// it or PGOUseInstrumentor.
if (Opts.TimePasses) {
if (Opts.TimePassesPerRun)
GenerateArg(Args, OPT_ftime_report_EQ, "per-pass-run", SA);
else
GenerateArg(Args, OPT_ftime_report, SA);
}
if (Opts.FunctionSections &&
(Opts.BBSections == "none" || Opts.BBSections == "labels"))
GenerateArg(Args, OPT_ffunction_sections, SA);
if (Opts.PrepareForLTO && !Opts.PrepareForThinLTO)
GenerateArg(Args, OPT_flto, SA);
if (Opts.PrepareForThinLTO)
GenerateArg(Args, OPT_flto_EQ, "thin", SA);
if (!Opts.ThinLTOIndexFile.empty())
GenerateArg(Args, OPT_fthinlto_index_EQ, Opts.ThinLTOIndexFile, SA);
if (Opts.SaveTempsFilePrefix == OutputFile)
GenerateArg(Args, OPT_save_temps_EQ, "obj", SA);
StringRef MemProfileBasename("memprof.profraw");
if (!Opts.MemoryProfileOutput.empty()) {
if (Opts.MemoryProfileOutput == MemProfileBasename) {
GenerateArg(Args, OPT_fmemory_profile, SA);
} else {
size_t ArgLength =
Opts.MemoryProfileOutput.size() - MemProfileBasename.size();
GenerateArg(Args, OPT_fmemory_profile_EQ,
Opts.MemoryProfileOutput.substr(0, ArgLength), SA);
}
}
if (memcmp(Opts.CoverageVersion, "408*", 4) != 0)
GenerateArg(Args, OPT_coverage_version_EQ, Opts.CoverageVersion, SA);
// TODO: Check if we need to generate arguments stored in CmdArgs. (Namely
// '-fembed_bitcode', which does not map to any CompilerInvocation field and
// won't be generated.)
if (Opts.XRayInstrumentationBundle.Mask != XRayInstrKind::All) {
std::string InstrBundle =
serializeXRayInstrumentationBundle(Opts.XRayInstrumentationBundle);
if (!InstrBundle.empty())
GenerateArg(Args, OPT_fxray_instrumentation_bundle, InstrBundle, SA);
}
if (Opts.CFProtectionReturn && Opts.CFProtectionBranch)
GenerateArg(Args, OPT_fcf_protection_EQ, "full", SA);
else if (Opts.CFProtectionReturn)
GenerateArg(Args, OPT_fcf_protection_EQ, "return", SA);
else if (Opts.CFProtectionBranch)
GenerateArg(Args, OPT_fcf_protection_EQ, "branch", SA);
for (const auto &F : Opts.LinkBitcodeFiles) {
bool Builtint = F.LinkFlags == llvm::Linker::Flags::LinkOnlyNeeded &&
F.PropagateAttrs && F.Internalize;
GenerateArg(Args,
Builtint ? OPT_mlink_builtin_bitcode : OPT_mlink_bitcode_file,
F.Filename, SA);
}
// TODO: Consider removing marshalling annotations from f[no_]emulated_tls.
// That would make it easy to generate the option only **once** if it was
// explicitly set to non-default value.
if (Opts.ExplicitEmulatedTLS) {
GenerateArg(
Args, Opts.EmulatedTLS ? OPT_femulated_tls : OPT_fno_emulated_tls, SA);
}
if (Opts.FPDenormalMode != llvm::DenormalMode::getIEEE()) {
std::string Buffer;
llvm::raw_string_ostream OS(Buffer);
Opts.FPDenormalMode.print(OS);
GenerateArg(Args, OPT_fdenormal_fp_math_EQ, OS.str(), SA);
}
if (Opts.FP32DenormalMode != llvm::DenormalMode::getIEEE()) {
std::string Buffer;
llvm::raw_string_ostream OS(Buffer);
Opts.FP32DenormalMode.print(OS);
GenerateArg(Args, OPT_fdenormal_fp_math_f32_EQ, OS.str(), SA);
}
if (Opts.StructReturnConvention == CodeGenOptions::SRCK_OnStack) {
OptSpecifier Opt =
T.isPPC32() ? OPT_maix_struct_return : OPT_fpcc_struct_return;
GenerateArg(Args, Opt, SA);
} else if (Opts.StructReturnConvention == CodeGenOptions::SRCK_InRegs) {
OptSpecifier Opt =
T.isPPC32() ? OPT_msvr4_struct_return : OPT_freg_struct_return;
GenerateArg(Args, Opt, SA);
}
if (Opts.IgnoreXCOFFVisibility)
GenerateArg(Args, OPT_mignore_xcoff_visibility, SA);
if (Opts.EnableAIXExtendedAltivecABI)
GenerateArg(Args, OPT_mabi_EQ_vec_extabi, SA);
if (!Opts.OptRecordPasses.empty())
GenerateArg(Args, OPT_opt_record_passes, Opts.OptRecordPasses, SA);
if (!Opts.OptRecordFormat.empty())
GenerateArg(Args, OPT_opt_record_format, Opts.OptRecordFormat, SA);
if (Opts.OptimizationRemarkPattern)
GenerateArg(Args, OPT_Rpass_EQ, Opts.OptimizationRemarkPattern.Pattern, SA);
if (Opts.OptimizationRemarkMissedPattern)
GenerateArg(Args, OPT_Rpass_missed_EQ,
Opts.OptimizationRemarkMissedPattern.Pattern, SA);
if (Opts.OptimizationRemarkAnalysisPattern)
GenerateArg(Args, OPT_Rpass_analysis_EQ,
Opts.OptimizationRemarkAnalysisPattern.Pattern, SA);
GenerateArg(Args, OPT_fdiagnostics_hotness_threshold_EQ,
Opts.DiagnosticsHotnessThreshold
? Twine(*Opts.DiagnosticsHotnessThreshold)
: "auto",
SA);
for (StringRef Sanitizer : serializeSanitizerKinds(Opts.SanitizeRecover))
GenerateArg(Args, OPT_fsanitize_recover_EQ, Sanitizer, SA);
for (StringRef Sanitizer : serializeSanitizerKinds(Opts.SanitizeTrap))
GenerateArg(Args, OPT_fsanitize_trap_EQ, Sanitizer, SA);
if (!Opts.EmitVersionIdentMetadata)
GenerateArg(Args, OPT_Qn, SA);
}
bool CompilerInvocation::ParseCodeGenArgsImpl(CodeGenOptions &Opts,
ArgList &Args, InputKind IK,
DiagnosticsEngine &Diags,
const llvm::Triple &T,
const std::string &OutputFile,
const LangOptions &LangOptsRef) {
unsigned NumErrorsBefore = Diags.getNumErrors();
bool Success = true;
unsigned OptimizationLevel = getOptimizationLevel(Args, IK, Diags);
// TODO: This could be done in Driver
unsigned MaxOptLevel = 3;
if (OptimizationLevel > MaxOptLevel) {
// If the optimization level is not supported, fall back on the default
// optimization
Diags.Report(diag::warn_drv_optimization_value)
<< Args.getLastArg(OPT_O)->getAsString(Args) << "-O" << MaxOptLevel;
OptimizationLevel = MaxOptLevel;
}
Opts.OptimizationLevel = OptimizationLevel;
// The key paths of codegen options defined in Options.td start with
// "CodeGenOpts.". Let's provide the expected variable name and type.
CodeGenOptions &CodeGenOpts = Opts;
// Some codegen options depend on language options. Let's provide the expected
// variable name and type.
const LangOptions *LangOpts = &LangOptsRef;
#define CODEGEN_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef CODEGEN_OPTION_WITH_MARSHALLING
Cleanup the handling of noinline function attributes, -fno-inline, -fno-inline-functions, -O0, and optnone. These were really, really tangled together: - We used the noinline LLVM attribute for -fno-inline - But not for -fno-inline-functions (breaking LTO) - But we did use it for -finline-hint-functions (yay, LTO is happy!) - But we didn't for -O0 (LTO is sad yet again...) - We had weird structuring of CodeGenOpts with both an inlining enumeration and a boolean. They interacted in weird ways and needlessly. - A *lot* of set smashing went on with setting these, and then got worse when we considered optnone and other inlining-effecting attributes. - A bunch of inline affecting attributes were managed in a completely different place from -fno-inline. - Even with -fno-inline we failed to put the LLVM noinline attribute onto many generated function definitions because they didn't show up as AST-level functions. - If you passed -O0 but -finline-functions we would run the normal inliner pass in LLVM despite it being in the O0 pipeline, which really doesn't make much sense. - Lastly, we used things like '-fno-inline' to manipulate the pass pipeline which forced the pass pipeline to be much more parameterizable than it really needs to be. Instead we can *just* use the optimization level to select a pipeline and control the rest via attributes. Sadly, this causes a bunch of churn in tests because we don't run the optimizer in the tests and check the contents of attribute sets. It would be awesome if attribute sets were a bit more FileCheck friendly, but oh well. I think this is a significant improvement and should remove the semantic need to change what inliner pass we run in order to comply with the requested inlining semantics by relying completely on attributes. It also cleans up tho optnone and related handling a bit. One unfortunate aspect of this is that for generating alwaysinline routines like those in OpenMP we end up removing noinline and then adding alwaysinline. I tried a bunch of other approaches, but because we recompute function attributes from scratch and don't have a declaration here I couldn't find anything substantially cleaner than this. Differential Revision: https://reviews.llvm.org/D28053 llvm-svn: 290398
2016-12-23 01:24:49 +00:00
// At O0 we want to fully disable inlining outside of cases marked with
// 'alwaysinline' that are required for correctness.
Opts.setInlining((Opts.OptimizationLevel == 0)
? CodeGenOptions::OnlyAlwaysInlining
: CodeGenOptions::NormalInlining);
// Explicit inlining flags can disable some or all inlining even at
// optimization levels above zero.
if (Arg *InlineArg = Args.getLastArg(
options::OPT_finline_functions, options::OPT_finline_hint_functions,
options::OPT_fno_inline_functions, options::OPT_fno_inline)) {
if (Opts.OptimizationLevel > 0) {
const Option &InlineOpt = InlineArg->getOption();
if (InlineOpt.matches(options::OPT_finline_functions))
Opts.setInlining(CodeGenOptions::NormalInlining);
else if (InlineOpt.matches(options::OPT_finline_hint_functions))
Opts.setInlining(CodeGenOptions::OnlyHintInlining);
else
Opts.setInlining(CodeGenOptions::OnlyAlwaysInlining);
}
}
Add -f[no-]direct-access-external-data to supersede -mpie-copy-relocations GCC r218397 "x86-64: Optimize access to globals in PIE with copy reloc" made -fpie code emit R_X86_64_PC32 to reference external data symbols by default. Clang adopted -mpie-copy-relocations D19996 as a flexible alternative. The name -mpie-copy-relocations can be improved [1] and does not capture the idea that this option can apply to -fno-pic and -fpic [2], so this patch introduces -f[no-]direct-access-external-data and makes -mpie-copy-relocations their aliases for compatibility. [1] For ``` extern int var; int get() { return var; } ``` if var is defined in another translation unit in the link unit, there is no copy relocation. [2] -fno-pic -fno-direct-access-external-data is useful to avoid copy relocations. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65888 If a shared object is linked with -Bsymbolic or --dynamic-list and exports a data symbol, normally the data symbol cannot be accessed by -fno-pic code (because by default an absolute relocation is produced which will lead to a copy relocation). -fno-direct-access-external-data can prevent copy relocations. -fpic -fdirect-access-external-data can avoid GOT indirection. This is like the undefined counterpart of -fno-semantic-interposition. However, the user should define var in another translation unit and link with -Bsymbolic or --dynamic-list, otherwise the linker will error in a -shared link. Generally the user has better tools for their goal but I want to mention that this combination is valid. On COFF, the behavior is like always -fdirect-access-external-data. `__declspec(dllimport)` is needed to enable indirect access. There is currently no plan to affect non-ELF behaviors or -fpic behaviors. -fno-pic -fno-direct-access-external-data will be implemented in the subsequent patch. GCC feature request https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98112 Reviewed By: tmsriram Differential Revision: https://reviews.llvm.org/D92633
2021-01-09 00:29:09 -08:00
// PIC defaults to -fno-direct-access-external-data while non-PIC defaults to
// -fdirect-access-external-data.
Opts.DirectAccessExternalData =
Args.hasArg(OPT_fdirect_access_external_data) ||
(!Args.hasArg(OPT_fno_direct_access_external_data) &&
LangOpts->PICLevel == 0);
if (Arg *A = Args.getLastArg(OPT_debug_info_kind_EQ)) {
unsigned Val =
llvm::StringSwitch<unsigned>(A->getValue())
.Case("line-tables-only", codegenoptions::DebugLineTablesOnly)
.Case("line-directives-only", codegenoptions::DebugDirectivesOnly)
.Case("constructor", codegenoptions::DebugInfoConstructor)
.Case("limited", codegenoptions::LimitedDebugInfo)
.Case("standalone", codegenoptions::FullDebugInfo)
.Case("unused-types", codegenoptions::UnusedTypeInfo)
.Default(~0U);
if (Val == ~0U)
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args)
<< A->getValue();
else
Opts.setDebugInfo(static_cast<codegenoptions::DebugInfoKind>(Val));
}
Add -f[no-]direct-access-external-data to supersede -mpie-copy-relocations GCC r218397 "x86-64: Optimize access to globals in PIE with copy reloc" made -fpie code emit R_X86_64_PC32 to reference external data symbols by default. Clang adopted -mpie-copy-relocations D19996 as a flexible alternative. The name -mpie-copy-relocations can be improved [1] and does not capture the idea that this option can apply to -fno-pic and -fpic [2], so this patch introduces -f[no-]direct-access-external-data and makes -mpie-copy-relocations their aliases for compatibility. [1] For ``` extern int var; int get() { return var; } ``` if var is defined in another translation unit in the link unit, there is no copy relocation. [2] -fno-pic -fno-direct-access-external-data is useful to avoid copy relocations. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65888 If a shared object is linked with -Bsymbolic or --dynamic-list and exports a data symbol, normally the data symbol cannot be accessed by -fno-pic code (because by default an absolute relocation is produced which will lead to a copy relocation). -fno-direct-access-external-data can prevent copy relocations. -fpic -fdirect-access-external-data can avoid GOT indirection. This is like the undefined counterpart of -fno-semantic-interposition. However, the user should define var in another translation unit and link with -Bsymbolic or --dynamic-list, otherwise the linker will error in a -shared link. Generally the user has better tools for their goal but I want to mention that this combination is valid. On COFF, the behavior is like always -fdirect-access-external-data. `__declspec(dllimport)` is needed to enable indirect access. There is currently no plan to affect non-ELF behaviors or -fpic behaviors. -fno-pic -fno-direct-access-external-data will be implemented in the subsequent patch. GCC feature request https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98112 Reviewed By: tmsriram Differential Revision: https://reviews.llvm.org/D92633
2021-01-09 00:29:09 -08:00
// If -fuse-ctor-homing is set and limited debug info is already on, then use
// constructor homing.
if (Args.getLastArg(OPT_fuse_ctor_homing))
if (Opts.getDebugInfo() == codegenoptions::LimitedDebugInfo)
Opts.setDebugInfo(codegenoptions::DebugInfoConstructor);
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) {
auto Split = StringRef(Arg).split('=');
Opts.DebugPrefixMap.insert(
{std::string(Split.first), std::string(Split.second)});
}
for (const auto &Arg : Args.getAllArgValues(OPT_fprofile_prefix_map_EQ)) {
auto Split = StringRef(Arg).split('=');
Opts.ProfilePrefixMap.insert(
{std::string(Split.first), std::string(Split.second)});
}
const llvm::Triple::ArchType DebugEntryValueArchs[] = {
llvm::Triple::x86, llvm::Triple::x86_64, llvm::Triple::aarch64,
llvm::Triple::arm, llvm::Triple::armeb, llvm::Triple::mips,
llvm::Triple::mipsel, llvm::Triple::mips64, llvm::Triple::mips64el};
if (Opts.OptimizationLevel > 0 && Opts.hasReducedDebugInfo() &&
llvm::is_contained(DebugEntryValueArchs, T.getArch()))
Opts.EmitCallSiteInfo = true;
Opts.NewStructPathTBAA = !Args.hasArg(OPT_no_struct_path_tbaa) &&
Args.hasArg(OPT_new_struct_path_tbaa);
Opts.OptimizeSize = getOptimizationLevelSize(Args);
Opts.SimplifyLibCalls = !LangOpts->NoBuiltin;
if (Opts.SimplifyLibCalls)
Opts.NoBuiltinFuncs = LangOpts->NoBuiltinFuncs;
Opts.UnrollLoops =
Args.hasFlag(OPT_funroll_loops, OPT_fno_unroll_loops,
(Opts.OptimizationLevel > 1));
Add -fbinutils-version= to gate ELF features on the specified binutils version There are two use cases. Assembler We have accrued some code gated on MCAsmInfo::useIntegratedAssembler(). Some features are supported by latest GNU as, but we have to use MCAsmInfo::useIntegratedAs() because the newer versions have not been widely adopted (e.g. SHF_LINK_ORDER 'o' and 'unique' linkage in 2.35, --compress-debug-sections= in 2.26). Linker We want to use features supported only by LLD or very new GNU ld, or don't want to work around older GNU ld. We currently can't represent that "we don't care about old GNU ld". You can find such workarounds in a few other places, e.g. Mips/MipsAsmprinter.cpp PowerPC/PPCTOCRegDeps.cpp X86/X86MCInstrLower.cpp AArch64 TLS workaround for R_AARCH64_TLSLD_MOVW_DTPREL_* (PR ld/18276), R_AARCH64_TLSLE_LDST8_TPREL_LO12 (https://bugs.llvm.org/show_bug.cgi?id=36727 https://sourceware.org/bugzilla/show_bug.cgi?id=22969) Mixed SHF_LINK_ORDER and non-SHF_LINK_ORDER components (supported by LLD in D84001; GNU ld feature request https://sourceware.org/bugzilla/show_bug.cgi?id=16833 may take a while before available). This feature allows to garbage collect some unused sections (e.g. fragmented .gcc_except_table). This patch adds `-fbinutils-version=` to clang and `-binutils-version` to llc. It changes one codegen place in SHF_MERGE to demonstrate its usage. `-fbinutils-version=2.35` means the produced object file does not care about GNU ld<2.35 compatibility. When `-fno-integrated-as` is specified, the produced assembly can be consumed by GNU as>=2.35, but older versions may not work. `-fbinutils-version=none` means that we can use all ELF features, regardless of GNU as/ld support. Both clang and llc need `parseBinutilsVersion`. Such command line parsing is usually implemented in `llvm/lib/CodeGen/CommandFlags.cpp` (LLVMCodeGen), however, ClangCodeGen does not depend on LLVMCodeGen. So I add `parseBinutilsVersion` to `llvm/lib/Target/TargetMachine.cpp` (LLVMTarget). Differential Revision: https://reviews.llvm.org/D85474
2021-01-26 12:28:23 -08:00
Opts.BinutilsVersion =
std::string(Args.getLastArgValue(OPT_fbinutils_version_EQ));
Opts.DebugNameTable = static_cast<unsigned>(
Args.hasArg(OPT_ggnu_pubnames)
? llvm::DICompileUnit::DebugNameTableKind::GNU
: Args.hasArg(OPT_gpubnames)
? llvm::DICompileUnit::DebugNameTableKind::Default
: llvm::DICompileUnit::DebugNameTableKind::None);
if (!Opts.ProfileInstrumentUsePath.empty())
setPGOUseInstrumentor(Opts, Opts.ProfileInstrumentUsePath);
if (const Arg *A = Args.getLastArg(OPT_ftime_report, OPT_ftime_report_EQ)) {
Opts.TimePasses = true;
// -ftime-report= is only for new pass manager.
if (A->getOption().getID() == OPT_ftime_report_EQ) {
if (Opts.LegacyPassManager)
Diags.Report(diag::err_drv_argument_only_allowed_with)
<< A->getAsString(Args) << "-fno-legacy-pass-manager";
StringRef Val = A->getValue();
if (Val == "per-pass")
Opts.TimePassesPerRun = false;
else if (Val == "per-pass-run")
Opts.TimePassesPerRun = true;
else
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
}
}
// Basic Block Sections implies Function Sections.
Opts.FunctionSections =
Args.hasArg(OPT_ffunction_sections) ||
(Opts.BBSections != "none" && Opts.BBSections != "labels");
Opts.PrepareForLTO = Args.hasArg(OPT_flto, OPT_flto_EQ);
Opts.PrepareForThinLTO = false;
if (Arg *A = Args.getLastArg(OPT_flto_EQ)) {
StringRef S = A->getValue();
if (S == "thin")
Opts.PrepareForThinLTO = true;
else if (S != "full")
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << S;
}
if (Arg *A = Args.getLastArg(OPT_fthinlto_index_EQ)) {
if (IK.getLanguage() != Language::LLVM_IR)
Diags.Report(diag::err_drv_argument_only_allowed_with)
<< A->getAsString(Args) << "-x ir";
Opts.ThinLTOIndexFile =
std::string(Args.getLastArgValue(OPT_fthinlto_index_EQ));
}
if (Arg *A = Args.getLastArg(OPT_save_temps_EQ))
Opts.SaveTempsFilePrefix =
llvm::StringSwitch<std::string>(A->getValue())
.Case("obj", OutputFile)
.Default(llvm::sys::path::filename(OutputFile).str());
// The memory profile runtime appends the pid to make this name more unique.
const char *MemProfileBasename = "memprof.profraw";
if (Args.hasArg(OPT_fmemory_profile_EQ)) {
SmallString<128> Path(
std::string(Args.getLastArgValue(OPT_fmemory_profile_EQ)));
llvm::sys::path::append(Path, MemProfileBasename);
Opts.MemoryProfileOutput = std::string(Path);
} else if (Args.hasArg(OPT_fmemory_profile))
Opts.MemoryProfileOutput = MemProfileBasename;
memcpy(Opts.CoverageVersion, "408*", 4);
if (Opts.EmitGcovArcs || Opts.EmitGcovNotes) {
if (Args.hasArg(OPT_coverage_version_EQ)) {
StringRef CoverageVersion = Args.getLastArgValue(OPT_coverage_version_EQ);
if (CoverageVersion.size() != 4) {
Diags.Report(diag::err_drv_invalid_value)
<< Args.getLastArg(OPT_coverage_version_EQ)->getAsString(Args)
<< CoverageVersion;
} else {
memcpy(Opts.CoverageVersion, CoverageVersion.data(), 4);
}
}
}
// FIXME: For backend options that are not yet recorded as function
// attributes in the IR, keep track of them so we can embed them in a
// separate data section and use them when building the bitcode.
for (const auto &A : Args) {
// Do not encode output and input.
if (A->getOption().getID() == options::OPT_o ||
A->getOption().getID() == options::OPT_INPUT ||
A->getOption().getID() == options::OPT_x ||
A->getOption().getID() == options::OPT_fembed_bitcode ||
A->getOption().matches(options::OPT_W_Group))
continue;
ArgStringList ASL;
A->render(Args, ASL);
for (const auto &arg : ASL) {
StringRef ArgStr(arg);
Opts.CmdArgs.insert(Opts.CmdArgs.end(), ArgStr.begin(), ArgStr.end());
// using \00 to separate each commandline options.
Opts.CmdArgs.push_back('\0');
}
}
auto XRayInstrBundles =
Args.getAllArgValues(OPT_fxray_instrumentation_bundle);
if (XRayInstrBundles.empty())
Opts.XRayInstrumentationBundle.Mask = XRayInstrKind::All;
else
for (const auto &A : XRayInstrBundles)
parseXRayInstrumentationBundle("-fxray-instrumentation-bundle=", A, Args,
Diags, Opts.XRayInstrumentationBundle);
if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ)) {
StringRef Name = A->getValue();
if (Name == "full") {
Opts.CFProtectionReturn = 1;
Opts.CFProtectionBranch = 1;
} else if (Name == "return")
Opts.CFProtectionReturn = 1;
else if (Name == "branch")
Opts.CFProtectionBranch = 1;
else if (Name != "none") {
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name;
Success = false;
}
}
for (auto *A :
Args.filtered(OPT_mlink_bitcode_file, OPT_mlink_builtin_bitcode)) {
CodeGenOptions::BitcodeFileToLink F;
F.Filename = A->getValue();
if (A->getOption().matches(OPT_mlink_builtin_bitcode)) {
F.LinkFlags = llvm::Linker::Flags::LinkOnlyNeeded;
// When linking CUDA bitcode, propagate function attributes so that
// e.g. libdevice gets fast-math attrs if we're building with fast-math.
F.PropagateAttrs = true;
F.Internalize = true;
}
Opts.LinkBitcodeFiles.push_back(F);
}
if (Args.getLastArg(OPT_femulated_tls) ||
Args.getLastArg(OPT_fno_emulated_tls)) {
Opts.ExplicitEmulatedTLS = true;
}
if (Arg *A = Args.getLastArg(OPT_fdenormal_fp_math_EQ)) {
StringRef Val = A->getValue();
Opts.FPDenormalMode = llvm::parseDenormalFPAttribute(Val);
if (!Opts.FPDenormalMode.isValid())
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val;
}
Consolidate internal denormal flushing controls Currently there are 4 different mechanisms for controlling denormal flushing behavior, and about as many equivalent frontend controls. - AMDGPU uses the fp32-denormals and fp64-f16-denormals subtarget features - NVPTX uses the nvptx-f32ftz attribute - ARM directly uses the denormal-fp-math attribute - Other targets indirectly use denormal-fp-math in one DAGCombine - cl-denorms-are-zero has a corresponding denorms-are-zero attribute AMDGPU wants a distinct control for f32 flushing from f16/f64, and as far as I can tell the same is true for NVPTX (based on the attribute name). Work on consolidating these into the denormal-fp-math attribute, and a new type specific denormal-fp-math-f32 variant. Only ARM seems to support the two different flush modes, so this is overkill for the other use cases. Ideally we would error on the unsupported positive-zero mode on other targets from somewhere. Move the logic for selecting the flush mode into the compiler driver, instead of handling it in cc1. denormal-fp-math/denormal-fp-math-f32 are now both cc1 flags, but denormal-fp-math-f32 is not yet exposed as a user flag. -cl-denorms-are-zero, -fcuda-flush-denormals-to-zero and -fno-cuda-flush-denormals-to-zero will be mapped to -fp-denormal-math-f32=ieee or preserve-sign rather than the old attributes. Stop emitting the denorms-are-zero attribute for the OpenCL flag. It has no in-tree users. The meaning would also be target dependent, such as the AMDGPU choice to treat this as only meaning allow flushing of f32 and not f16 or f64. The naming is also potentially confusing, since DAZ in other contexts refers to instructions implicitly treating input denormals as zero, not necessarily flushing output denormals to zero. This also does not attempt to change the behavior for the current attribute. The LangRef now states that the default is ieee behavior, but this is inaccurate for the current implementation. The clang handling is slightly hacky to avoid touching the existing denormal-fp-math uses. Fixing this will be left for a future patch. AMDGPU is still using the subtarget feature to control the denormal mode, but the new attribute are now emitted. A future change will switch this and remove the subtarget features.
2019-11-01 17:57:29 -07:00
if (Arg *A = Args.getLastArg(OPT_fdenormal_fp_math_f32_EQ)) {
StringRef Val = A->getValue();
Opts.FP32DenormalMode = llvm::parseDenormalFPAttribute(Val);
if (!Opts.FP32DenormalMode.isValid())
Consolidate internal denormal flushing controls Currently there are 4 different mechanisms for controlling denormal flushing behavior, and about as many equivalent frontend controls. - AMDGPU uses the fp32-denormals and fp64-f16-denormals subtarget features - NVPTX uses the nvptx-f32ftz attribute - ARM directly uses the denormal-fp-math attribute - Other targets indirectly use denormal-fp-math in one DAGCombine - cl-denorms-are-zero has a corresponding denorms-are-zero attribute AMDGPU wants a distinct control for f32 flushing from f16/f64, and as far as I can tell the same is true for NVPTX (based on the attribute name). Work on consolidating these into the denormal-fp-math attribute, and a new type specific denormal-fp-math-f32 variant. Only ARM seems to support the two different flush modes, so this is overkill for the other use cases. Ideally we would error on the unsupported positive-zero mode on other targets from somewhere. Move the logic for selecting the flush mode into the compiler driver, instead of handling it in cc1. denormal-fp-math/denormal-fp-math-f32 are now both cc1 flags, but denormal-fp-math-f32 is not yet exposed as a user flag. -cl-denorms-are-zero, -fcuda-flush-denormals-to-zero and -fno-cuda-flush-denormals-to-zero will be mapped to -fp-denormal-math-f32=ieee or preserve-sign rather than the old attributes. Stop emitting the denorms-are-zero attribute for the OpenCL flag. It has no in-tree users. The meaning would also be target dependent, such as the AMDGPU choice to treat this as only meaning allow flushing of f32 and not f16 or f64. The naming is also potentially confusing, since DAZ in other contexts refers to instructions implicitly treating input denormals as zero, not necessarily flushing output denormals to zero. This also does not attempt to change the behavior for the current attribute. The LangRef now states that the default is ieee behavior, but this is inaccurate for the current implementation. The clang handling is slightly hacky to avoid touching the existing denormal-fp-math uses. Fixing this will be left for a future patch. AMDGPU is still using the subtarget feature to control the denormal mode, but the new attribute are now emitted. A future change will switch this and remove the subtarget features.
2019-11-01 17:57:29 -07:00
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val;
}
// X86_32 has -fppc-struct-return and -freg-struct-return.
// PPC32 has -maix-struct-return and -msvr4-struct-return.
if (Arg *A =
Args.getLastArg(OPT_fpcc_struct_return, OPT_freg_struct_return,
OPT_maix_struct_return, OPT_msvr4_struct_return)) {
// TODO: We might want to consider enabling these options on AIX in the
// future.
if (T.isOSAIX())
Diags.Report(diag::err_drv_unsupported_opt_for_target)
<< A->getSpelling() << T.str();
const Option &O = A->getOption();
if (O.matches(OPT_fpcc_struct_return) ||
O.matches(OPT_maix_struct_return)) {
Opts.setStructReturnConvention(CodeGenOptions::SRCK_OnStack);
} else {
assert(O.matches(OPT_freg_struct_return) ||
O.matches(OPT_msvr4_struct_return));
Opts.setStructReturnConvention(CodeGenOptions::SRCK_InRegs);
}
}
if (T.isOSAIX() && (Args.hasArg(OPT_mignore_xcoff_visibility) ||
!Args.hasArg(OPT_fvisibility)))
Opts.IgnoreXCOFFVisibility = 1;
if (Arg *A =
Args.getLastArg(OPT_mabi_EQ_vec_default, OPT_mabi_EQ_vec_extabi)) {
if (!T.isOSAIX())
Diags.Report(diag::err_drv_unsupported_opt_for_target)
<< A->getSpelling() << T.str();
const Option &O = A->getOption();
if (O.matches(OPT_mabi_EQ_vec_default))
Diags.Report(diag::err_aix_default_altivec_abi)
<< A->getSpelling() << T.str();
else {
assert(O.matches(OPT_mabi_EQ_vec_extabi));
Opts.EnableAIXExtendedAltivecABI = 1;
}
}
bool NeedLocTracking = false;
if (!Opts.OptRecordFile.empty())
NeedLocTracking = true;
if (Arg *A = Args.getLastArg(OPT_opt_record_passes)) {
Opts.OptRecordPasses = A->getValue();
NeedLocTracking = true;
}
if (Arg *A = Args.getLastArg(OPT_opt_record_format)) {
Opts.OptRecordFormat = A->getValue();
NeedLocTracking = true;
}
if (Arg *A = Args.getLastArg(OPT_Rpass_EQ)) {
Opts.OptimizationRemarkPattern =
GenerateOptimizationRemarkRegex(Diags, Args, A);
NeedLocTracking = true;
}
if (Arg *A = Args.getLastArg(OPT_Rpass_missed_EQ)) {
Opts.OptimizationRemarkMissedPattern =
GenerateOptimizationRemarkRegex(Diags, Args, A);
NeedLocTracking = true;
}
if (Arg *A = Args.getLastArg(OPT_Rpass_analysis_EQ)) {
Opts.OptimizationRemarkAnalysisPattern =
GenerateOptimizationRemarkRegex(Diags, Args, A);
NeedLocTracking = true;
}
bool UsingSampleProfile = !Opts.SampleProfileFile.empty();
bool UsingProfile = UsingSampleProfile ||
(Opts.getProfileUse() != CodeGenOptions::ProfileNone);
if (Opts.DiagnosticsWithHotness && !UsingProfile &&
// An IR file will contain PGO as metadata
IK.getLanguage() != Language::LLVM_IR)
Diags.Report(diag::warn_drv_diagnostics_hotness_requires_pgo)
<< "-fdiagnostics-show-hotness";
// Parse remarks hotness threshold. Valid value is either integer or 'auto'.
if (auto *arg =
Args.getLastArg(options::OPT_fdiagnostics_hotness_threshold_EQ)) {
auto ResultOrErr =
llvm::remarks::parseHotnessThresholdOption(arg->getValue());
if (!ResultOrErr) {
Diags.Report(diag::err_drv_invalid_diagnotics_hotness_threshold)
<< "-fdiagnostics-hotness-threshold=";
} else {
Opts.DiagnosticsHotnessThreshold = *ResultOrErr;
if ((!Opts.DiagnosticsHotnessThreshold.hasValue() ||
Opts.DiagnosticsHotnessThreshold.getValue() > 0) &&
!UsingProfile)
Diags.Report(diag::warn_drv_diagnostics_hotness_requires_pgo)
<< "-fdiagnostics-hotness-threshold=";
}
}
// If the user requested to use a sample profile for PGO, then the
// backend will need to track source location information so the profile
// can be incorporated into the IR.
if (UsingSampleProfile)
NeedLocTracking = true;
// If the user requested a flag that requires source locations available in
// the backend, make sure that the backend tracks source location information.
if (NeedLocTracking && Opts.getDebugInfo() == codegenoptions::NoDebugInfo)
Opts.setDebugInfo(codegenoptions::LocTrackingOnly);
// Parse -fsanitize-recover= arguments.
// FIXME: Report unrecoverable sanitizers incorrectly specified here.
parseSanitizerKinds("-fsanitize-recover=",
Args.getAllArgValues(OPT_fsanitize_recover_EQ), Diags,
Opts.SanitizeRecover);
parseSanitizerKinds("-fsanitize-trap=",
Args.getAllArgValues(OPT_fsanitize_trap_EQ), Diags,
Opts.SanitizeTrap);
Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
if (Args.hasArg(options::OPT_ffinite_loops))
Opts.FiniteLoops = CodeGenOptions::FiniteLoopsKind::Always;
else if (Args.hasArg(options::OPT_fno_finite_loops))
Opts.FiniteLoops = CodeGenOptions::FiniteLoopsKind::Never;
return Success && Diags.getNumErrors() == NumErrorsBefore;
}
bool CompilerInvocation::ParseCodeGenArgs(
CompilerInvocation &Res, CodeGenOptions &Opts, ArgList &Args, InputKind IK,
DiagnosticsEngine &Diags, const llvm::Triple &T,
const std::string &OutputFile, const LangOptions &LangOptsRef) {
CodeGenOptions DummyOpts;
return RoundTrip(
[&](CompilerInvocation &Res, ArgList &Args, DiagnosticsEngine &Diags) {
Args.getLastArg(OPT_O0, OPT_O4, OPT_O, OPT_Ofast);
return ParseCodeGenArgsImpl(Res.getCodeGenOpts(), Args, IK, Diags, T,
OutputFile, LangOptsRef);
},
[&](CompilerInvocation &Res, SmallVectorImpl<const char *> &GeneratedArgs,
StringAllocator SA) {
GenerateCodeGenArgs(Res.getCodeGenOpts(), GeneratedArgs, SA, T,
OutputFile, &LangOptsRef);
},
[&DummyOpts](CompilerInvocation &Res) {
std::swap(Res.CodeGenOpts, DummyOpts);
},
Res, Args, Diags, "CodeGenOptions");
}
static void
GenerateDependencyOutputArgs(const DependencyOutputOptions &Opts,
SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA) {
const DependencyOutputOptions &DependencyOutputOpts = Opts;
#define DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING
if (Opts.ShowIncludesDest != ShowIncludesDestination::None)
GenerateArg(Args, OPT_show_includes, SA);
for (const auto &Dep : Opts.ExtraDeps) {
switch (Dep.second) {
case EDK_SanitizeBlacklist:
// Sanitizer blacklist arguments are generated from LanguageOptions.
continue;
case EDK_ModuleFile:
// Module file arguments are generated from FrontendOptions and
// HeaderSearchOptions.
continue;
case EDK_ProfileList:
GenerateArg(Args, OPT_fprofile_list_EQ, Dep.first, SA);
break;
case EDK_DepFileEntry:
GenerateArg(Args, OPT_fdepfile_entry, Dep.first, SA);
break;
}
}
}
static bool ParseDependencyOutputArgsImpl(
DependencyOutputOptions &Opts, ArgList &Args,
DiagnosticsEngine &Diags,
frontend::ActionKind Action, bool ShowLineMarkers) {
unsigned NumErrorsBefore = Diags.getNumErrors();
bool Success = true;
DependencyOutputOptions &DependencyOutputOpts = Opts;
#define DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING
if (Args.hasArg(OPT_show_includes)) {
// Writing both /showIncludes and preprocessor output to stdout
// would produce interleaved output, so use stderr for /showIncludes.
// This behaves the same as cl.exe, when /E, /EP or /P are passed.
if (Action == frontend::PrintPreprocessedInput || !ShowLineMarkers)
Opts.ShowIncludesDest = ShowIncludesDestination::Stderr;
else
Opts.ShowIncludesDest = ShowIncludesDestination::Stdout;
} else {
Opts.ShowIncludesDest = ShowIncludesDestination::None;
}
// Add sanitizer blacklists as extra dependencies.
// They won't be discovered by the regular preprocessor, so
// we let make / ninja to know about this implicit dependency.
if (!Args.hasArg(OPT_fno_sanitize_blacklist)) {
for (const auto *A : Args.filtered(OPT_fsanitize_blacklist)) {
StringRef Val = A->getValue();
if (Val.find('=') == StringRef::npos)
Opts.ExtraDeps.emplace_back(std::string(Val), EDK_SanitizeBlacklist);
}
if (Opts.IncludeSystemHeaders) {
for (const auto *A : Args.filtered(OPT_fsanitize_system_blacklist)) {
StringRef Val = A->getValue();
if (Val.find('=') == StringRef::npos)
Opts.ExtraDeps.emplace_back(std::string(Val), EDK_SanitizeBlacklist);
}
}
}
// -fprofile-list= dependencies.
for (const auto &Filename : Args.getAllArgValues(OPT_fprofile_list_EQ))
Opts.ExtraDeps.emplace_back(Filename, EDK_ProfileList);
// Propagate the extra dependencies.
for (const auto *A : Args.filtered(OPT_fdepfile_entry))
Opts.ExtraDeps.emplace_back(A->getValue(), EDK_DepFileEntry);
// Only the -fmodule-file=<file> form.
for (const auto *A : Args.filtered(OPT_fmodule_file)) {
StringRef Val = A->getValue();
if (Val.find('=') == StringRef::npos)
Opts.ExtraDeps.emplace_back(std::string(Val), EDK_ModuleFile);
}
return Success && Diags.getNumErrors() == NumErrorsBefore;
}
static bool ParseDependencyOutputArgs(CompilerInvocation &Res,
DependencyOutputOptions &Opts,
ArgList &Args, DiagnosticsEngine &Diags,
frontend::ActionKind Action,
bool ShowLineMarkers) {
DependencyOutputOptions DummyOpts;
return RoundTrip(
[Action, ShowLineMarkers](CompilerInvocation &Res, ArgList &Args,
DiagnosticsEngine &Diags) {
return ParseDependencyOutputArgsImpl(Res.getDependencyOutputOpts(),
Args, Diags, Action,
ShowLineMarkers);
},
[&Args](CompilerInvocation &Res,
SmallVectorImpl<const char *> &GeneratedArgs,
CompilerInvocation::StringAllocator SA) {
GenerateDependencyOutputArgs(Res.getDependencyOutputOpts(),
GeneratedArgs, SA);
// We're querying sanitizer blacklist and module file arguments, but
// they are generated from LanguageOptions and HeaderSearchOptions.
// Let's satisfy RoundTrip by generating them ourselves for now.
if (!Args.hasArg(OPT_fno_sanitize_blacklist)) {
for (const auto *A : Args.filtered(OPT_fsanitize_blacklist)) {
StringRef Val = A->getValue();
if (Val.find('=') == StringRef::npos)
GenerateArg(GeneratedArgs, OPT_fsanitize_blacklist, Val, SA);
}
if (Res.getDependencyOutputOpts().IncludeSystemHeaders) {
for (const auto *A :
Args.filtered(OPT_fsanitize_system_blacklist)) {
StringRef Val = A->getValue();
if (Val.find('=') == StringRef::npos)
GenerateArg(GeneratedArgs, OPT_fsanitize_system_blacklist, Val,
SA);
}
}
}
for (const auto *A : Args.filtered(OPT_fmodule_file)) {
StringRef Val = A->getValue();
if (Val.find('=') == StringRef::npos)
GenerateArg(GeneratedArgs, OPT_fmodule_file, Val, SA);
}
},
[&DummyOpts](CompilerInvocation &Res) {
std::swap(Res.getDependencyOutputOpts(), DummyOpts);
},
Res, Args, Diags, "DependencyOutputOptions");
}
static bool parseShowColorsArgs(const ArgList &Args, bool DefaultColor) {
// Color diagnostics default to auto ("on" if terminal supports) in the driver
// but default to off in cc1, needing an explicit OPT_fdiagnostics_color.
// Support both clang's -f[no-]color-diagnostics and gcc's
// -f[no-]diagnostics-colors[=never|always|auto].
enum {
Colors_On,
Colors_Off,
Colors_Auto
} ShowColors = DefaultColor ? Colors_Auto : Colors_Off;
for (auto *A : Args) {
const Option &O = A->getOption();
if (O.matches(options::OPT_fcolor_diagnostics) ||
O.matches(options::OPT_fdiagnostics_color)) {
ShowColors = Colors_On;
} else if (O.matches(options::OPT_fno_color_diagnostics) ||
O.matches(options::OPT_fno_diagnostics_color)) {
ShowColors = Colors_Off;
} else if (O.matches(options::OPT_fdiagnostics_color_EQ)) {
StringRef Value(A->getValue());
if (Value == "always")
ShowColors = Colors_On;
else if (Value == "never")
ShowColors = Colors_Off;
else if (Value == "auto")
ShowColors = Colors_Auto;
}
}
return ShowColors == Colors_On ||
(ShowColors == Colors_Auto &&
llvm::sys::Process::StandardErrHasColors());
}
static bool checkVerifyPrefixes(const std::vector<std::string> &VerifyPrefixes,
DiagnosticsEngine &Diags) {
bool Success = true;
for (const auto &Prefix : VerifyPrefixes) {
// Every prefix must start with a letter and contain only alphanumeric
// characters, hyphens, and underscores.
auto BadChar = llvm::find_if(Prefix, [](char C) {
return !isAlphanumeric(C) && C != '-' && C != '_';
});
if (BadChar != Prefix.end() || !isLetter(Prefix[0])) {
Success = false;
Diags.Report(diag::err_drv_invalid_value) << "-verify=" << Prefix;
Diags.Report(diag::note_drv_verify_prefix_spelling);
}
}
return Success;
}
static void GenerateFileSystemArgs(const FileSystemOptions &Opts,
SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA) {
const FileSystemOptions &FileSystemOpts = Opts;
#define FILE_SYSTEM_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef FILE_SYSTEM_OPTION_WITH_MARSHALLING
}
static bool ParseFileSystemArgs(FileSystemOptions &Opts, const ArgList &Args,
DiagnosticsEngine &Diags) {
FileSystemOptions &FileSystemOpts = Opts;
bool Success = true;
#define FILE_SYSTEM_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef FILE_SYSTEM_OPTION_WITH_MARSHALLING
return Success;
}
static void GenerateMigratorArgs(const MigratorOptions &Opts,
SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA) {
const MigratorOptions &MigratorOpts = Opts;
#define MIGRATOR_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef MIGRATOR_OPTION_WITH_MARSHALLING
}
static bool ParseMigratorArgs(MigratorOptions &Opts, const ArgList &Args,
DiagnosticsEngine &Diags) {
MigratorOptions &MigratorOpts = Opts;
bool Success = true;
#define MIGRATOR_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef MIGRATOR_OPTION_WITH_MARSHALLING
return Success;
}
void CompilerInvocation::GenerateDiagnosticArgs(
const DiagnosticOptions &Opts, SmallVectorImpl<const char *> &Args,
StringAllocator SA, bool DefaultDiagColor) {
const DiagnosticOptions *DiagnosticOpts = &Opts;
#define DIAG_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef DIAG_OPTION_WITH_MARSHALLING
if (!Opts.DiagnosticSerializationFile.empty())
GenerateArg(Args, OPT_diagnostic_serialized_file,
Opts.DiagnosticSerializationFile, SA);
if (Opts.ShowColors)
GenerateArg(Args, OPT_fcolor_diagnostics, SA);
if (Opts.VerifyDiagnostics &&
llvm::is_contained(Opts.VerifyPrefixes, "expected"))
GenerateArg(Args, OPT_verify, SA);
for (const auto &Prefix : Opts.VerifyPrefixes)
if (Prefix != "expected")
GenerateArg(Args, OPT_verify_EQ, Prefix, SA);
DiagnosticLevelMask VIU = Opts.getVerifyIgnoreUnexpected();
if (VIU == DiagnosticLevelMask::None) {
// This is the default, don't generate anything.
} else if (VIU == DiagnosticLevelMask::All) {
GenerateArg(Args, OPT_verify_ignore_unexpected, SA);
} else {
if (static_cast<unsigned>(VIU & DiagnosticLevelMask::Note) != 0)
GenerateArg(Args, OPT_verify_ignore_unexpected_EQ, "note", SA);
if (static_cast<unsigned>(VIU & DiagnosticLevelMask::Remark) != 0)
GenerateArg(Args, OPT_verify_ignore_unexpected_EQ, "remark", SA);
if (static_cast<unsigned>(VIU & DiagnosticLevelMask::Warning) != 0)
GenerateArg(Args, OPT_verify_ignore_unexpected_EQ, "warning", SA);
if (static_cast<unsigned>(VIU & DiagnosticLevelMask::Error) != 0)
GenerateArg(Args, OPT_verify_ignore_unexpected_EQ, "error", SA);
}
for (const auto &Warning : Opts.Warnings) {
// This option is automatically generated from UndefPrefixes.
if (Warning == "undef-prefix")
continue;
Args.push_back(SA(StringRef("-W") + Warning));
}
for (const auto &Remark : Opts.Remarks)
Args.push_back(SA(StringRef("-R") + Remark));
}
bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
DiagnosticsEngine *Diags,
bool DefaultDiagColor) {
Optional<DiagnosticsEngine> IgnoringDiags;
if (!Diags) {
IgnoringDiags.emplace(new DiagnosticIDs(), new DiagnosticOptions(),
new IgnoringDiagConsumer());
Diags = &*IgnoringDiags;
}
[clang][cli] Specify KeyPath prefixes via TableGen classes It turns out we need to handle `LangOptions` separately from the rest of the options. `LangOptions` used to be conditionally parsed only when `!(DashX.getFormat() == InputKind::Precompiled || DashX.getLanguage() == Language::LLVM_IR)` and we need to restore this order (for more info, see D94682). We could do this similarly to how `DiagnosticOptions` are handled: via a counterpart to the `IsDiag` mix-in (e.g. `IsLang`). These mix-ins would prefix the option key path with the appropriate `CompilerInvocation::XxxOpts` member. However, this solution would be problematic, as we'd now have two kinds of options (`Lang` and `Diag`) with seemingly incomplete key paths in the same file. To understand what `CompilerInvocation` member an option affects, one would need to read the whole option definition and notice the `IsDiag` or `IsLang` class. Instead, this patch introduces more robust way to handle different kinds of options separately: via the `KeyPathAndMacroPrefix` class. We have one specialization of that class per `CompilerInvocation` member (e.g. `LangOpts`, `DiagnosticOpts`, etc.). Now, instead of specifying a key path with `"LangOpts->UndefPrefixes"`, we use `LangOpts<"UndefPrefixes">`. This keeps the readability intact (you don't have to look for the `IsLang` mix-in, the key path is complete on its own) and allows us to specify a custom macro prefix within `LangOpts`. Reviewed By: Bigcheese Differential Revision: https://reviews.llvm.org/D94676
2021-01-13 12:50:19 +01:00
// The key paths of diagnostic options defined in Options.td start with
// "DiagnosticOpts->". Let's provide the expected variable name and type.
DiagnosticOptions *DiagnosticOpts = &Opts;
bool Success = true;
#define DIAG_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, *Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef DIAG_OPTION_WITH_MARSHALLING
llvm::sys::Process::UseANSIEscapeCodes(Opts.UseANSIEscapeCodes);
if (Arg *A =
Args.getLastArg(OPT_diagnostic_serialized_file, OPT__serialize_diags))
Opts.DiagnosticSerializationFile = A->getValue();
Opts.ShowColors = parseShowColorsArgs(Args, DefaultDiagColor);
Opts.VerifyDiagnostics = Args.hasArg(OPT_verify) || Args.hasArg(OPT_verify_EQ);
Opts.VerifyPrefixes = Args.getAllArgValues(OPT_verify_EQ);
if (Args.hasArg(OPT_verify))
Opts.VerifyPrefixes.push_back("expected");
// Keep VerifyPrefixes in its original order for the sake of diagnostics, and
// then sort it to prepare for fast lookup using std::binary_search.
if (!checkVerifyPrefixes(Opts.VerifyPrefixes, *Diags)) {
Opts.VerifyDiagnostics = false;
Success = false;
}
else
llvm::sort(Opts.VerifyPrefixes);
DiagnosticLevelMask DiagMask = DiagnosticLevelMask::None;
Success &= parseDiagnosticLevelMask("-verify-ignore-unexpected=",
Args.getAllArgValues(OPT_verify_ignore_unexpected_EQ),
*Diags, DiagMask);
if (Args.hasArg(OPT_verify_ignore_unexpected))
DiagMask = DiagnosticLevelMask::All;
Opts.setVerifyIgnoreUnexpected(DiagMask);
if (Opts.TabStop == 0 || Opts.TabStop > DiagnosticOptions::MaxTabStop) {
Opts.TabStop = DiagnosticOptions::DefaultTabStop;
Diags->Report(diag::warn_ignoring_ftabstop_value)
<< Opts.TabStop << DiagnosticOptions::DefaultTabStop;
}
addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, Opts.Warnings);
addDiagnosticArgs(Args, OPT_R_Group, OPT_R_value_Group, Opts.Remarks);
return Success;
}
bool CompilerInvocation::ParseDiagnosticArgsRoundTrip(CompilerInvocation &Res,
DiagnosticOptions &Opts,
ArgList &Args,
DiagnosticsEngine *Diags,
bool DefaultDiagColor) {
IntrusiveRefCntPtr<DiagnosticOptions> DummyOpts(new DiagnosticOptions);
return RoundTrip(
[DefaultDiagColor](CompilerInvocation &Res, ArgList &Args,
DiagnosticsEngine &Diags) {
// Query the options might not get queried properly during parsing, but
// should be generated from DiagnosticOptions.
Args.getLastArg(OPT_fcolor_diagnostics);
Args.getLastArg(OPT_fno_color_diagnostics);
Args.getLastArg(OPT_fdiagnostics_color);
Args.getLastArg(OPT_fno_diagnostics_color);
Args.getLastArg(OPT_fdiagnostics_color_EQ);
for (auto *A : Args.filtered(OPT_W_Group))
Args.getLastArg(A->getOption().getID());
for (auto *A : Args.filtered(OPT_R_Group))
Args.getLastArg(A->getOption().getID());
return clang::ParseDiagnosticArgs(Res.getDiagnosticOpts(), Args, &Diags,
DefaultDiagColor);
},
[DefaultDiagColor](CompilerInvocation &Res,
SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA) {
GenerateDiagnosticArgs(Res.getDiagnosticOpts(), Args, SA,
DefaultDiagColor);
},
[&DummyOpts](CompilerInvocation &Res) {
Res.DiagnosticOpts.swap(DummyOpts);
},
Res, Args, *Diags, "DiagnosticOptions");
}
/// Parse the argument to the -ftest-module-file-extension
/// command-line argument.
///
/// \returns true on error, false on success.
static bool parseTestModuleFileExtensionArg(StringRef Arg,
std::string &BlockName,
unsigned &MajorVersion,
unsigned &MinorVersion,
bool &Hashed,
std::string &UserInfo) {
SmallVector<StringRef, 5> Args;
Arg.split(Args, ':', 5);
if (Args.size() < 5)
return true;
BlockName = std::string(Args[0]);
if (Args[1].getAsInteger(10, MajorVersion)) return true;
if (Args[2].getAsInteger(10, MinorVersion)) return true;
if (Args[3].getAsInteger(2, Hashed)) return true;
if (Args.size() > 4)
UserInfo = std::string(Args[4]);
return false;
}
/// Return a table that associates command line option specifiers with the
/// frontend action. Note: The pair {frontend::PluginAction, OPT_plugin} is
/// intentionally missing, as this case is handled separately from other
/// frontend options.
static const auto &getFrontendActionTable() {
static const std::pair<frontend::ActionKind, unsigned> Table[] = {
{frontend::ASTDeclList, OPT_ast_list},
{frontend::ASTDump, OPT_ast_dump_all_EQ},
{frontend::ASTDump, OPT_ast_dump_all},
{frontend::ASTDump, OPT_ast_dump_EQ},
{frontend::ASTDump, OPT_ast_dump},
{frontend::ASTDump, OPT_ast_dump_lookups},
{frontend::ASTDump, OPT_ast_dump_decl_types},
{frontend::ASTPrint, OPT_ast_print},
{frontend::ASTView, OPT_ast_view},
{frontend::DumpCompilerOptions, OPT_compiler_options_dump},
{frontend::DumpRawTokens, OPT_dump_raw_tokens},
{frontend::DumpTokens, OPT_dump_tokens},
{frontend::EmitAssembly, OPT_S},
{frontend::EmitBC, OPT_emit_llvm_bc},
{frontend::EmitHTML, OPT_emit_html},
{frontend::EmitLLVM, OPT_emit_llvm},
{frontend::EmitLLVMOnly, OPT_emit_llvm_only},
{frontend::EmitCodeGenOnly, OPT_emit_codegen_only},
{frontend::EmitCodeGenOnly, OPT_emit_codegen_only},
{frontend::EmitObj, OPT_emit_obj},
{frontend::FixIt, OPT_fixit_EQ},
{frontend::FixIt, OPT_fixit},
{frontend::GenerateModule, OPT_emit_module},
{frontend::GenerateModuleInterface, OPT_emit_module_interface},
{frontend::GenerateHeaderModule, OPT_emit_header_module},
{frontend::GeneratePCH, OPT_emit_pch},
{frontend::GenerateInterfaceStubs, OPT_emit_interface_stubs},
{frontend::InitOnly, OPT_init_only},
{frontend::ParseSyntaxOnly, OPT_fsyntax_only},
{frontend::ModuleFileInfo, OPT_module_file_info},
{frontend::VerifyPCH, OPT_verify_pch},
{frontend::PrintPreamble, OPT_print_preamble},
{frontend::PrintPreprocessedInput, OPT_E},
{frontend::TemplightDump, OPT_templight_dump},
{frontend::RewriteMacros, OPT_rewrite_macros},
{frontend::RewriteObjC, OPT_rewrite_objc},
{frontend::RewriteTest, OPT_rewrite_test},
{frontend::RunAnalysis, OPT_analyze},
{frontend::MigrateSource, OPT_migrate},
{frontend::RunPreprocessorOnly, OPT_Eonly},
{frontend::PrintDependencyDirectivesSourceMinimizerOutput,
OPT_print_dependency_directives_minimized_source},
};
return Table;
}
/// Maps command line option to frontend action.
static Optional<frontend::ActionKind> getFrontendAction(OptSpecifier &Opt) {
for (const auto &ActionOpt : getFrontendActionTable())
if (ActionOpt.second == Opt.getID())
return ActionOpt.first;
return None;
}
/// Maps frontend action to command line option.
static Optional<OptSpecifier>
getProgramActionOpt(frontend::ActionKind ProgramAction) {
for (const auto &ActionOpt : getFrontendActionTable())
if (ActionOpt.first == ProgramAction)
return OptSpecifier(ActionOpt.second);
return None;
}
static void GenerateFrontendArgs(const FrontendOptions &Opts,
SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA,
bool IsHeader) {
const FrontendOptions &FrontendOpts = Opts;
#define FRONTEND_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef FRONTEND_OPTION_WITH_MARSHALLING
Optional<OptSpecifier> ProgramActionOpt =
getProgramActionOpt(Opts.ProgramAction);
// Generating a simple flag covers most frontend actions.
std::function<void()> GenerateProgramAction = [&]() {
GenerateArg(Args, *ProgramActionOpt, SA);
};
if (!ProgramActionOpt) {
// PluginAction is the only program action handled separately.
assert(Opts.ProgramAction == frontend::PluginAction &&
"Frontend action without option.");
GenerateProgramAction = [&]() {
GenerateArg(Args, OPT_plugin, Opts.ActionName, SA);
};
}
// FIXME: Simplify the complex 'AST dump' command line.
if (Opts.ProgramAction == frontend::ASTDump) {
GenerateProgramAction = [&]() {
// ASTDumpLookups, ASTDumpDeclTypes and ASTDumpFilter are generated via
// marshalling infrastructure.
if (Opts.ASTDumpFormat != ADOF_Default) {
StringRef Format;
switch (Opts.ASTDumpFormat) {
case ADOF_Default:
llvm_unreachable("Default AST dump format.");
case ADOF_JSON:
Format = "json";
break;
}
if (Opts.ASTDumpAll)
GenerateArg(Args, OPT_ast_dump_all_EQ, Format, SA);
if (Opts.ASTDumpDecls)
GenerateArg(Args, OPT_ast_dump_EQ, Format, SA);
} else {
if (Opts.ASTDumpAll)
GenerateArg(Args, OPT_ast_dump_all, SA);
if (Opts.ASTDumpDecls)
GenerateArg(Args, OPT_ast_dump, SA);
}
};
}
if (Opts.ProgramAction == frontend::FixIt && !Opts.FixItSuffix.empty()) {
GenerateProgramAction = [&]() {
GenerateArg(Args, OPT_fixit_EQ, Opts.FixItSuffix, SA);
};
}
GenerateProgramAction();
for (const auto &PluginArgs : Opts.PluginArgs)
for (const auto &PluginArg : PluginArgs.second)
GenerateArg(Args, OPT_plugin_arg, PluginArgs.first + PluginArg, SA);
for (const auto &Ext : Opts.ModuleFileExtensions) {
if (auto *TestExt = dyn_cast_or_null<TestModuleFileExtension>(Ext.get())) {
std::string Buffer;
llvm::raw_string_ostream OS(Buffer);
OS << *TestExt;
GenerateArg(Args, OPT_ftest_module_file_extension_EQ, OS.str(), SA);
}
}
if (!Opts.CodeCompletionAt.FileName.empty())
GenerateArg(Args, OPT_code_completion_at, Opts.CodeCompletionAt.ToString(),
SA);
for (const auto &Plugin : Opts.Plugins)
GenerateArg(Args, OPT_load, Plugin, SA);
// ASTDumpDecls and ASTDumpAll already handled with ProgramAction.
for (const auto &ModuleFile : Opts.ModuleFiles)
GenerateArg(Args, OPT_fmodule_file, ModuleFile, SA);
if (Opts.AuxTargetCPU.hasValue())
GenerateArg(Args, OPT_aux_target_cpu, *Opts.AuxTargetCPU, SA);
if (Opts.AuxTargetFeatures.hasValue())
for (const auto &Feature : *Opts.AuxTargetFeatures)
GenerateArg(Args, OPT_aux_target_feature, Feature, SA);
{
StringRef Preprocessed = Opts.DashX.isPreprocessed() ? "-cpp-output" : "";
StringRef ModuleMap =
Opts.DashX.getFormat() == InputKind::ModuleMap ? "-module-map" : "";
StringRef Header = IsHeader ? "-header" : "";
StringRef Lang;
switch (Opts.DashX.getLanguage()) {
case Language::C:
Lang = "c";
break;
case Language::OpenCL:
Lang = "cl";
break;
case Language::CUDA:
Lang = "cuda";
break;
case Language::HIP:
Lang = "hip";
break;
case Language::CXX:
Lang = "c++";
break;
case Language::ObjC:
Lang = "objective-c";
break;
case Language::ObjCXX:
Lang = "objective-c++";
break;
case Language::RenderScript:
Lang = "renderscript";
break;
case Language::Asm:
Lang = "assembler-with-cpp";
break;
case Language::Unknown:
assert(Opts.DashX.getFormat() == InputKind::Precompiled &&
"Generating -x argument for unknown language (not precompiled).");
Lang = "ast";
break;
case Language::LLVM_IR:
Lang = "ir";
break;
}
GenerateArg(Args, OPT_x, Lang + Header + ModuleMap + Preprocessed, SA);
}
// OPT_INPUT has a unique class, generate it directly.
for (const auto &Input : Opts.Inputs)
Args.push_back(SA(Input.getFile()));
}
static bool ParseFrontendArgsImpl(FrontendOptions &Opts, ArgList &Args,
DiagnosticsEngine &Diags,
bool &IsHeaderFile) {
FrontendOptions &FrontendOpts = Opts;
bool Success = true;
unsigned NumErrorsBefore = Diags.getNumErrors();
#define FRONTEND_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef FRONTEND_OPTION_WITH_MARSHALLING
Opts.ProgramAction = frontend::ParseSyntaxOnly;
if (const Arg *A = Args.getLastArg(OPT_Action_Group)) {
OptSpecifier Opt = OptSpecifier(A->getOption().getID());
Optional<frontend::ActionKind> ProgramAction = getFrontendAction(Opt);
assert(ProgramAction && "Option specifier not in Action_Group.");
if (ProgramAction == frontend::ASTDump &&
(Opt == OPT_ast_dump_all_EQ || Opt == OPT_ast_dump_EQ)) {
unsigned Val = llvm::StringSwitch<unsigned>(A->getValue())
.CaseLower("default", ADOF_Default)
.CaseLower("json", ADOF_JSON)
.Default(std::numeric_limits<unsigned>::max());
if (Val != std::numeric_limits<unsigned>::max())
Opts.ASTDumpFormat = static_cast<ASTDumpOutputFormat>(Val);
else {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
Opts.ASTDumpFormat = ADOF_Default;
}
}
if (ProgramAction == frontend::FixIt && Opt == OPT_fixit_EQ)
Opts.FixItSuffix = A->getValue();
if (ProgramAction == frontend::GenerateInterfaceStubs) {
StringRef ArgStr =
Args.hasArg(OPT_interface_stub_version_EQ)
? Args.getLastArgValue(OPT_interface_stub_version_EQ)
: "experimental-ifs-v2";
if (ArgStr == "experimental-yaml-elf-v1" ||
ArgStr == "experimental-ifs-v1" ||
ArgStr == "experimental-tapi-elf-v1") {
std::string ErrorMessage =
"Invalid interface stub format: " + ArgStr.str() +
" is deprecated.";
Diags.Report(diag::err_drv_invalid_value)
<< "Must specify a valid interface stub format type, ie: "
"-interface-stub-version=experimental-ifs-v2"
<< ErrorMessage;
ProgramAction = frontend::ParseSyntaxOnly;
} else if (!ArgStr.startswith("experimental-ifs-")) {
std::string ErrorMessage =
"Invalid interface stub format: " + ArgStr.str() + ".";
[clang-ifs] Clang Interface Stubs, first version (second landing attempt). This change reverts r363649; effectively re-landing r363626. At this point clang::Index::CodegenNameGeneratorImpl has been refactored into clang::AST::ASTNameGenerator. This makes it so that the previous circular link dependency no longer exists, fixing the previous share lib (-DBUILD_SHARED_LIBS=ON) build issue which was the reason for r363649. Clang interface stubs (previously referred to as clang-ifsos) is a new frontend action in clang that allows the generation of stub files that contain mangled name info that can be used to produce a stub library. These stub libraries can be useful for breaking up build dependencies and controlling access to a library's internal symbols. Generation of these stubs can be invoked by: clang -fvisibility=<visibility> -emit-interface-stubs \ -interface-stub-version=<interface format> Notice that -fvisibility (along with use of visibility attributes) can be used to control what symbols get generated. Currently the interface format is experimental but there are a wide range of possibilities here. Currently clang-ifs produces .ifs files that can be thought of as analogous to object (.o) files, but just for the mangled symbol info. In a subsequent patch I intend to add support for merging the .ifs files into one .ifs/.ifso file that can be the input to something like llvm-elfabi to produce something like a .so file or .dll (but without any of the code, just symbols). Differential Revision: https://reviews.llvm.org/D60974 llvm-svn: 363948
2019-06-20 16:59:48 +00:00
Diags.Report(diag::err_drv_invalid_value)
<< "Must specify a valid interface stub format type, ie: "
"-interface-stub-version=experimental-ifs-v2"
<< ErrorMessage;
ProgramAction = frontend::ParseSyntaxOnly;
}
}
Opts.ProgramAction = *ProgramAction;
}
if (const Arg* A = Args.getLastArg(OPT_plugin)) {
Opts.Plugins.emplace_back(A->getValue(0));
Opts.ProgramAction = frontend::PluginAction;
Opts.ActionName = A->getValue();
}
for (const auto *AA : Args.filtered(OPT_plugin_arg))
Opts.PluginArgs[AA->getValue(0)].emplace_back(AA->getValue(1));
for (const std::string &Arg :
Args.getAllArgValues(OPT_ftest_module_file_extension_EQ)) {
std::string BlockName;
unsigned MajorVersion;
unsigned MinorVersion;
bool Hashed;
std::string UserInfo;
if (parseTestModuleFileExtensionArg(Arg, BlockName, MajorVersion,
MinorVersion, Hashed, UserInfo)) {
Diags.Report(diag::err_test_module_file_extension_format) << Arg;
continue;
}
// Add the testing module file extension.
Opts.ModuleFileExtensions.push_back(
std::make_shared<TestModuleFileExtension>(
BlockName, MajorVersion, MinorVersion, Hashed, UserInfo));
}
if (const Arg *A = Args.getLastArg(OPT_code_completion_at)) {
Opts.CodeCompletionAt =
ParsedSourceLocation::FromString(A->getValue());
if (Opts.CodeCompletionAt.FileName.empty())
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
}
Opts.Plugins = Args.getAllArgValues(OPT_load);
Opts.ASTDumpDecls = Args.hasArg(OPT_ast_dump, OPT_ast_dump_EQ);
Opts.ASTDumpAll = Args.hasArg(OPT_ast_dump_all, OPT_ast_dump_all_EQ);
// Only the -fmodule-file=<file> form.
for (const auto *A : Args.filtered(OPT_fmodule_file)) {
StringRef Val = A->getValue();
if (Val.find('=') == StringRef::npos)
Opts.ModuleFiles.push_back(std::string(Val));
}
if (Opts.ProgramAction != frontend::GenerateModule && Opts.IsSystemModule)
Diags.Report(diag::err_drv_argument_only_allowed_with) << "-fsystem-module"
<< "-emit-module";
if (Args.hasArg(OPT_aux_target_cpu))
Opts.AuxTargetCPU = std::string(Args.getLastArgValue(OPT_aux_target_cpu));
if (Args.hasArg(OPT_aux_target_feature))
Opts.AuxTargetFeatures = Args.getAllArgValues(OPT_aux_target_feature);
if (Opts.ARCMTAction != FrontendOptions::ARCMT_None &&
Opts.ObjCMTAction != FrontendOptions::ObjCMT_None) {
Diags.Report(diag::err_drv_argument_not_allowed_with)
<< "ARC migration" << "ObjC migration";
}
InputKind DashX(Language::Unknown);
if (const Arg *A = Args.getLastArg(OPT_x)) {
StringRef XValue = A->getValue();
// Parse suffixes: '<lang>(-header|[-module-map][-cpp-output])'.
// FIXME: Supporting '<lang>-header-cpp-output' would be useful.
bool Preprocessed = XValue.consume_back("-cpp-output");
bool ModuleMap = XValue.consume_back("-module-map");
IsHeaderFile = !Preprocessed && !ModuleMap &&
XValue != "precompiled-header" &&
XValue.consume_back("-header");
// Principal languages.
DashX = llvm::StringSwitch<InputKind>(XValue)
.Case("c", Language::C)
.Case("cl", Language::OpenCL)
.Case("cuda", Language::CUDA)
.Case("hip", Language::HIP)
.Case("c++", Language::CXX)
.Case("objective-c", Language::ObjC)
.Case("objective-c++", Language::ObjCXX)
.Case("renderscript", Language::RenderScript)
.Default(Language::Unknown);
// "objc[++]-cpp-output" is an acceptable synonym for
// "objective-c[++]-cpp-output".
if (DashX.isUnknown() && Preprocessed && !IsHeaderFile && !ModuleMap)
DashX = llvm::StringSwitch<InputKind>(XValue)
.Case("objc", Language::ObjC)
.Case("objc++", Language::ObjCXX)
.Default(Language::Unknown);
// Some special cases cannot be combined with suffixes.
if (DashX.isUnknown() && !Preprocessed && !ModuleMap && !IsHeaderFile)
DashX = llvm::StringSwitch<InputKind>(XValue)
.Case("cpp-output", InputKind(Language::C).getPreprocessed())
.Case("assembler-with-cpp", Language::Asm)
.Cases("ast", "pcm", "precompiled-header",
InputKind(Language::Unknown, InputKind::Precompiled))
.Case("ir", Language::LLVM_IR)
.Default(Language::Unknown);
if (DashX.isUnknown())
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
if (Preprocessed)
DashX = DashX.getPreprocessed();
if (ModuleMap)
DashX = DashX.withFormat(InputKind::ModuleMap);
}
// '-' is the default input if none is given.
std::vector<std::string> Inputs = Args.getAllArgValues(OPT_INPUT);
Opts.Inputs.clear();
if (Inputs.empty())
Inputs.push_back("-");
for (unsigned i = 0, e = Inputs.size(); i != e; ++i) {
InputKind IK = DashX;
if (IK.isUnknown()) {
IK = FrontendOptions::getInputKindForExtension(
StringRef(Inputs[i]).rsplit('.').second);
// FIXME: Warn on this?
if (IK.isUnknown())
IK = Language::C;
// FIXME: Remove this hack.
if (i == 0)
DashX = IK;
}
bool IsSystem = false;
// The -emit-module action implicitly takes a module map.
if (Opts.ProgramAction == frontend::GenerateModule &&
IK.getFormat() == InputKind::Source) {
IK = IK.withFormat(InputKind::ModuleMap);
IsSystem = Opts.IsSystemModule;
}
Opts.Inputs.emplace_back(std::move(Inputs[i]), IK, IsSystem);
}
Opts.DashX = DashX;
return Diags.getNumErrors() == NumErrorsBefore;
}
static bool ParseFrontendArgs(CompilerInvocation &Res, FrontendOptions &Opts,
ArgList &Args, DiagnosticsEngine &Diags,
bool &IsHeaderFile) {
FrontendOptions DummyOpts;
return RoundTrip(
[&IsHeaderFile](CompilerInvocation &Res, ArgList &Args,
DiagnosticsEngine &Diags) {
// ParseFrontendArgsImpl handles frontend action without querying the
// options. Let's do it now so RoundTrip considers us responsible for
// generating it.
for (const auto &Pair : getFrontendActionTable())
Args.hasArg(Pair.second);
return ParseFrontendArgsImpl(Res.getFrontendOpts(), Args, Diags,
IsHeaderFile);
},
[&IsHeaderFile](CompilerInvocation &Res,
SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA) {
GenerateFrontendArgs(Res.getFrontendOpts(), Args, SA, IsHeaderFile);
},
[&DummyOpts](CompilerInvocation &Res) {
std::swap(Res.getFrontendOpts(), DummyOpts);
},
Res, Args, Diags, "FrontendOptions");
}
std::string CompilerInvocation::GetResourcesPath(const char *Argv0,
void *MainAddr) {
std::string ClangExecutable =
llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
return Driver::GetResourcesPath(ClangExecutable, CLANG_RESOURCE_DIR);
}
void CompilerInvocation::GenerateHeaderSearchArgs(
HeaderSearchOptions &Opts, SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA) {
const HeaderSearchOptions *HeaderSearchOpts = &Opts;
#define HEADER_SEARCH_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef HEADER_SEARCH_OPTION_WITH_MARSHALLING
if (Opts.UseLibcxx)
GenerateArg(Args, OPT_stdlib_EQ, "libc++", SA);
if (!Opts.ModuleCachePath.empty())
GenerateArg(Args, OPT_fmodules_cache_path, Opts.ModuleCachePath, SA);
for (const auto &File : Opts.PrebuiltModuleFiles)
GenerateArg(Args, OPT_fmodule_file, File.first + "=" + File.second, SA);
for (const auto &Path : Opts.PrebuiltModulePaths)
GenerateArg(Args, OPT_fprebuilt_module_path, Path, SA);
for (const auto &Macro : Opts.ModulesIgnoreMacros)
GenerateArg(Args, OPT_fmodules_ignore_macro, Macro.val(), SA);
auto Matches = [](const HeaderSearchOptions::Entry &Entry,
llvm::ArrayRef<frontend::IncludeDirGroup> Groups,
llvm::Optional<bool> IsFramework,
llvm::Optional<bool> IgnoreSysRoot) {
return llvm::find(Groups, Entry.Group) != Groups.end() &&
(!IsFramework || (Entry.IsFramework == *IsFramework)) &&
(!IgnoreSysRoot || (Entry.IgnoreSysRoot == *IgnoreSysRoot));
};
auto It = Opts.UserEntries.begin();
auto End = Opts.UserEntries.end();
// Add -I..., -F..., and -index-header-map options in order.
for (; It < End &&
Matches(*It, {frontend::IndexHeaderMap, frontend::Angled}, None, true);
++It) {
OptSpecifier Opt = [It, Matches]() {
if (Matches(*It, frontend::IndexHeaderMap, true, true))
return OPT_F;
if (Matches(*It, frontend::IndexHeaderMap, false, true))
return OPT_I;
if (Matches(*It, frontend::Angled, true, true))
return OPT_F;
if (Matches(*It, frontend::Angled, false, true))
return OPT_I;
llvm_unreachable("Unexpected HeaderSearchOptions::Entry.");
}();
if (It->Group == frontend::IndexHeaderMap)
GenerateArg(Args, OPT_index_header_map, SA);
GenerateArg(Args, Opt, It->Path, SA);
};
// Note: some paths that came from "[-iprefix=xx] -iwithprefixbefore=yy" may
// have already been generated as "-I[xx]yy". If that's the case, their
// position on command line was such that this has no semantic impact on
// include paths.
for (; It < End &&
Matches(*It, {frontend::After, frontend::Angled}, false, true);
++It) {
OptSpecifier Opt =
It->Group == frontend::After ? OPT_iwithprefix : OPT_iwithprefixbefore;
GenerateArg(Args, Opt, It->Path, SA);
}
// Note: Some paths that came from "-idirafter=xxyy" may have already been
// generated as "-iwithprefix=xxyy". If that's the case, their position on
// command line was such that this has no semantic impact on include paths.
for (; It < End && Matches(*It, {frontend::After}, false, true); ++It)
GenerateArg(Args, OPT_idirafter, It->Path, SA);
for (; It < End && Matches(*It, {frontend::Quoted}, false, true); ++It)
GenerateArg(Args, OPT_iquote, It->Path, SA);
for (; It < End && Matches(*It, {frontend::System}, false, None); ++It)
GenerateArg(Args, It->IgnoreSysRoot ? OPT_isystem : OPT_iwithsysroot,
It->Path, SA);
for (; It < End && Matches(*It, {frontend::System}, true, true); ++It)
GenerateArg(Args, OPT_iframework, It->Path, SA);
for (; It < End && Matches(*It, {frontend::System}, true, false); ++It)
GenerateArg(Args, OPT_iframeworkwithsysroot, It->Path, SA);
// Add the paths for the various language specific isystem flags.
for (; It < End && Matches(*It, {frontend::CSystem}, false, true); ++It)
GenerateArg(Args, OPT_c_isystem, It->Path, SA);
for (; It < End && Matches(*It, {frontend::CXXSystem}, false, true); ++It)
GenerateArg(Args, OPT_cxx_isystem, It->Path, SA);
for (; It < End && Matches(*It, {frontend::ObjCSystem}, false, true); ++It)
GenerateArg(Args, OPT_objc_isystem, It->Path, SA);
for (; It < End && Matches(*It, {frontend::ObjCXXSystem}, false, true); ++It)
GenerateArg(Args, OPT_objcxx_isystem, It->Path, SA);
// Add the internal paths from a driver that detects standard include paths.
// Note: Some paths that came from "-internal-isystem" arguments may have
// already been generated as "-isystem". If that's the case, their position on
// command line was such that this has no semantic impact on include paths.
for (; It < End &&
Matches(*It, {frontend::System, frontend::ExternCSystem}, false, true);
++It) {
OptSpecifier Opt = It->Group == frontend::System
? OPT_internal_isystem
: OPT_internal_externc_isystem;
GenerateArg(Args, Opt, It->Path, SA);
}
assert(It == End && "Unhandled HeaderSearchOption::Entry.");
// Add the path prefixes which are implicitly treated as being system headers.
for (const auto &P : Opts.SystemHeaderPrefixes) {
OptSpecifier Opt = P.IsSystemHeader ? OPT_system_header_prefix
: OPT_no_system_header_prefix;
GenerateArg(Args, Opt, P.Prefix, SA);
}
for (const std::string &F : Opts.VFSOverlayFiles)
GenerateArg(Args, OPT_ivfsoverlay, F, SA);
}
static bool ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args,
DiagnosticsEngine &Diags,
const std::string &WorkingDir) {
HeaderSearchOptions *HeaderSearchOpts = &Opts;
bool Success = true;
#define HEADER_SEARCH_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef HEADER_SEARCH_OPTION_WITH_MARSHALLING
if (const Arg *A = Args.getLastArg(OPT_stdlib_EQ))
Opts.UseLibcxx = (strcmp(A->getValue(), "libc++") == 0);
// Canonicalize -fmodules-cache-path before storing it.
SmallString<128> P(Args.getLastArgValue(OPT_fmodules_cache_path));
if (!(P.empty() || llvm::sys::path::is_absolute(P))) {
if (WorkingDir.empty())
llvm::sys::fs::make_absolute(P);
else
llvm::sys::fs::make_absolute(WorkingDir, P);
}
llvm::sys::path::remove_dots(P);
Opts.ModuleCachePath = std::string(P.str());
// Only the -fmodule-file=<name>=<file> form.
for (const auto *A : Args.filtered(OPT_fmodule_file)) {
StringRef Val = A->getValue();
if (Val.find('=') != StringRef::npos){
auto Split = Val.split('=');
Opts.PrebuiltModuleFiles.insert(
{std::string(Split.first), std::string(Split.second)});
}
}
for (const auto *A : Args.filtered(OPT_fprebuilt_module_path))
Opts.AddPrebuiltModulePath(A->getValue());
for (const auto *A : Args.filtered(OPT_fmodules_ignore_macro)) {
StringRef MacroDef = A->getValue();
Opts.ModulesIgnoreMacros.insert(
llvm::CachedHashString(MacroDef.split('=').first));
}
// Add -I..., -F..., and -index-header-map options in order.
bool IsIndexHeaderMap = false;
bool IsSysrootSpecified =
Args.hasArg(OPT__sysroot_EQ) || Args.hasArg(OPT_isysroot);
for (const auto *A : Args.filtered(OPT_I, OPT_F, OPT_index_header_map)) {
if (A->getOption().matches(OPT_index_header_map)) {
// -index-header-map applies to the next -I or -F.
IsIndexHeaderMap = true;
continue;
}
frontend::IncludeDirGroup Group =
IsIndexHeaderMap ? frontend::IndexHeaderMap : frontend::Angled;
bool IsFramework = A->getOption().matches(OPT_F);
std::string Path = A->getValue();
if (IsSysrootSpecified && !IsFramework && A->getValue()[0] == '=') {
SmallString<32> Buffer;
llvm::sys::path::append(Buffer, Opts.Sysroot,
llvm::StringRef(A->getValue()).substr(1));
Path = std::string(Buffer.str());
}
Opts.AddPath(Path, Group, IsFramework,
/*IgnoreSysroot*/ true);
IsIndexHeaderMap = false;
}
// Add -iprefix/-iwithprefix/-iwithprefixbefore options.
StringRef Prefix = ""; // FIXME: This isn't the correct default prefix.
for (const auto *A :
Args.filtered(OPT_iprefix, OPT_iwithprefix, OPT_iwithprefixbefore)) {
if (A->getOption().matches(OPT_iprefix))
Prefix = A->getValue();
else if (A->getOption().matches(OPT_iwithprefix))
Opts.AddPath(Prefix.str() + A->getValue(), frontend::After, false, true);
else
Opts.AddPath(Prefix.str() + A->getValue(), frontend::Angled, false, true);
}
for (const auto *A : Args.filtered(OPT_idirafter))
Opts.AddPath(A->getValue(), frontend::After, false, true);
for (const auto *A : Args.filtered(OPT_iquote))
Opts.AddPath(A->getValue(), frontend::Quoted, false, true);
for (const auto *A : Args.filtered(OPT_isystem, OPT_iwithsysroot))
Opts.AddPath(A->getValue(), frontend::System, false,
!A->getOption().matches(OPT_iwithsysroot));
for (const auto *A : Args.filtered(OPT_iframework))
Opts.AddPath(A->getValue(), frontend::System, true, true);
for (const auto *A : Args.filtered(OPT_iframeworkwithsysroot))
Opts.AddPath(A->getValue(), frontend::System, /*IsFramework=*/true,
/*IgnoreSysRoot=*/false);
// Add the paths for the various language specific isystem flags.
for (const auto *A : Args.filtered(OPT_c_isystem))
Opts.AddPath(A->getValue(), frontend::CSystem, false, true);
for (const auto *A : Args.filtered(OPT_cxx_isystem))
Opts.AddPath(A->getValue(), frontend::CXXSystem, false, true);
for (const auto *A : Args.filtered(OPT_objc_isystem))
Opts.AddPath(A->getValue(), frontend::ObjCSystem, false,true);
for (const auto *A : Args.filtered(OPT_objcxx_isystem))
Opts.AddPath(A->getValue(), frontend::ObjCXXSystem, false, true);
// Add the internal paths from a driver that detects standard include paths.
for (const auto *A :
Args.filtered(OPT_internal_isystem, OPT_internal_externc_isystem)) {
frontend::IncludeDirGroup Group = frontend::System;
if (A->getOption().matches(OPT_internal_externc_isystem))
Group = frontend::ExternCSystem;
Opts.AddPath(A->getValue(), Group, false, true);
}
// Add the path prefixes which are implicitly treated as being system headers.
for (const auto *A :
Args.filtered(OPT_system_header_prefix, OPT_no_system_header_prefix))
Opts.AddSystemHeaderPrefix(
A->getValue(), A->getOption().matches(OPT_system_header_prefix));
for (const auto *A : Args.filtered(OPT_ivfsoverlay))
Opts.AddVFSOverlayFile(A->getValue());
return Success;
}
void CompilerInvocation::ParseHeaderSearchArgs(CompilerInvocation &Res,
HeaderSearchOptions &Opts,
ArgList &Args,
DiagnosticsEngine &Diags,
const std::string &WorkingDir) {
auto DummyOpts = std::make_shared<HeaderSearchOptions>();
RoundTrip(
[&WorkingDir](CompilerInvocation &Res, ArgList &Args,
DiagnosticsEngine &Diags) {
return ::ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Diags,
WorkingDir);
},
[](CompilerInvocation &Res, SmallVectorImpl<const char *> &GeneratedArgs,
CompilerInvocation::StringAllocator SA) {
GenerateHeaderSearchArgs(Res.getHeaderSearchOpts(), GeneratedArgs, SA);
},
[&DummyOpts](CompilerInvocation &Res) {
Res.HeaderSearchOpts.swap(DummyOpts);
},
Res, Args, Diags, "HeaderSearchOptions");
}
void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
const llvm::Triple &T,
std::vector<std::string> &Includes,
LangStandard::Kind LangStd) {
// Set some properties which depend solely on the input kind; it would be nice
// to move these to the language standard, and have the driver resolve the
// input kind + language standard.
//
// FIXME: Perhaps a better model would be for a single source file to have
// multiple language standards (C / C++ std, ObjC std, OpenCL std, OpenMP std)
// simultaneously active?
if (IK.getLanguage() == Language::Asm) {
Opts.AsmPreprocessor = 1;
} else if (IK.isObjectiveC()) {
Opts.ObjC = 1;
}
if (LangStd == LangStandard::lang_unspecified) {
// Based on the base language, pick one.
switch (IK.getLanguage()) {
case Language::Unknown:
case Language::LLVM_IR:
llvm_unreachable("Invalid input kind!");
case Language::OpenCL:
LangStd = LangStandard::lang_opencl10;
break;
case Language::CUDA:
LangStd = LangStandard::lang_cuda;
break;
case Language::Asm:
case Language::C:
#if defined(CLANG_DEFAULT_STD_C)
LangStd = CLANG_DEFAULT_STD_C;
#else
// The PS4 uses C99 as the default C standard.
if (T.isPS4())
LangStd = LangStandard::lang_gnu99;
else
LangStd = LangStandard::lang_gnu17;
#endif
break;
case Language::ObjC:
#if defined(CLANG_DEFAULT_STD_C)
LangStd = CLANG_DEFAULT_STD_C;
#else
LangStd = LangStandard::lang_gnu11;
#endif
break;
case Language::CXX:
case Language::ObjCXX:
#if defined(CLANG_DEFAULT_STD_CXX)
LangStd = CLANG_DEFAULT_STD_CXX;
#else
LangStd = LangStandard::lang_gnucxx14;
#endif
break;
case Language::RenderScript:
LangStd = LangStandard::lang_c99;
break;
case Language::HIP:
LangStd = LangStandard::lang_hip;
break;
}
}
const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd);
Opts.LangStd = LangStd;
Opts.LineComment = Std.hasLineComments();
Opts.C99 = Std.isC99();
Opts.C11 = Std.isC11();
Opts.C17 = Std.isC17();
Opts.C2x = Std.isC2x();
Opts.CPlusPlus = Std.isCPlusPlus();
Opts.CPlusPlus11 = Std.isCPlusPlus11();
Opts.CPlusPlus14 = Std.isCPlusPlus14();
Opts.CPlusPlus17 = Std.isCPlusPlus17();
Opts.CPlusPlus20 = Std.isCPlusPlus20();
Opts.CPlusPlus2b = Std.isCPlusPlus2b();
Opts.GNUMode = Std.isGNUMode();
Opts.GNUCVersion = 0;
Opts.HexFloats = Std.hasHexFloats();
Opts.ImplicitInt = Std.hasImplicitInt();
Opts.CPlusPlusModules = Opts.CPlusPlus20;
// Set OpenCL Version.
Opts.OpenCL = Std.isOpenCL();
if (LangStd == LangStandard::lang_opencl10)
Opts.OpenCLVersion = 100;
else if (LangStd == LangStandard::lang_opencl11)
Opts.OpenCLVersion = 110;
else if (LangStd == LangStandard::lang_opencl12)
Opts.OpenCLVersion = 120;
else if (LangStd == LangStandard::lang_opencl20)
Opts.OpenCLVersion = 200;
else if (LangStd == LangStandard::lang_opencl30)
Opts.OpenCLVersion = 300;
else if (LangStd == LangStandard::lang_openclcpp)
Opts.OpenCLCPlusPlusVersion = 100;
// OpenCL has some additional defaults.
if (Opts.OpenCL) {
Opts.AltiVec = 0;
Opts.ZVector = 0;
Opts.setDefaultFPContractMode(LangOptions::FPM_On);
Opts.OpenCLCPlusPlus = Opts.CPlusPlus;
Opts.OpenCLPipe = Opts.OpenCLCPlusPlus || Opts.OpenCLVersion == 200;
Opts.OpenCLGenericAddressSpace =
Opts.OpenCLCPlusPlus || Opts.OpenCLVersion == 200;
// Include default header file for OpenCL.
if (Opts.IncludeDefaultHeader) {
if (Opts.DeclareOpenCLBuiltins) {
// Only include base header file for builtin types and constants.
Includes.push_back("opencl-c-base.h");
} else {
Includes.push_back("opencl-c.h");
}
}
}
Opts.HIP = IK.getLanguage() == Language::HIP;
Opts.CUDA = IK.getLanguage() == Language::CUDA || Opts.HIP;
[HIP] Fix regressions due to fp contract change Recently HIP toolchain made a change to use clang instead of opt/llc to do compilation (https://reviews.llvm.org/D81861). The intention is to make HIP toolchain canonical like other toolchains. However, this change introduced an unintentional change regarding backend fp fuse option, which caused regressions in some HIP applications. Basically before the change, HIP toolchain used clang to generate bitcode, then use opt/llc to optimize bitcode and generate ISA. As such, the amdgpu backend takes the default fp fuse mode which is 'Standard'. This mode respect contract flag of fmul/fadd instructions and do not fuse fmul/fadd instructions without contract flag. However, after the change, HIP toolchain now use clang to generate IR, do optimization, and generate ISA as one process. Now amdgpu backend fp fuse option is determined by -ffp-contract option, which is 'fast' by default. And this -ffp-contract=fast language option is translated to 'Fast' fp fuse option in backend. Suddenly backend starts to fuse fmul/fadd instructions without contract flag. This causes wrong result for some device library functions, e.g. tan(-1e20), which should return 0.8446, now returns -0.933. What is worse is that since backend with 'Fast' fp fuse option does not respect contract flag, there is no way to use #pragma clang fp contract directive to enforce fp contract requirements. This patch fixes the regression by introducing a new value 'fast-honor-pragmas' for -ffp-contract and use it for HIP by default. 'fast-honor-pragmas' is equivalent to 'fast' in frontend but let the backend to use 'Standard' fp fuse option. 'fast-honor-pragmas' is useful since 'Fast' fp fuse option in backend does not honor contract flag, it is of little use to HIP applications since all code with #pragma STDC FP_CONTRACT or any IR from a source compiled with -ffp-contract=on is broken. Differential Revision: https://reviews.llvm.org/D90174
2020-10-23 16:24:48 -04:00
if (Opts.HIP) {
// HIP toolchain does not support 'Fast' FPOpFusion in backends since it
// fuses multiplication/addition instructions without contract flag from
// device library functions in LLVM bitcode, which causes accuracy loss in
// certain math functions, e.g. tan(-1e20) becomes -0.933 instead of 0.8446.
// For device library functions in bitcode to work, 'Strict' or 'Standard'
// FPOpFusion options in backends is needed. Therefore 'fast-honor-pragmas'
// FP contract option is used to allow fuse across statements in frontend
// whereas respecting contract flag in backend.
Opts.setDefaultFPContractMode(LangOptions::FPM_FastHonorPragmas);
} else if (Opts.CUDA) {
// Allow fuse across statements disregarding pragmas.
Opts.setDefaultFPContractMode(LangOptions::FPM_Fast);
[HIP] Fix regressions due to fp contract change Recently HIP toolchain made a change to use clang instead of opt/llc to do compilation (https://reviews.llvm.org/D81861). The intention is to make HIP toolchain canonical like other toolchains. However, this change introduced an unintentional change regarding backend fp fuse option, which caused regressions in some HIP applications. Basically before the change, HIP toolchain used clang to generate bitcode, then use opt/llc to optimize bitcode and generate ISA. As such, the amdgpu backend takes the default fp fuse mode which is 'Standard'. This mode respect contract flag of fmul/fadd instructions and do not fuse fmul/fadd instructions without contract flag. However, after the change, HIP toolchain now use clang to generate IR, do optimization, and generate ISA as one process. Now amdgpu backend fp fuse option is determined by -ffp-contract option, which is 'fast' by default. And this -ffp-contract=fast language option is translated to 'Fast' fp fuse option in backend. Suddenly backend starts to fuse fmul/fadd instructions without contract flag. This causes wrong result for some device library functions, e.g. tan(-1e20), which should return 0.8446, now returns -0.933. What is worse is that since backend with 'Fast' fp fuse option does not respect contract flag, there is no way to use #pragma clang fp contract directive to enforce fp contract requirements. This patch fixes the regression by introducing a new value 'fast-honor-pragmas' for -ffp-contract and use it for HIP by default. 'fast-honor-pragmas' is equivalent to 'fast' in frontend but let the backend to use 'Standard' fp fuse option. 'fast-honor-pragmas' is useful since 'Fast' fp fuse option in backend does not honor contract flag, it is of little use to HIP applications since all code with #pragma STDC FP_CONTRACT or any IR from a source compiled with -ffp-contract=on is broken. Differential Revision: https://reviews.llvm.org/D90174
2020-10-23 16:24:48 -04:00
}
Opts.RenderScript = IK.getLanguage() == Language::RenderScript;
// OpenCL and C++ both have bool, true, false keywords.
Opts.Bool = Opts.OpenCL || Opts.CPlusPlus;
// OpenCL has half keyword
Opts.Half = Opts.OpenCL;
}
/// Check if input file kind and language standard are compatible.
static bool IsInputCompatibleWithStandard(InputKind IK,
const LangStandard &S) {
switch (IK.getLanguage()) {
case Language::Unknown:
case Language::LLVM_IR:
llvm_unreachable("should not parse language flags for this input");
case Language::C:
case Language::ObjC:
case Language::RenderScript:
return S.getLanguage() == Language::C;
case Language::OpenCL:
return S.getLanguage() == Language::OpenCL;
case Language::CXX:
case Language::ObjCXX:
return S.getLanguage() == Language::CXX;
case Language::CUDA:
// FIXME: What -std= values should be permitted for CUDA compilations?
return S.getLanguage() == Language::CUDA ||
S.getLanguage() == Language::CXX;
case Language::HIP:
return S.getLanguage() == Language::CXX || S.getLanguage() == Language::HIP;
case Language::Asm:
// Accept (and ignore) all -std= values.
// FIXME: The -std= value is not ignored; it affects the tokenization
// and preprocessing rules if we're preprocessing this asm input.
return true;
}
llvm_unreachable("unexpected input language");
}
/// Get language name for given input kind.
static const StringRef GetInputKindName(InputKind IK) {
switch (IK.getLanguage()) {
case Language::C:
return "C";
case Language::ObjC:
return "Objective-C";
case Language::CXX:
return "C++";
case Language::ObjCXX:
return "Objective-C++";
case Language::OpenCL:
return "OpenCL";
case Language::CUDA:
return "CUDA";
case Language::RenderScript:
return "RenderScript";
case Language::HIP:
return "HIP";
case Language::Asm:
return "Asm";
case Language::LLVM_IR:
return "LLVM IR";
case Language::Unknown:
break;
}
llvm_unreachable("unknown input language");
}
void CompilerInvocation::GenerateLangArgs(const LangOptions &Opts,
SmallVectorImpl<const char *> &Args,
StringAllocator SA,
const llvm::Triple &T) {
OptSpecifier StdOpt;
switch (Opts.LangStd) {
case LangStandard::lang_opencl10:
case LangStandard::lang_opencl11:
case LangStandard::lang_opencl12:
case LangStandard::lang_opencl20:
case LangStandard::lang_opencl30:
case LangStandard::lang_openclcpp:
StdOpt = OPT_cl_std_EQ;
break;
default:
StdOpt = OPT_std_EQ;
break;
}
auto LangStandard = LangStandard::getLangStandardForKind(Opts.LangStd);
GenerateArg(Args, StdOpt, LangStandard.getName(), SA);
if (Opts.IncludeDefaultHeader)
GenerateArg(Args, OPT_finclude_default_header, SA);
if (Opts.DeclareOpenCLBuiltins)
GenerateArg(Args, OPT_fdeclare_opencl_builtins, SA);
const LangOptions *LangOpts = &Opts;
#define LANG_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef LANG_OPTION_WITH_MARSHALLING
// The '-fcf-protection=' option is generated by CodeGenOpts generator.
if (Opts.ObjC) {
std::string Buffer;
llvm::raw_string_ostream Stream(Buffer);
Stream << Opts.ObjCRuntime;
GenerateArg(Args, OPT_fobjc_runtime_EQ, Stream.str(), SA);
if (Opts.GC == LangOptions::GCOnly)
GenerateArg(Args, OPT_fobjc_gc_only, SA);
else if (Opts.GC == LangOptions::HybridGC)
GenerateArg(Args, OPT_fobjc_gc, SA);
else if (Opts.ObjCAutoRefCount == 1)
GenerateArg(Args, OPT_fobjc_arc, SA);
if (Opts.ObjCWeakRuntime)
GenerateArg(Args, OPT_fobjc_runtime_has_weak, SA);
if (Opts.ObjCWeak)
GenerateArg(Args, OPT_fobjc_weak, SA);
if (Opts.ObjCSubscriptingLegacyRuntime)
GenerateArg(Args, OPT_fobjc_subscripting_legacy_runtime, SA);
}
if (Opts.GNUCVersion != 0) {
unsigned Major = Opts.GNUCVersion / 100 / 100;
unsigned Minor = (Opts.GNUCVersion / 100) % 100;
unsigned Patch = Opts.GNUCVersion % 100;
GenerateArg(Args, OPT_fgnuc_version_EQ,
Twine(Major) + "." + Twine(Minor) + "." + Twine(Patch), SA);
}
if (Opts.SignedOverflowBehavior == LangOptions::SOB_Trapping) {
GenerateArg(Args, OPT_ftrapv, SA);
GenerateArg(Args, OPT_ftrapv_handler, Opts.OverflowHandler, SA);
} else if (Opts.SignedOverflowBehavior == LangOptions::SOB_Defined) {
GenerateArg(Args, OPT_fwrapv, SA);
}
if (Opts.MSCompatibilityVersion != 0) {
unsigned Major = Opts.MSCompatibilityVersion / 10000000;
unsigned Minor = (Opts.MSCompatibilityVersion / 100000) % 100;
unsigned Subminor = Opts.MSCompatibilityVersion % 100000;
GenerateArg(Args, OPT_fms_compatibility_version,
Twine(Major) + "." + Twine(Minor) + "." + Twine(Subminor), SA);
}
if ((!Opts.GNUMode && !Opts.MSVCCompat && !Opts.CPlusPlus17) || T.isOSzOS()) {
if (!Opts.Trigraphs)
GenerateArg(Args, OPT_fno_trigraphs, SA);
} else {
if (Opts.Trigraphs)
GenerateArg(Args, OPT_ftrigraphs, SA);
}
if (Opts.Blocks && !(Opts.OpenCL && Opts.OpenCLVersion == 200))
GenerateArg(Args, OPT_fblocks, SA);
if (Opts.ConvergentFunctions &&
!(Opts.OpenCL || (Opts.CUDA && Opts.CUDAIsDevice) || Opts.SYCLIsDevice))
GenerateArg(Args, OPT_fconvergent_functions, SA);
if (Opts.NoBuiltin && !Opts.Freestanding)
GenerateArg(Args, OPT_fno_builtin, SA);
if (!Opts.NoBuiltin)
for (const auto &Func : Opts.NoBuiltinFuncs)
GenerateArg(Args, OPT_fno_builtin_, Func, SA);
if (Opts.LongDoubleSize == 128)
GenerateArg(Args, OPT_mlong_double_128, SA);
else if (Opts.LongDoubleSize == 64)
GenerateArg(Args, OPT_mlong_double_64, SA);
// Not generating '-mrtd', it's just an alias for '-fdefault-calling-conv='.
// OpenMP was requested via '-fopenmp', not implied by '-fopenmp-simd' or
// '-fopenmp-targets='.
if (Opts.OpenMP && !Opts.OpenMPSimd) {
GenerateArg(Args, OPT_fopenmp, SA);
if (Opts.OpenMP != 50)
GenerateArg(Args, OPT_fopenmp_version_EQ, Twine(Opts.OpenMP), SA);
if (!Opts.OpenMPUseTLS)
GenerateArg(Args, OPT_fnoopenmp_use_tls, SA);
if (Opts.OpenMPIsDevice)
GenerateArg(Args, OPT_fopenmp_is_device, SA);
if (Opts.OpenMPIRBuilder)
GenerateArg(Args, OPT_fopenmp_enable_irbuilder, SA);
}
if (Opts.OpenMPSimd) {
GenerateArg(Args, OPT_fopenmp_simd, SA);
if (Opts.OpenMP != 50)
GenerateArg(Args, OPT_fopenmp_version_EQ, Twine(Opts.OpenMP), SA);
}
if (Opts.OpenMPCUDANumSMs != 0)
GenerateArg(Args, OPT_fopenmp_cuda_number_of_sm_EQ,
Twine(Opts.OpenMPCUDANumSMs), SA);
if (Opts.OpenMPCUDABlocksPerSM != 0)
GenerateArg(Args, OPT_fopenmp_cuda_blocks_per_sm_EQ,
Twine(Opts.OpenMPCUDABlocksPerSM), SA);
if (Opts.OpenMPCUDAReductionBufNum != 1024)
GenerateArg(Args, OPT_fopenmp_cuda_teams_reduction_recs_num_EQ,
Twine(Opts.OpenMPCUDAReductionBufNum), SA);
if (!Opts.OMPTargetTriples.empty()) {
std::string Targets;
llvm::raw_string_ostream OS(Targets);
llvm::interleave(
Opts.OMPTargetTriples, OS,
[&OS](const llvm::Triple &T) { OS << T.str(); }, ",");
GenerateArg(Args, OPT_fopenmp_targets_EQ, OS.str(), SA);
}
if (!Opts.OMPHostIRFile.empty())
GenerateArg(Args, OPT_fopenmp_host_ir_file_path, Opts.OMPHostIRFile, SA);
if (Opts.OpenMPCUDAMode)
GenerateArg(Args, OPT_fopenmp_cuda_mode, SA);
if (Opts.OpenMPCUDATargetParallel)
GenerateArg(Args, OPT_fopenmp_cuda_parallel_target_regions, SA);
if (Opts.OpenMPCUDAForceFullRuntime)
GenerateArg(Args, OPT_fopenmp_cuda_force_full_runtime, SA);
// The arguments used to set 'Optimize' and 'OptimizeSize' will be generated
// by CodeGenOptions.
if (Opts.NoInlineDefine && Opts.Optimize)
GenerateArg(Args, OPT_fno_inline, SA);
if (Opts.DefaultFPContractMode == LangOptions::FPM_Fast)
GenerateArg(Args, OPT_ffp_contract, "fast", SA);
else if (Opts.DefaultFPContractMode == LangOptions::FPM_On)
GenerateArg(Args, OPT_ffp_contract, "on", SA);
else if (Opts.DefaultFPContractMode == LangOptions::FPM_Off)
GenerateArg(Args, OPT_ffp_contract, "off", SA);
else if (Opts.DefaultFPContractMode == LangOptions::FPM_FastHonorPragmas)
GenerateArg(Args, OPT_ffp_contract, "fast-honor-pragmas", SA);
for (StringRef Sanitizer : serializeSanitizerKinds(Opts.Sanitize))
GenerateArg(Args, OPT_fsanitize_EQ, Sanitizer, SA);
// Conflating '-fsanitize-system-blacklist' and '-fsanitize-blacklist'.
for (const std::string &F : Opts.SanitizerBlacklistFiles)
GenerateArg(Args, OPT_fsanitize_blacklist, F, SA);
if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver3_8)
GenerateArg(Args, OPT_fclang_abi_compat_EQ, "3.8", SA);
else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver4)
GenerateArg(Args, OPT_fclang_abi_compat_EQ, "4.0", SA);
else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver6)
GenerateArg(Args, OPT_fclang_abi_compat_EQ, "6.0", SA);
else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver7)
GenerateArg(Args, OPT_fclang_abi_compat_EQ, "7.0", SA);
else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver9)
GenerateArg(Args, OPT_fclang_abi_compat_EQ, "9.0", SA);
else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver11)
GenerateArg(Args, OPT_fclang_abi_compat_EQ, "11.0", SA);
if (Opts.getSignReturnAddressScope() ==
LangOptions::SignReturnAddressScopeKind::All)
GenerateArg(Args, OPT_msign_return_address_EQ, "all", SA);
else if (Opts.getSignReturnAddressScope() ==
LangOptions::SignReturnAddressScopeKind::NonLeaf)
GenerateArg(Args, OPT_msign_return_address_EQ, "non-leaf", SA);
if (Opts.getSignReturnAddressKey() ==
LangOptions::SignReturnAddressKeyKind::BKey)
GenerateArg(Args, OPT_msign_return_address_key_EQ, "b_key", SA);
}
bool CompilerInvocation::ParseLangArgsImpl(LangOptions &Opts, ArgList &Args,
InputKind IK, const llvm::Triple &T,
std::vector<std::string> &Includes,
DiagnosticsEngine &Diags) {
unsigned NumErrorsBefore = Diags.getNumErrors();
// FIXME: Cleanup per-file based stuff.
LangStandard::Kind LangStd = LangStandard::lang_unspecified;
if (const Arg *A = Args.getLastArg(OPT_std_EQ)) {
LangStd = LangStandard::getLangKind(A->getValue());
if (LangStd == LangStandard::lang_unspecified) {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
// Report supported standards with short description.
for (unsigned KindValue = 0;
KindValue != LangStandard::lang_unspecified;
++KindValue) {
const LangStandard &Std = LangStandard::getLangStandardForKind(
static_cast<LangStandard::Kind>(KindValue));
if (IsInputCompatibleWithStandard(IK, Std)) {
auto Diag = Diags.Report(diag::note_drv_use_standard);
Diag << Std.getName() << Std.getDescription();
unsigned NumAliases = 0;
#define LANGSTANDARD(id, name, lang, desc, features)
#define LANGSTANDARD_ALIAS(id, alias) \
if (KindValue == LangStandard::lang_##id) ++NumAliases;
#define LANGSTANDARD_ALIAS_DEPR(id, alias)
#include "clang/Basic/LangStandards.def"
Diag << NumAliases;
#define LANGSTANDARD(id, name, lang, desc, features)
#define LANGSTANDARD_ALIAS(id, alias) \
if (KindValue == LangStandard::lang_##id) Diag << alias;
#define LANGSTANDARD_ALIAS_DEPR(id, alias)
#include "clang/Basic/LangStandards.def"
}
}
} else {
// Valid standard, check to make sure language and standard are
// compatible.
const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd);
if (!IsInputCompatibleWithStandard(IK, Std)) {
Diags.Report(diag::err_drv_argument_not_allowed_with)
<< A->getAsString(Args) << GetInputKindName(IK);
}
}
}
// -cl-std only applies for OpenCL language standards.
// Override the -std option in this case.
2010-12-04 01:51:40 +00:00
if (const Arg *A = Args.getLastArg(OPT_cl_std_EQ)) {
LangStandard::Kind OpenCLLangStd
= llvm::StringSwitch<LangStandard::Kind>(A->getValue())
.Cases("cl", "CL", LangStandard::lang_opencl10)
.Cases("cl1.0", "CL1.0", LangStandard::lang_opencl10)
.Cases("cl1.1", "CL1.1", LangStandard::lang_opencl11)
.Cases("cl1.2", "CL1.2", LangStandard::lang_opencl12)
.Cases("cl2.0", "CL2.0", LangStandard::lang_opencl20)
.Cases("cl3.0", "CL3.0", LangStandard::lang_opencl30)
.Cases("clc++", "CLC++", LangStandard::lang_openclcpp)
.Default(LangStandard::lang_unspecified);
if (OpenCLLangStd == LangStandard::lang_unspecified) {
2010-12-04 01:51:40 +00:00
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
2010-12-04 01:51:40 +00:00
}
else
LangStd = OpenCLLangStd;
2010-12-04 01:51:40 +00:00
}
// These need to be parsed now. They are used to set OpenCL defaults.
Opts.IncludeDefaultHeader = Args.hasArg(OPT_finclude_default_header);
Opts.DeclareOpenCLBuiltins = Args.hasArg(OPT_fdeclare_opencl_builtins);
CompilerInvocation::setLangDefaults(Opts, IK, T, Includes, LangStd);
// The key paths of codegen options defined in Options.td start with
// "LangOpts->". Let's provide the expected variable name and type.
LangOptions *LangOpts = &Opts;
bool Success = true;
#define LANG_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef LANG_OPTION_WITH_MARSHALLING
if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ)) {
StringRef Name = A->getValue();
if (Name == "full" || Name == "branch") {
Opts.CFProtectionBranch = 1;
}
}
if (auto *A = Args.getLastArg(OPT_cuid_EQ)) {
Opts.CUID = std::string(A->getValue());
}
if (Opts.ObjC) {
if (Arg *arg = Args.getLastArg(OPT_fobjc_runtime_EQ)) {
StringRef value = arg->getValue();
if (Opts.ObjCRuntime.tryParse(value))
Diags.Report(diag::err_drv_unknown_objc_runtime) << value;
}
if (Args.hasArg(OPT_fobjc_gc_only))
Opts.setGC(LangOptions::GCOnly);
else if (Args.hasArg(OPT_fobjc_gc))
Opts.setGC(LangOptions::HybridGC);
else if (Args.hasArg(OPT_fobjc_arc)) {
Opts.ObjCAutoRefCount = 1;
if (!Opts.ObjCRuntime.allowsARC())
Diags.Report(diag::err_arc_unsupported_on_runtime);
Define weak and __weak to mean ARC-style weak references, even in MRC. Previously, __weak was silently accepted and ignored in MRC mode. That makes this a potentially source-breaking change that we have to roll out cautiously. Accordingly, for the time being, actual support for __weak references in MRC is experimental, and the compiler will reject attempts to actually form such references. The intent is to eventually enable the feature by default in all non-GC modes. (It is, of course, incompatible with ObjC GC's interpretation of __weak.) If you like, you can enable this feature with -Xclang -fobjc-weak but like any -Xclang option, this option may be removed at any point, e.g. if/when it is eventually enabled by default. This patch also enables the use of the ARC __unsafe_unretained qualifier in MRC. Unlike __weak, this is being enabled immediately. Since variables are essentially __unsafe_unretained by default in MRC, the only practical uses are (1) communication and (2) changing the default behavior of by-value block capture. As an implementation matter, this means that the ObjC ownership qualifiers may appear in any ObjC language mode, and so this patch removes a number of checks for getLangOpts().ObjCAutoRefCount that were guarding the processing of these qualifiers. I don't expect this to be a significant drain on performance; it may even be faster to just check for these qualifiers directly on a type (since it's probably in a register anyway) than to do N dependent loads to grab the LangOptions. rdar://9674298 llvm-svn: 251041
2015-10-22 18:38:17 +00:00
}
Define weak and __weak to mean ARC-style weak references, even in MRC. Previously, __weak was silently accepted and ignored in MRC mode. That makes this a potentially source-breaking change that we have to roll out cautiously. Accordingly, for the time being, actual support for __weak references in MRC is experimental, and the compiler will reject attempts to actually form such references. The intent is to eventually enable the feature by default in all non-GC modes. (It is, of course, incompatible with ObjC GC's interpretation of __weak.) If you like, you can enable this feature with -Xclang -fobjc-weak but like any -Xclang option, this option may be removed at any point, e.g. if/when it is eventually enabled by default. This patch also enables the use of the ARC __unsafe_unretained qualifier in MRC. Unlike __weak, this is being enabled immediately. Since variables are essentially __unsafe_unretained by default in MRC, the only practical uses are (1) communication and (2) changing the default behavior of by-value block capture. As an implementation matter, this means that the ObjC ownership qualifiers may appear in any ObjC language mode, and so this patch removes a number of checks for getLangOpts().ObjCAutoRefCount that were guarding the processing of these qualifiers. I don't expect this to be a significant drain on performance; it may even be faster to just check for these qualifiers directly on a type (since it's probably in a register anyway) than to do N dependent loads to grab the LangOptions. rdar://9674298 llvm-svn: 251041
2015-10-22 18:38:17 +00:00
// ObjCWeakRuntime tracks whether the runtime supports __weak, not
// whether the feature is actually enabled. This is predominantly
// determined by -fobjc-runtime, but we allow it to be overridden
// from the command line for testing purposes.
if (Args.hasArg(OPT_fobjc_runtime_has_weak))
Opts.ObjCWeakRuntime = 1;
else
Opts.ObjCWeakRuntime = Opts.ObjCRuntime.allowsWeak();
// ObjCWeak determines whether __weak is actually enabled.
// Note that we allow -fno-objc-weak to disable this even in ARC mode.
if (auto weakArg = Args.getLastArg(OPT_fobjc_weak, OPT_fno_objc_weak)) {
if (!weakArg->getOption().matches(OPT_fobjc_weak)) {
assert(!Opts.ObjCWeak);
} else if (Opts.getGC() != LangOptions::NonGC) {
Define weak and __weak to mean ARC-style weak references, even in MRC. Previously, __weak was silently accepted and ignored in MRC mode. That makes this a potentially source-breaking change that we have to roll out cautiously. Accordingly, for the time being, actual support for __weak references in MRC is experimental, and the compiler will reject attempts to actually form such references. The intent is to eventually enable the feature by default in all non-GC modes. (It is, of course, incompatible with ObjC GC's interpretation of __weak.) If you like, you can enable this feature with -Xclang -fobjc-weak but like any -Xclang option, this option may be removed at any point, e.g. if/when it is eventually enabled by default. This patch also enables the use of the ARC __unsafe_unretained qualifier in MRC. Unlike __weak, this is being enabled immediately. Since variables are essentially __unsafe_unretained by default in MRC, the only practical uses are (1) communication and (2) changing the default behavior of by-value block capture. As an implementation matter, this means that the ObjC ownership qualifiers may appear in any ObjC language mode, and so this patch removes a number of checks for getLangOpts().ObjCAutoRefCount that were guarding the processing of these qualifiers. I don't expect this to be a significant drain on performance; it may even be faster to just check for these qualifiers directly on a type (since it's probably in a register anyway) than to do N dependent loads to grab the LangOptions. rdar://9674298 llvm-svn: 251041
2015-10-22 18:38:17 +00:00
Diags.Report(diag::err_objc_weak_with_gc);
} else if (!Opts.ObjCWeakRuntime) {
Define weak and __weak to mean ARC-style weak references, even in MRC. Previously, __weak was silently accepted and ignored in MRC mode. That makes this a potentially source-breaking change that we have to roll out cautiously. Accordingly, for the time being, actual support for __weak references in MRC is experimental, and the compiler will reject attempts to actually form such references. The intent is to eventually enable the feature by default in all non-GC modes. (It is, of course, incompatible with ObjC GC's interpretation of __weak.) If you like, you can enable this feature with -Xclang -fobjc-weak but like any -Xclang option, this option may be removed at any point, e.g. if/when it is eventually enabled by default. This patch also enables the use of the ARC __unsafe_unretained qualifier in MRC. Unlike __weak, this is being enabled immediately. Since variables are essentially __unsafe_unretained by default in MRC, the only practical uses are (1) communication and (2) changing the default behavior of by-value block capture. As an implementation matter, this means that the ObjC ownership qualifiers may appear in any ObjC language mode, and so this patch removes a number of checks for getLangOpts().ObjCAutoRefCount that were guarding the processing of these qualifiers. I don't expect this to be a significant drain on performance; it may even be faster to just check for these qualifiers directly on a type (since it's probably in a register anyway) than to do N dependent loads to grab the LangOptions. rdar://9674298 llvm-svn: 251041
2015-10-22 18:38:17 +00:00
Diags.Report(diag::err_objc_weak_unsupported);
} else {
Opts.ObjCWeak = 1;
Define weak and __weak to mean ARC-style weak references, even in MRC. Previously, __weak was silently accepted and ignored in MRC mode. That makes this a potentially source-breaking change that we have to roll out cautiously. Accordingly, for the time being, actual support for __weak references in MRC is experimental, and the compiler will reject attempts to actually form such references. The intent is to eventually enable the feature by default in all non-GC modes. (It is, of course, incompatible with ObjC GC's interpretation of __weak.) If you like, you can enable this feature with -Xclang -fobjc-weak but like any -Xclang option, this option may be removed at any point, e.g. if/when it is eventually enabled by default. This patch also enables the use of the ARC __unsafe_unretained qualifier in MRC. Unlike __weak, this is being enabled immediately. Since variables are essentially __unsafe_unretained by default in MRC, the only practical uses are (1) communication and (2) changing the default behavior of by-value block capture. As an implementation matter, this means that the ObjC ownership qualifiers may appear in any ObjC language mode, and so this patch removes a number of checks for getLangOpts().ObjCAutoRefCount that were guarding the processing of these qualifiers. I don't expect this to be a significant drain on performance; it may even be faster to just check for these qualifiers directly on a type (since it's probably in a register anyway) than to do N dependent loads to grab the LangOptions. rdar://9674298 llvm-svn: 251041
2015-10-22 18:38:17 +00:00
}
} else if (Opts.ObjCAutoRefCount) {
Opts.ObjCWeak = Opts.ObjCWeakRuntime;
}
if (Args.hasArg(OPT_fobjc_subscripting_legacy_runtime))
Opts.ObjCSubscriptingLegacyRuntime =
(Opts.ObjCRuntime.getKind() == ObjCRuntime::FragileMacOSX);
}
if (Arg *A = Args.getLastArg(options::OPT_fgnuc_version_EQ)) {
// Check that the version has 1 to 3 components and the minor and patch
// versions fit in two decimal digits.
VersionTuple GNUCVer;
bool Invalid = GNUCVer.tryParse(A->getValue());
unsigned Major = GNUCVer.getMajor();
unsigned Minor = GNUCVer.getMinor().getValueOr(0);
unsigned Patch = GNUCVer.getSubminor().getValueOr(0);
if (Invalid || GNUCVer.getBuild() || Minor >= 100 || Patch >= 100) {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
}
Opts.GNUCVersion = Major * 100 * 100 + Minor * 100 + Patch;
}
if (Args.hasArg(OPT_ftrapv)) {
2010-10-21 03:16:25 +00:00
Opts.setSignedOverflowBehavior(LangOptions::SOB_Trapping);
// Set the handler, if one is specified.
Opts.OverflowHandler =
std::string(Args.getLastArgValue(OPT_ftrapv_handler));
}
else if (Args.hasArg(OPT_fwrapv))
2010-10-21 03:16:25 +00:00
Opts.setSignedOverflowBehavior(LangOptions::SOB_Defined);
Opts.MSCompatibilityVersion = 0;
if (const Arg *A = Args.getLastArg(OPT_fms_compatibility_version)) {
VersionTuple VT;
if (VT.tryParse(A->getValue()))
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args)
<< A->getValue();
Opts.MSCompatibilityVersion = VT.getMajor() * 10000000 +
VT.getMinor().getValueOr(0) * 100000 +
VT.getSubminor().getValueOr(0);
}
// Mimicking gcc's behavior, trigraphs are only enabled if -trigraphs
// is specified, or -std is set to a conforming mode.
// Trigraphs are disabled by default in c++1z onwards.
// For z/OS, trigraphs are enabled by default (without regard to the above).
Opts.Trigraphs =
(!Opts.GNUMode && !Opts.MSVCCompat && !Opts.CPlusPlus17) || T.isOSzOS();
Opts.Trigraphs =
Args.hasFlag(OPT_ftrigraphs, OPT_fno_trigraphs, Opts.Trigraphs);
Opts.Blocks = Args.hasArg(OPT_fblocks) || (Opts.OpenCL
&& Opts.OpenCLVersion == 200);
Opts.ConvergentFunctions = Opts.OpenCL || (Opts.CUDA && Opts.CUDAIsDevice) ||
Opts.SYCLIsDevice ||
Args.hasArg(OPT_fconvergent_functions);
Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding;
if (!Opts.NoBuiltin)
getAllNoBuiltinFuncValues(Args, Opts.NoBuiltinFuncs);
Opts.LongDoubleSize = Args.hasArg(OPT_mlong_double_128)
? 128
: Args.hasArg(OPT_mlong_double_64) ? 64 : 0;
if (Opts.FastRelaxedMath)
Opts.setDefaultFPContractMode(LangOptions::FPM_Fast);
llvm::sort(Opts.ModuleFeatures);
// -mrtd option
if (Arg *A = Args.getLastArg(OPT_mrtd)) {
if (Opts.getDefaultCallingConv() != LangOptions::DCC_None)
Diags.Report(diag::err_drv_argument_not_allowed_with)
<< A->getSpelling() << "-fdefault-calling-conv";
else {
if (T.getArch() != llvm::Triple::x86)
Diags.Report(diag::err_drv_argument_not_allowed_with)
<< A->getSpelling() << T.getTriple();
else
Opts.setDefaultCallingConv(LangOptions::DCC_StdCall);
}
}
// Check if -fopenmp is specified and set default version to 5.0.
Opts.OpenMP = Args.hasArg(OPT_fopenmp) ? 50 : 0;
// Check if -fopenmp-simd is specified.
bool IsSimdSpecified =
Args.hasFlag(options::OPT_fopenmp_simd, options::OPT_fno_openmp_simd,
/*Default=*/false);
Opts.OpenMPSimd = !Opts.OpenMP && IsSimdSpecified;
Opts.OpenMPUseTLS =
Opts.OpenMP && !Args.hasArg(options::OPT_fnoopenmp_use_tls);
Opts.OpenMPIsDevice =
Opts.OpenMP && Args.hasArg(options::OPT_fopenmp_is_device);
Opts.OpenMPIRBuilder =
Opts.OpenMP && Args.hasArg(options::OPT_fopenmp_enable_irbuilder);
bool IsTargetSpecified =
Opts.OpenMPIsDevice || Args.hasArg(options::OPT_fopenmp_targets_EQ);
Opts.ConvergentFunctions = Opts.ConvergentFunctions || Opts.OpenMPIsDevice;
if (Opts.OpenMP || Opts.OpenMPSimd) {
if (int Version = getLastArgIntValue(
Args, OPT_fopenmp_version_EQ,
(IsSimdSpecified || IsTargetSpecified) ? 50 : Opts.OpenMP, Diags))
Opts.OpenMP = Version;
// Provide diagnostic when a given target is not expected to be an OpenMP
// device or host.
if (!Opts.OpenMPIsDevice) {
switch (T.getArch()) {
default:
break;
// Add unsupported host targets here:
case llvm::Triple::nvptx:
case llvm::Triple::nvptx64:
Diags.Report(diag::err_drv_omp_host_target_not_supported) << T.str();
break;
}
}
}
// Set the flag to prevent the implementation from emitting device exception
// handling code for those requiring so.
if ((Opts.OpenMPIsDevice && (T.isNVPTX() || T.isAMDGCN())) ||
Opts.OpenCLCPlusPlus) {
Opts.Exceptions = 0;
Opts.CXXExceptions = 0;
}
if (Opts.OpenMPIsDevice && T.isNVPTX()) {
Opts.OpenMPCUDANumSMs =
getLastArgIntValue(Args, options::OPT_fopenmp_cuda_number_of_sm_EQ,
Opts.OpenMPCUDANumSMs, Diags);
Opts.OpenMPCUDABlocksPerSM =
getLastArgIntValue(Args, options::OPT_fopenmp_cuda_blocks_per_sm_EQ,
Opts.OpenMPCUDABlocksPerSM, Diags);
Opts.OpenMPCUDAReductionBufNum = getLastArgIntValue(
Args, options::OPT_fopenmp_cuda_teams_reduction_recs_num_EQ,
Opts.OpenMPCUDAReductionBufNum, Diags);
}
// Get the OpenMP target triples if any.
if (Arg *A = Args.getLastArg(options::OPT_fopenmp_targets_EQ)) {
enum ArchPtrSize { Arch16Bit, Arch32Bit, Arch64Bit };
auto getArchPtrSize = [](const llvm::Triple &T) {
if (T.isArch16Bit())
return Arch16Bit;
if (T.isArch32Bit())
return Arch32Bit;
assert(T.isArch64Bit() && "Expected 64-bit architecture");
return Arch64Bit;
};
for (unsigned i = 0; i < A->getNumValues(); ++i) {
llvm::Triple TT(A->getValue(i));
if (TT.getArch() == llvm::Triple::UnknownArch ||
!(TT.getArch() == llvm::Triple::aarch64 || TT.isPPC() ||
TT.getArch() == llvm::Triple::nvptx ||
TT.getArch() == llvm::Triple::nvptx64 ||
TT.getArch() == llvm::Triple::amdgcn ||
TT.getArch() == llvm::Triple::x86 ||
TT.getArch() == llvm::Triple::x86_64))
Diags.Report(diag::err_drv_invalid_omp_target) << A->getValue(i);
else if (getArchPtrSize(T) != getArchPtrSize(TT))
Diags.Report(diag::err_drv_incompatible_omp_arch)
<< A->getValue(i) << T.str();
else
Opts.OMPTargetTriples.push_back(TT);
}
}
// Get OpenMP host file path if any and report if a non existent file is
// found
if (Arg *A = Args.getLastArg(options::OPT_fopenmp_host_ir_file_path)) {
Opts.OMPHostIRFile = A->getValue();
if (!llvm::sys::fs::exists(Opts.OMPHostIRFile))
Diags.Report(diag::err_drv_omp_host_ir_file_not_found)
<< Opts.OMPHostIRFile;
}
// Set CUDA mode for OpenMP target NVPTX/AMDGCN if specified in options
Opts.OpenMPCUDAMode = Opts.OpenMPIsDevice && (T.isNVPTX() || T.isAMDGCN()) &&
Args.hasArg(options::OPT_fopenmp_cuda_mode);
// Set CUDA support for parallel execution of target regions for OpenMP target
// NVPTX/AMDGCN if specified in options.
Opts.OpenMPCUDATargetParallel =
Opts.OpenMPIsDevice && (T.isNVPTX() || T.isAMDGCN()) &&
Args.hasArg(options::OPT_fopenmp_cuda_parallel_target_regions);
// Set CUDA mode for OpenMP target NVPTX/AMDGCN if specified in options
Opts.OpenMPCUDAForceFullRuntime =
Opts.OpenMPIsDevice && (T.isNVPTX() || T.isAMDGCN()) &&
Args.hasArg(options::OPT_fopenmp_cuda_force_full_runtime);
// FIXME: Eliminate this dependency.
unsigned Opt = getOptimizationLevel(Args, IK, Diags),
OptSize = getOptimizationLevelSize(Args);
Opts.Optimize = Opt != 0;
Opts.OptimizeSize = OptSize != 0;
// This is the __NO_INLINE__ define, which just depends on things like the
// optimization level and -fno-inline, not actually whether the backend has
// inlining enabled.
Cleanup the handling of noinline function attributes, -fno-inline, -fno-inline-functions, -O0, and optnone. These were really, really tangled together: - We used the noinline LLVM attribute for -fno-inline - But not for -fno-inline-functions (breaking LTO) - But we did use it for -finline-hint-functions (yay, LTO is happy!) - But we didn't for -O0 (LTO is sad yet again...) - We had weird structuring of CodeGenOpts with both an inlining enumeration and a boolean. They interacted in weird ways and needlessly. - A *lot* of set smashing went on with setting these, and then got worse when we considered optnone and other inlining-effecting attributes. - A bunch of inline affecting attributes were managed in a completely different place from -fno-inline. - Even with -fno-inline we failed to put the LLVM noinline attribute onto many generated function definitions because they didn't show up as AST-level functions. - If you passed -O0 but -finline-functions we would run the normal inliner pass in LLVM despite it being in the O0 pipeline, which really doesn't make much sense. - Lastly, we used things like '-fno-inline' to manipulate the pass pipeline which forced the pass pipeline to be much more parameterizable than it really needs to be. Instead we can *just* use the optimization level to select a pipeline and control the rest via attributes. Sadly, this causes a bunch of churn in tests because we don't run the optimizer in the tests and check the contents of attribute sets. It would be awesome if attribute sets were a bit more FileCheck friendly, but oh well. I think this is a significant improvement and should remove the semantic need to change what inliner pass we run in order to comply with the requested inlining semantics by relying completely on attributes. It also cleans up tho optnone and related handling a bit. One unfortunate aspect of this is that for generating alwaysinline routines like those in OpenMP we end up removing noinline and then adding alwaysinline. I tried a bunch of other approaches, but because we recompute function attributes from scratch and don't have a declaration here I couldn't find anything substantially cleaner than this. Differential Revision: https://reviews.llvm.org/D28053 llvm-svn: 290398
2016-12-23 01:24:49 +00:00
Opts.NoInlineDefine = !Opts.Optimize;
if (Arg *InlineArg = Args.getLastArg(
options::OPT_finline_functions, options::OPT_finline_hint_functions,
options::OPT_fno_inline_functions, options::OPT_fno_inline))
if (InlineArg->getOption().matches(options::OPT_fno_inline))
Opts.NoInlineDefine = true;
Use FPContractModeKind universally FPContractModeKind is the codegen option flag which is already ternary (off, on, fast). This makes it universally the type for the contractable info across the front-end: * In FPOptions (i.e. in the Sema + in the expression nodes). * In LangOpts::DefaultFPContractMode which is the option that initializes FPOptions in the Sema. Another way to look at this change is that before fp-contractable on/off were the only states handled to the front-end: * For "on", FMA folding was performed by the front-end * For "fast", we simply forwarded the flag to TargetOptions to handle it in LLVM Now off/on/fast are all exposed because for fast we will generate fast-math-flags during CodeGen. This is toward moving fp-contraction=fast from an LLVM TargetOption to a FastMathFlag in order to fix PR25721. --- This is a recommit of r299027 with an adjustment to the test CodeGenCUDA/fp-contract.cu. The test assumed that even though -ffp-contract=on is passed FE-based folding of FMA won't happen. This is obviously wrong since the user is asking for this explicitly with the option. CUDA is different that -ffp-contract=fast is on by default. The test used to "work" because contract=fast and contract=on were maintained separately and we didn't fold in the FE because contract=fast was on due to the target-default. This patch consolidates the contract=on/fast/off state into a ternary state hence the change in behavior. --- Differential Revision: https://reviews.llvm.org/D31167 llvm-svn: 299033
2017-03-29 21:54:24 +00:00
if (Arg *A = Args.getLastArg(OPT_ffp_contract)) {
StringRef Val = A->getValue();
if (Val == "fast")
Opts.setDefaultFPContractMode(LangOptions::FPM_Fast);
Use FPContractModeKind universally FPContractModeKind is the codegen option flag which is already ternary (off, on, fast). This makes it universally the type for the contractable info across the front-end: * In FPOptions (i.e. in the Sema + in the expression nodes). * In LangOpts::DefaultFPContractMode which is the option that initializes FPOptions in the Sema. Another way to look at this change is that before fp-contractable on/off were the only states handled to the front-end: * For "on", FMA folding was performed by the front-end * For "fast", we simply forwarded the flag to TargetOptions to handle it in LLVM Now off/on/fast are all exposed because for fast we will generate fast-math-flags during CodeGen. This is toward moving fp-contraction=fast from an LLVM TargetOption to a FastMathFlag in order to fix PR25721. --- This is a recommit of r299027 with an adjustment to the test CodeGenCUDA/fp-contract.cu. The test assumed that even though -ffp-contract=on is passed FE-based folding of FMA won't happen. This is obviously wrong since the user is asking for this explicitly with the option. CUDA is different that -ffp-contract=fast is on by default. The test used to "work" because contract=fast and contract=on were maintained separately and we didn't fold in the FE because contract=fast was on due to the target-default. This patch consolidates the contract=on/fast/off state into a ternary state hence the change in behavior. --- Differential Revision: https://reviews.llvm.org/D31167 llvm-svn: 299033
2017-03-29 21:54:24 +00:00
else if (Val == "on")
Opts.setDefaultFPContractMode(LangOptions::FPM_On);
Use FPContractModeKind universally FPContractModeKind is the codegen option flag which is already ternary (off, on, fast). This makes it universally the type for the contractable info across the front-end: * In FPOptions (i.e. in the Sema + in the expression nodes). * In LangOpts::DefaultFPContractMode which is the option that initializes FPOptions in the Sema. Another way to look at this change is that before fp-contractable on/off were the only states handled to the front-end: * For "on", FMA folding was performed by the front-end * For "fast", we simply forwarded the flag to TargetOptions to handle it in LLVM Now off/on/fast are all exposed because for fast we will generate fast-math-flags during CodeGen. This is toward moving fp-contraction=fast from an LLVM TargetOption to a FastMathFlag in order to fix PR25721. --- This is a recommit of r299027 with an adjustment to the test CodeGenCUDA/fp-contract.cu. The test assumed that even though -ffp-contract=on is passed FE-based folding of FMA won't happen. This is obviously wrong since the user is asking for this explicitly with the option. CUDA is different that -ffp-contract=fast is on by default. The test used to "work" because contract=fast and contract=on were maintained separately and we didn't fold in the FE because contract=fast was on due to the target-default. This patch consolidates the contract=on/fast/off state into a ternary state hence the change in behavior. --- Differential Revision: https://reviews.llvm.org/D31167 llvm-svn: 299033
2017-03-29 21:54:24 +00:00
else if (Val == "off")
Opts.setDefaultFPContractMode(LangOptions::FPM_Off);
[HIP] Fix regressions due to fp contract change Recently HIP toolchain made a change to use clang instead of opt/llc to do compilation (https://reviews.llvm.org/D81861). The intention is to make HIP toolchain canonical like other toolchains. However, this change introduced an unintentional change regarding backend fp fuse option, which caused regressions in some HIP applications. Basically before the change, HIP toolchain used clang to generate bitcode, then use opt/llc to optimize bitcode and generate ISA. As such, the amdgpu backend takes the default fp fuse mode which is 'Standard'. This mode respect contract flag of fmul/fadd instructions and do not fuse fmul/fadd instructions without contract flag. However, after the change, HIP toolchain now use clang to generate IR, do optimization, and generate ISA as one process. Now amdgpu backend fp fuse option is determined by -ffp-contract option, which is 'fast' by default. And this -ffp-contract=fast language option is translated to 'Fast' fp fuse option in backend. Suddenly backend starts to fuse fmul/fadd instructions without contract flag. This causes wrong result for some device library functions, e.g. tan(-1e20), which should return 0.8446, now returns -0.933. What is worse is that since backend with 'Fast' fp fuse option does not respect contract flag, there is no way to use #pragma clang fp contract directive to enforce fp contract requirements. This patch fixes the regression by introducing a new value 'fast-honor-pragmas' for -ffp-contract and use it for HIP by default. 'fast-honor-pragmas' is equivalent to 'fast' in frontend but let the backend to use 'Standard' fp fuse option. 'fast-honor-pragmas' is useful since 'Fast' fp fuse option in backend does not honor contract flag, it is of little use to HIP applications since all code with #pragma STDC FP_CONTRACT or any IR from a source compiled with -ffp-contract=on is broken. Differential Revision: https://reviews.llvm.org/D90174
2020-10-23 16:24:48 -04:00
else if (Val == "fast-honor-pragmas")
Opts.setDefaultFPContractMode(LangOptions::FPM_FastHonorPragmas);
Use FPContractModeKind universally FPContractModeKind is the codegen option flag which is already ternary (off, on, fast). This makes it universally the type for the contractable info across the front-end: * In FPOptions (i.e. in the Sema + in the expression nodes). * In LangOpts::DefaultFPContractMode which is the option that initializes FPOptions in the Sema. Another way to look at this change is that before fp-contractable on/off were the only states handled to the front-end: * For "on", FMA folding was performed by the front-end * For "fast", we simply forwarded the flag to TargetOptions to handle it in LLVM Now off/on/fast are all exposed because for fast we will generate fast-math-flags during CodeGen. This is toward moving fp-contraction=fast from an LLVM TargetOption to a FastMathFlag in order to fix PR25721. --- This is a recommit of r299027 with an adjustment to the test CodeGenCUDA/fp-contract.cu. The test assumed that even though -ffp-contract=on is passed FE-based folding of FMA won't happen. This is obviously wrong since the user is asking for this explicitly with the option. CUDA is different that -ffp-contract=fast is on by default. The test used to "work" because contract=fast and contract=on were maintained separately and we didn't fold in the FE because contract=fast was on due to the target-default. This patch consolidates the contract=on/fast/off state into a ternary state hence the change in behavior. --- Differential Revision: https://reviews.llvm.org/D31167 llvm-svn: 299033
2017-03-29 21:54:24 +00:00
else
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val;
}
// Parse -fsanitize= arguments.
parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ),
Diags, Opts.Sanitize);
Opts.SanitizerBlacklistFiles = Args.getAllArgValues(OPT_fsanitize_blacklist);
std::vector<std::string> systemBlacklists =
Args.getAllArgValues(OPT_fsanitize_system_blacklist);
Opts.SanitizerBlacklistFiles.insert(Opts.SanitizerBlacklistFiles.end(),
systemBlacklists.begin(),
systemBlacklists.end());
if (Arg *A = Args.getLastArg(OPT_fclang_abi_compat_EQ)) {
Opts.setClangABICompat(LangOptions::ClangABI::Latest);
StringRef Ver = A->getValue();
std::pair<StringRef, StringRef> VerParts = Ver.split('.');
unsigned Major, Minor = 0;
// Check the version number is valid: either 3.x (0 <= x <= 9) or
// y or y.0 (4 <= y <= current version).
if (!VerParts.first.startswith("0") &&
!VerParts.first.getAsInteger(10, Major) &&
3 <= Major && Major <= CLANG_VERSION_MAJOR &&
(Major == 3 ? VerParts.second.size() == 1 &&
!VerParts.second.getAsInteger(10, Minor)
: VerParts.first.size() == Ver.size() ||
VerParts.second == "0")) {
// Got a valid version number.
if (Major == 3 && Minor <= 8)
Opts.setClangABICompat(LangOptions::ClangABI::Ver3_8);
else if (Major <= 4)
Opts.setClangABICompat(LangOptions::ClangABI::Ver4);
else if (Major <= 6)
Opts.setClangABICompat(LangOptions::ClangABI::Ver6);
else if (Major <= 7)
Opts.setClangABICompat(LangOptions::ClangABI::Ver7);
else if (Major <= 9)
Opts.setClangABICompat(LangOptions::ClangABI::Ver9);
else if (Major <= 11)
Opts.setClangABICompat(LangOptions::ClangABI::Ver11);
} else if (Ver != "latest") {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
}
}
if (Arg *A = Args.getLastArg(OPT_msign_return_address_EQ)) {
StringRef SignScope = A->getValue();
if (SignScope.equals_lower("none"))
Opts.setSignReturnAddressScope(
LangOptions::SignReturnAddressScopeKind::None);
else if (SignScope.equals_lower("all"))
Opts.setSignReturnAddressScope(
LangOptions::SignReturnAddressScopeKind::All);
else if (SignScope.equals_lower("non-leaf"))
Opts.setSignReturnAddressScope(
LangOptions::SignReturnAddressScopeKind::NonLeaf);
else
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << SignScope;
if (Arg *A = Args.getLastArg(OPT_msign_return_address_key_EQ)) {
StringRef SignKey = A->getValue();
if (!SignScope.empty() && !SignKey.empty()) {
if (SignKey.equals_lower("a_key"))
Opts.setSignReturnAddressKey(
LangOptions::SignReturnAddressKeyKind::AKey);
else if (SignKey.equals_lower("b_key"))
Opts.setSignReturnAddressKey(
LangOptions::SignReturnAddressKeyKind::BKey);
else
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << SignKey;
}
}
}
return Success && Diags.getNumErrors() == NumErrorsBefore;
}
bool CompilerInvocation::ParseLangArgs(CompilerInvocation &Res,
LangOptions &Opts,
llvm::opt::ArgList &Args, InputKind IK,
const llvm::Triple &T,
std::vector<std::string> &Includes,
DiagnosticsEngine &Diags) {
auto DummyOpts = std::make_shared<LangOptions>();
// We need to work around inconsistencies related to optimization flags. Their
// primary consumer is CodeGenOptions. However, the LangOptions parser also
// queries them, which means RoundTrip expects us to generate them. We don't
// want to do it in GenerateLangArgs, because it should eventually be the
// responsibility of GenerateCodeGenArgs. Until we start doing one big
// round-trip, let's do it here.
//
// Our parser always queries OPT_O_Group. When given -O1, -O2 or -O3, it also
// queries OPT_O. To ensure RoundTrip consistently considers us responsible
// for generating all of them, we ensure to proactively query them all.
return RoundTrip(
[IK, &T, &Includes](CompilerInvocation &Res, ArgList &Args,
DiagnosticsEngine &Diags) {
// Proactively query all optimization flags.
Args.getLastArg(OPT_O0, OPT_O4, OPT_O, OPT_Ofast);
return ParseLangArgsImpl(*Res.getLangOpts(), Args, IK, T, Includes,
Diags);
},
[&T, &Args](CompilerInvocation &Res,
SmallVectorImpl<const char *> &GenArgs, StringAllocator SA) {
GenerateLangArgs(*Res.getLangOpts(), GenArgs, SA, T);
// Generate all optimization flags we queried.
if (Arg *A = Args.getLastArg(OPT_O_Group)) {
OptSpecifier Opt = A->getOption().getID();
if (A->getNumValues() > 0)
GenerateArg(GenArgs, Opt, A->getValues().back(), SA);
else
GenerateArg(GenArgs, Opt, SA);
}
// We also queried -fcf-protection, but don't have enough information to
// generate it. Eventually, it will be generated from CodeGenOptions.
if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ))
GenerateArg(GenArgs, OPT_fcf_protection_EQ, A->getValue(), SA);
},
[&DummyOpts](CompilerInvocation &Res) { Res.LangOpts.swap(DummyOpts); },
Res, Args, Diags, "LangOptions");
}
static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
switch (Action) {
case frontend::ASTDeclList:
case frontend::ASTDump:
case frontend::ASTPrint:
case frontend::ASTView:
case frontend::EmitAssembly:
case frontend::EmitBC:
case frontend::EmitHTML:
case frontend::EmitLLVM:
case frontend::EmitLLVMOnly:
case frontend::EmitCodeGenOnly:
case frontend::EmitObj:
case frontend::FixIt:
case frontend::GenerateModule:
case frontend::GenerateModuleInterface:
case frontend::GenerateHeaderModule:
case frontend::GeneratePCH:
case frontend::GenerateInterfaceStubs:
case frontend::ParseSyntaxOnly:
case frontend::ModuleFileInfo:
case frontend::VerifyPCH:
case frontend::PluginAction:
case frontend::RewriteObjC:
case frontend::RewriteTest:
case frontend::RunAnalysis:
case frontend::TemplightDump:
case frontend::MigrateSource:
return false;
case frontend::DumpCompilerOptions:
case frontend::DumpRawTokens:
case frontend::DumpTokens:
case frontend::InitOnly:
case frontend::PrintPreamble:
case frontend::PrintPreprocessedInput:
case frontend::RewriteMacros:
case frontend::RunPreprocessorOnly:
case frontend::PrintDependencyDirectivesSourceMinimizerOutput:
return true;
}
llvm_unreachable("invalid frontend action");
}
static void GeneratePreprocessorArgs(PreprocessorOptions &Opts,
SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA,
const LangOptions &LangOpts,
const FrontendOptions &FrontendOpts,
const CodeGenOptions &CodeGenOpts) {
PreprocessorOptions *PreprocessorOpts = &Opts;
#define PREPROCESSOR_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef PREPROCESSOR_OPTION_WITH_MARSHALLING
if (Opts.PCHWithHdrStop && !Opts.PCHWithHdrStopCreate)
GenerateArg(Args, OPT_pch_through_hdrstop_use, SA);
for (const auto &D : Opts.DeserializedPCHDeclsToErrorOn)
GenerateArg(Args, OPT_error_on_deserialized_pch_decl, D, SA);
for (const auto &MP : Opts.MacroPrefixMap)
GenerateArg(Args, OPT_fmacro_prefix_map_EQ, MP.first + "=" + MP.second, SA);
if (Opts.PrecompiledPreambleBytes != std::make_pair(0u, false))
GenerateArg(Args, OPT_preamble_bytes_EQ,
Twine(Opts.PrecompiledPreambleBytes.first) + "," +
(Opts.PrecompiledPreambleBytes.second ? "1" : "0"),
SA);
for (const auto &M : Opts.Macros) {
// Don't generate __CET__ macro definitions. They are implied by the
// -fcf-protection option that is generated elsewhere.
if (M.first == "__CET__=1" && !M.second &&
!CodeGenOpts.CFProtectionReturn && CodeGenOpts.CFProtectionBranch)
continue;
if (M.first == "__CET__=2" && !M.second && CodeGenOpts.CFProtectionReturn &&
!CodeGenOpts.CFProtectionBranch)
continue;
if (M.first == "__CET__=3" && !M.second && CodeGenOpts.CFProtectionReturn &&
CodeGenOpts.CFProtectionBranch)
continue;
GenerateArg(Args, M.second ? OPT_U : OPT_D, M.first, SA);
}
for (const auto &I : Opts.Includes) {
// Don't generate OpenCL includes. They are implied by other flags that are
// generated elsewhere.
if (LangOpts.OpenCL && LangOpts.IncludeDefaultHeader &&
((LangOpts.DeclareOpenCLBuiltins && I == "opencl-c-base.h") ||
I == "opencl-c.h"))
continue;
GenerateArg(Args, OPT_include, I, SA);
}
for (const auto &CI : Opts.ChainedIncludes)
GenerateArg(Args, OPT_chain_include, CI, SA);
for (const auto &RF : Opts.RemappedFiles)
GenerateArg(Args, OPT_remap_file, RF.first + ";" + RF.second, SA);
// Don't handle LexEditorPlaceholders. It is implied by the action that is
// generated elsewhere.
}
static bool ParsePreprocessorArgsImpl(PreprocessorOptions &Opts, ArgList &Args,
DiagnosticsEngine &Diags,
frontend::ActionKind Action,
const FrontendOptions &FrontendOpts) {
PreprocessorOptions *PreprocessorOpts = &Opts;
bool Success = true;
#define PREPROCESSOR_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef PREPROCESSOR_OPTION_WITH_MARSHALLING
Opts.PCHWithHdrStop = Args.hasArg(OPT_pch_through_hdrstop_create) ||
Args.hasArg(OPT_pch_through_hdrstop_use);
for (const auto *A : Args.filtered(OPT_error_on_deserialized_pch_decl))
Opts.DeserializedPCHDeclsToErrorOn.insert(A->getValue());
for (const auto &A : Args.getAllArgValues(OPT_fmacro_prefix_map_EQ)) {
auto Split = StringRef(A).split('=');
Opts.MacroPrefixMap.insert(
{std::string(Split.first), std::string(Split.second)});
}
if (const Arg *A = Args.getLastArg(OPT_preamble_bytes_EQ)) {
StringRef Value(A->getValue());
size_t Comma = Value.find(',');
unsigned Bytes = 0;
unsigned EndOfLine = 0;
2010-10-21 03:16:25 +00:00
if (Comma == StringRef::npos ||
Value.substr(0, Comma).getAsInteger(10, Bytes) ||
Value.substr(Comma + 1).getAsInteger(10, EndOfLine))
Diags.Report(diag::err_drv_preamble_format);
else {
Opts.PrecompiledPreambleBytes.first = Bytes;
Opts.PrecompiledPreambleBytes.second = (EndOfLine != 0);
}
}
2010-10-21 03:16:25 +00:00
// Add the __CET__ macro if a CFProtection option is set.
if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ)) {
StringRef Name = A->getValue();
if (Name == "branch")
Opts.addMacroDef("__CET__=1");
else if (Name == "return")
Opts.addMacroDef("__CET__=2");
else if (Name == "full")
Opts.addMacroDef("__CET__=3");
}
// Add macros from the command line.
for (const auto *A : Args.filtered(OPT_D, OPT_U)) {
if (A->getOption().matches(OPT_D))
Opts.addMacroDef(A->getValue());
else
Opts.addMacroUndef(A->getValue());
}
// Add the ordered list of -includes.
for (const auto *A : Args.filtered(OPT_include))
Opts.Includes.emplace_back(A->getValue());
for (const auto *A : Args.filtered(OPT_chain_include))
Opts.ChainedIncludes.emplace_back(A->getValue());
for (const auto *A : Args.filtered(OPT_remap_file)) {
std::pair<StringRef, StringRef> Split = StringRef(A->getValue()).split(';');
if (Split.second.empty()) {
Diags.Report(diag::err_drv_invalid_remap_file) << A->getAsString(Args);
continue;
}
Opts.addRemappedFile(Split.first, Split.second);
}
// Always avoid lexing editor placeholders when we're just running the
// preprocessor as we never want to emit the
// "editor placeholder in source file" error in PP only mode.
if (isStrictlyPreprocessorAction(Action))
Opts.LexEditorPlaceholders = false;
return Success;
}
static bool ParsePreprocessorArgs(CompilerInvocation &Res,
PreprocessorOptions &Opts, ArgList &Args,
DiagnosticsEngine &Diags,
frontend::ActionKind Action,
FrontendOptions &FrontendOpts) {
auto DummyOpts = std::make_shared<PreprocessorOptions>();
auto Parse = [Action](CompilerInvocation &Res, ArgList &Args,
DiagnosticsEngine &Diags) {
return ParsePreprocessorArgsImpl(Res.getPreprocessorOpts(), Args, Diags,
Action, Res.getFrontendOpts());
};
auto Generate = [&Args](CompilerInvocation &Res,
SmallVectorImpl<const char *> &GeneratedArgs,
CompilerInvocation::StringAllocator SA) {
GeneratePreprocessorArgs(Res.getPreprocessorOpts(), GeneratedArgs, SA,
*Res.getLangOpts(), Res.getFrontendOpts(),
Res.getCodeGenOpts());
// The ParsePreprocessorArgs function queries the -fcf-protection option,
// which means that it won't be directly copied during argument generation.
// The GeneratePreprocessorArgs function isn't responsible for generating it
// either. This would cause -fcf-protection to get forgotten during
// round-trip and the __CET__ macros wouldn't get deduced during second call
// to ParsePreprocessorArgs. Let's fix this by generating -fcf-protection
// here.
// TODO: Remove this once we're doing one big round-trip instead of many
// small ones.
if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ))
GenerateArg(GeneratedArgs, OPT_fcf_protection_EQ, A->getValue(), SA);
};
auto Swap = [&DummyOpts](CompilerInvocation &Res) {
std::swap(Res.PreprocessorOpts, DummyOpts);
};
return RoundTrip(Parse, Generate, Swap, Res, Args, Diags,
"PreprocessorOptions");
}
static void GeneratePreprocessorOutputArgs(
const PreprocessorOutputOptions &Opts, SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA, frontend::ActionKind Action) {
const PreprocessorOutputOptions &PreprocessorOutputOpts = Opts;
#define PREPROCESSOR_OUTPUT_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef PREPROCESSOR_OUTPUT_OPTION_WITH_MARSHALLING
bool Generate_dM = isStrictlyPreprocessorAction(Action) && !Opts.ShowCPP;
if (Generate_dM)
GenerateArg(Args, OPT_dM, SA);
if (!Generate_dM && Opts.ShowMacros)
GenerateArg(Args, OPT_dD, SA);
}
static bool ParsePreprocessorOutputArgsImpl(PreprocessorOutputOptions &Opts,
ArgList &Args,
DiagnosticsEngine &Diags,
frontend::ActionKind Action) {
PreprocessorOutputOptions &PreprocessorOutputOpts = Opts;
unsigned NumErrorsBefore = Diags.getNumErrors();
bool Success = true;
#define PREPROCESSOR_OUTPUT_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef PREPROCESSOR_OUTPUT_OPTION_WITH_MARSHALLING
Opts.ShowCPP = isStrictlyPreprocessorAction(Action) && !Args.hasArg(OPT_dM);
Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD);
return Success && Diags.getNumErrors() == NumErrorsBefore;
}
static bool ParsePreprocessorOutputArgs(CompilerInvocation &Res,
PreprocessorOutputOptions &Opts,
ArgList &Args, DiagnosticsEngine &Diags,
frontend::ActionKind Action) {
PreprocessorOutputOptions DummyOpts;
return RoundTrip(
[Action](CompilerInvocation &Res, ArgList &Args,
DiagnosticsEngine &Diags) {
return ParsePreprocessorOutputArgsImpl(Res.getPreprocessorOutputOpts(),
Args, Diags, Action);
},
[Action](CompilerInvocation &Res, SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA) {
GeneratePreprocessorOutputArgs(Res.getPreprocessorOutputOpts(), Args,
SA, Action);
},
[&DummyOpts](CompilerInvocation &Res) {
std::swap(DummyOpts, Res.getPreprocessorOutputOpts());
},
Res, Args, Diags, "PreprocessorOutputOptions");
}
static void GenerateTargetArgs(const TargetOptions &Opts,
SmallVectorImpl<const char *> &Args,
CompilerInvocation::StringAllocator SA) {
const TargetOptions *TargetOpts = &Opts;
#define TARGET_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
GENERATE_OPTION_WITH_MARSHALLING( \
Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef TARGET_OPTION_WITH_MARSHALLING
if (!Opts.SDKVersion.empty())
GenerateArg(Args, OPT_target_sdk_version_EQ, Opts.SDKVersion.getAsString(),
SA);
}
static bool ParseTargetArgsImpl(TargetOptions &Opts, ArgList &Args,
DiagnosticsEngine &Diags) {
TargetOptions *TargetOpts = &Opts;
unsigned NumErrorsBefore = Diags.getNumErrors();
bool Success = true;
#define TARGET_OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
MERGER, EXTRACTOR, TABLE_INDEX) \
PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
MERGER, TABLE_INDEX)
#include "clang/Driver/Options.inc"
#undef TARGET_OPTION_WITH_MARSHALLING
if (Arg *A = Args.getLastArg(options::OPT_target_sdk_version_EQ)) {
llvm::VersionTuple Version;
if (Version.tryParse(A->getValue()))
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
else
Opts.SDKVersion = Version;
}
return Success && Diags.getNumErrors() == NumErrorsBefore;
}
static bool ParseTargetArgs(CompilerInvocation &Res, TargetOptions &Opts,
ArgList &Args, DiagnosticsEngine &Diags) {
auto DummyOpts = std::make_shared<TargetOptions>();
return RoundTrip(
[](CompilerInvocation &Res, ArgList &Args,
DiagnosticsEngine &Diags) {
return ParseTargetArgsImpl(Res.getTargetOpts(), Args, Diags);
},
[](CompilerInvocation &Res, SmallVectorImpl<const char *> &GeneratedArgs,
CompilerInvocation::StringAllocator SA) {
GenerateTargetArgs(Res.getTargetOpts(), GeneratedArgs, SA);
},
[&DummyOpts](CompilerInvocation &Res) {
Res.TargetOpts.swap(DummyOpts);
},
Res, Args, Diags, "TargetArgs");
}
bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res,
ArrayRef<const char *> CommandLineArgs,
DiagnosticsEngine &Diags,
const char *Argv0) {
bool Success = true;
// Parse the arguments.
const OptTable &Opts = getDriverOptTable();
const unsigned IncludedFlagsBitmask = options::CC1Option;
unsigned MissingArgIndex, MissingArgCount;
InputArgList Args = Opts.ParseArgs(CommandLineArgs, MissingArgIndex,
MissingArgCount, IncludedFlagsBitmask);
LangOptions &LangOpts = *Res.getLangOpts();
// Check for missing argument error.
if (MissingArgCount) {
Diags.Report(diag::err_drv_missing_argument)
<< Args.getArgString(MissingArgIndex) << MissingArgCount;
Success = false;
}
// Issue errors on unknown arguments.
for (const auto *A : Args.filtered(OPT_UNKNOWN)) {
auto ArgString = A->getAsString(Args);
std::string Nearest;
if (Opts.findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1)
Diags.Report(diag::err_drv_unknown_argument) << ArgString;
else
Diags.Report(diag::err_drv_unknown_argument_with_suggestion)
<< ArgString << Nearest;
Success = false;
}
Success &= ParseFileSystemArgs(Res.getFileSystemOpts(), Args, Diags);
Success &= ParseMigratorArgs(Res.getMigratorOpts(), Args, Diags);
Success &= ParseAnalyzerArgs(Res, *Res.getAnalyzerOpts(), Args, Diags);
Success &=
ParseDiagnosticArgsRoundTrip(Res, Res.getDiagnosticOpts(), Args, &Diags,
/*DefaultDiagColor=*/false);
Success &= ParseFrontendArgs(Res, Res.getFrontendOpts(), Args, Diags,
LangOpts.IsHeaderFile);
// FIXME: We shouldn't have to pass the DashX option around here
InputKind DashX = Res.getFrontendOpts().DashX;
ParseTargetArgs(Res, Res.getTargetOpts(), Args, Diags);
llvm::Triple T(Res.getTargetOpts().Triple);
ParseHeaderSearchArgs(Res, Res.getHeaderSearchOpts(), Args, Diags,
Res.getFileSystemOpts().WorkingDir);
if (DashX.getFormat() == InputKind::Precompiled ||
DashX.getLanguage() == Language::LLVM_IR) {
// ObjCAAutoRefCount and Sanitize LangOpts are used to setup the
// PassManager in BackendUtil.cpp. They need to be initializd no matter
// what the input type is.
if (Args.hasArg(OPT_fobjc_arc))
LangOpts.ObjCAutoRefCount = 1;
// PIClevel and PIELevel are needed during code generation and this should be
// set regardless of the input type.
LangOpts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags);
LangOpts.PIE = Args.hasArg(OPT_pic_is_pie);
parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ),
Diags, LangOpts.Sanitize);
} else {
// Other LangOpts are only initialized when the input is not AST or LLVM IR.
// FIXME: Should we really be calling this for an Language::Asm input?
Success &= ParseLangArgs(Res, LangOpts, Args, DashX, T,
Res.getPreprocessorOpts().Includes, Diags);
if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC)
LangOpts.ObjCExceptions = 1;
if (T.isOSDarwin() && DashX.isPreprocessed()) {
// Supress the darwin-specific 'stdlibcxx-not-found' diagnostic for
// preprocessed input as we don't expect it to be used with -std=libc++
// anyway.
Res.getDiagnosticOpts().Warnings.push_back("no-stdlibcxx-not-found");
}
}
if (LangOpts.CUDA) {
// During CUDA device-side compilation, the aux triple is the
// triple used for host compilation.
if (LangOpts.CUDAIsDevice)
Res.getTargetOpts().HostTriple = Res.getFrontendOpts().AuxTriple;
}
// Set the triple of the host for OpenMP device compile.
if (LangOpts.OpenMPIsDevice)
Res.getTargetOpts().HostTriple = Res.getFrontendOpts().AuxTriple;
Success &= ParseCodeGenArgs(Res, Res.getCodeGenOpts(), Args, DashX, Diags, T,
Res.getFrontendOpts().OutputFile, LangOpts);
// FIXME: Override value name discarding when asan or msan is used because the
// backend passes depend on the name of the alloca in order to print out
// names.
Res.getCodeGenOpts().DiscardValueNames &=
!LangOpts.Sanitize.has(SanitizerKind::Address) &&
!LangOpts.Sanitize.has(SanitizerKind::KernelAddress) &&
!LangOpts.Sanitize.has(SanitizerKind::Memory) &&
!LangOpts.Sanitize.has(SanitizerKind::KernelMemory);
ParsePreprocessorArgs(Res, Res.getPreprocessorOpts(), Args, Diags,
Res.getFrontendOpts().ProgramAction,
Res.getFrontendOpts());
ParsePreprocessorOutputArgs(Res, Res.getPreprocessorOutputOpts(), Args, Diags,
Res.getFrontendOpts().ProgramAction);
2016-07-28 19:26:30 +00:00
ParseDependencyOutputArgs(Res, Res.getDependencyOutputOpts(), Args, Diags,
Res.getFrontendOpts().ProgramAction,
Res.getPreprocessorOutputOpts().ShowLineMarkers);
if (!Res.getDependencyOutputOpts().OutputFile.empty() &&
Res.getDependencyOutputOpts().Targets.empty()) {
Diags.Report(diag::err_fe_dependency_file_requires_MT);
Success = false;
}
2016-07-28 19:26:30 +00:00
// Turn on -Wspir-compat for SPIR target.
if (T.isSPIR())
2016-07-28 19:26:30 +00:00
Res.getDiagnosticOpts().Warnings.push_back("spir-compat");
// If sanitizer is enabled, disable OPT_ffine_grained_bitfield_accesses.
if (Res.getCodeGenOpts().FineGrainedBitfieldAccesses &&
!Res.getLangOpts()->Sanitize.empty()) {
Res.getCodeGenOpts().FineGrainedBitfieldAccesses = false;
Diags.Report(diag::warn_drv_fine_grained_bitfield_accesses_ignored);
}
// Store the command-line for using in the CodeView backend.
Res.getCodeGenOpts().Argv0 = Argv0;
Res.getCodeGenOpts().CommandLineArgs = CommandLineArgs;
FixupInvocation(Res, Diags, Args, DashX);
return Success;
}
std::string CompilerInvocation::getModuleHash() const {
// Note: For QoI reasons, the things we use as a hash here should all be
// dumped via the -module-info flag.
using llvm::hash_code;
using llvm::hash_value;
using llvm::hash_combine;
using llvm::hash_combine_range;
// Start the signature with the compiler version.
// FIXME: We'd rather use something more cryptographically sound than
// CityHash, but this will do for now.
hash_code code = hash_value(getClangFullRepositoryVersion());
// Also include the serialization version, in case LLVM_APPEND_VC_REV is off
// and getClangFullRepositoryVersion() doesn't include git revision.
code = hash_combine(code, serialization::VERSION_MAJOR,
serialization::VERSION_MINOR);
// Extend the signature with the language options
#define LANGOPT(Name, Bits, Default, Description) \
code = hash_combine(code, LangOpts->Name);
#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \
code = hash_combine(code, static_cast<unsigned>(LangOpts->get##Name()));
#define BENIGN_LANGOPT(Name, Bits, Default, Description)
#define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description)
#include "clang/Basic/LangOptions.def"
for (StringRef Feature : LangOpts->ModuleFeatures)
code = hash_combine(code, Feature);
code = hash_combine(code, LangOpts->ObjCRuntime);
const auto &BCN = LangOpts->CommentOpts.BlockCommandNames;
code = hash_combine(code, hash_combine_range(BCN.begin(), BCN.end()));
// Extend the signature with the target options.
code = hash_combine(code, TargetOpts->Triple, TargetOpts->CPU,
TargetOpts->TuneCPU, TargetOpts->ABI);
for (const auto &FeatureAsWritten : TargetOpts->FeaturesAsWritten)
code = hash_combine(code, FeatureAsWritten);
// Extend the signature with preprocessor options.
const PreprocessorOptions &ppOpts = getPreprocessorOpts();
const HeaderSearchOptions &hsOpts = getHeaderSearchOpts();
code = hash_combine(code, ppOpts.UsePredefines, ppOpts.DetailedRecord);
for (const auto &I : getPreprocessorOpts().Macros) {
// If we're supposed to ignore this macro for the purposes of modules,
// don't put it into the hash.
if (!hsOpts.ModulesIgnoreMacros.empty()) {
// Check whether we're ignoring this macro.
StringRef MacroDef = I.first;
if (hsOpts.ModulesIgnoreMacros.count(
llvm::CachedHashString(MacroDef.split('=').first)))
continue;
}
code = hash_combine(code, I.first, I.second);
}
// Extend the signature with the sysroot and other header search options.
code = hash_combine(code, hsOpts.Sysroot,
hsOpts.ModuleFormat,
hsOpts.UseDebugInfo,
hsOpts.UseBuiltinIncludes,
hsOpts.UseStandardSystemIncludes,
hsOpts.UseStandardCXXIncludes,
hsOpts.UseLibcxx,
hsOpts.ModulesValidateDiagnosticOptions);
code = hash_combine(code, hsOpts.ResourceDir);
if (hsOpts.ModulesStrictContextHash) {
hash_code SHPC = hash_combine_range(hsOpts.SystemHeaderPrefixes.begin(),
hsOpts.SystemHeaderPrefixes.end());
hash_code UEC = hash_combine_range(hsOpts.UserEntries.begin(),
hsOpts.UserEntries.end());
code = hash_combine(code, hsOpts.SystemHeaderPrefixes.size(), SHPC,
hsOpts.UserEntries.size(), UEC);
const DiagnosticOptions &diagOpts = getDiagnosticOpts();
#define DIAGOPT(Name, Bits, Default) \
code = hash_combine(code, diagOpts.Name);
#define ENUM_DIAGOPT(Name, Type, Bits, Default) \
code = hash_combine(code, diagOpts.get##Name());
#include "clang/Basic/DiagnosticOptions.def"
#undef DIAGOPT
#undef ENUM_DIAGOPT
}
// Extend the signature with the user build path.
code = hash_combine(code, hsOpts.ModuleUserBuildPath);
// Extend the signature with the module file extensions.
const FrontendOptions &frontendOpts = getFrontendOpts();
for (const auto &ext : frontendOpts.ModuleFileExtensions) {
code = ext->hashExtension(code);
}
// When compiling with -gmodules, also hash -fdebug-prefix-map as it
// affects the debug info in the PCM.
if (getCodeGenOpts().DebugTypeExtRefs)
for (const auto &KeyValue : getCodeGenOpts().DebugPrefixMap)
code = hash_combine(code, KeyValue.first, KeyValue.second);
// Extend the signature with the enabled sanitizers, if at least one is
// enabled. Sanitizers which cannot affect AST generation aren't hashed.
SanitizerSet SanHash = LangOpts->Sanitize;
SanHash.clear(getPPTransparentSanitizers());
if (!SanHash.empty())
code = hash_combine(code, SanHash.Mask);
return llvm::APInt(64, code).toString(36, /*Signed=*/false);
}
void CompilerInvocation::generateCC1CommandLine(
SmallVectorImpl<const char *> &Args, StringAllocator SA) const {
llvm::Triple T(TargetOpts->Triple);
GenerateFileSystemArgs(FileSystemOpts, Args, SA);
GenerateMigratorArgs(MigratorOpts, Args, SA);
GenerateAnalyzerArgs(*AnalyzerOpts, Args, SA);
GenerateDiagnosticArgs(*DiagnosticOpts, Args, SA, false);
GenerateFrontendArgs(FrontendOpts, Args, SA, LangOpts->IsHeaderFile);
GenerateTargetArgs(*TargetOpts, Args, SA);
GenerateHeaderSearchArgs(*HeaderSearchOpts, Args, SA);
GenerateLangArgs(*LangOpts, Args, SA, T);
GenerateCodeGenArgs(CodeGenOpts, Args, SA, T, FrontendOpts.OutputFile,
&*LangOpts);
GeneratePreprocessorArgs(*PreprocessorOpts, Args, SA, *LangOpts, FrontendOpts,
CodeGenOpts);
GeneratePreprocessorOutputArgs(PreprocessorOutputOpts, Args, SA,
FrontendOpts.ProgramAction);
GenerateDependencyOutputArgs(DependencyOutputOpts, Args, SA);
}
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
clang::createVFSFromCompilerInvocation(const CompilerInvocation &CI,
DiagnosticsEngine &Diags) {
return createVFSFromCompilerInvocation(CI, Diags,
llvm::vfs::getRealFileSystem());
}
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
clang::createVFSFromCompilerInvocation(
const CompilerInvocation &CI, DiagnosticsEngine &Diags,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) {
if (CI.getHeaderSearchOpts().VFSOverlayFiles.empty())
return BaseFS;
IntrusiveRefCntPtr<llvm::vfs::FileSystem> Result = BaseFS;
// earlier vfs files are on the bottom
for (const auto &File : CI.getHeaderSearchOpts().VFSOverlayFiles) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
Result->getBufferForFile(File);
if (!Buffer) {
Diags.Report(diag::err_missing_vfs_overlay_file) << File;
continue;
}
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = llvm::vfs::getVFSFromYAML(
std::move(Buffer.get()), /*DiagHandler*/ nullptr, File,
/*DiagContext*/ nullptr, Result);
if (!FS) {
Diags.Report(diag::err_invalid_vfs_overlay) << File;
continue;
}
Result = FS;
}
return Result;
}