aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/tools
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/tools')
-rw-r--r--contrib/llvm-project/clang/tools/amdgpu-arch/AMDGPUArch.cpp78
-rw-r--r--contrib/llvm-project/clang/tools/clang-format/ClangFormat.cpp301
-rw-r--r--contrib/llvm-project/clang/tools/clang-repl/ClangRepl.cpp108
-rw-r--r--contrib/llvm-project/clang/tools/clang-scan-deps/ClangScanDeps.cpp1055
-rw-r--r--contrib/llvm-project/clang/tools/clang-scan-deps/Opts.td42
-rw-r--r--contrib/llvm-project/clang/tools/driver/cc1_main.cpp194
-rw-r--r--contrib/llvm-project/clang/tools/driver/cc1as_main.cpp155
-rw-r--r--contrib/llvm-project/clang/tools/driver/cc1gen_reproducer_main.cpp24
-rw-r--r--contrib/llvm-project/clang/tools/driver/driver.cpp462
9 files changed, 1764 insertions, 655 deletions
diff --git a/contrib/llvm-project/clang/tools/amdgpu-arch/AMDGPUArch.cpp b/contrib/llvm-project/clang/tools/amdgpu-arch/AMDGPUArch.cpp
deleted file mode 100644
index 4fae78b4f121..000000000000
--- a/contrib/llvm-project/clang/tools/amdgpu-arch/AMDGPUArch.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-//===- AMDGPUArch.cpp - list AMDGPU installed ----------*- C++ -*---------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements a tool for detecting name of AMDGPU installed in system
-// using HSA. This tool is used by AMDGPU OpenMP driver.
-//
-//===----------------------------------------------------------------------===//
-
-#if defined(__has_include)
-#if __has_include("hsa.h")
-#define HSA_HEADER_FOUND 1
-#include "hsa.h"
-#elif __has_include("hsa/hsa.h")
-#define HSA_HEADER_FOUND 1
-#include "hsa/hsa.h"
-#else
-#define HSA_HEADER_FOUND 0
-#endif
-#else
-#define HSA_HEADER_FOUND 0
-#endif
-
-#if !HSA_HEADER_FOUND
-int main() { return 1; }
-#else
-
-#include <string>
-#include <vector>
-
-static hsa_status_t iterateAgentsCallback(hsa_agent_t Agent, void *Data) {
- hsa_device_type_t DeviceType;
- hsa_status_t Status =
- hsa_agent_get_info(Agent, HSA_AGENT_INFO_DEVICE, &DeviceType);
-
- // continue only if device type if GPU
- if (Status != HSA_STATUS_SUCCESS || DeviceType != HSA_DEVICE_TYPE_GPU) {
- return Status;
- }
-
- std::vector<std::string> *GPUs =
- static_cast<std::vector<std::string> *>(Data);
- char GPUName[64];
- Status = hsa_agent_get_info(Agent, HSA_AGENT_INFO_NAME, GPUName);
- if (Status != HSA_STATUS_SUCCESS) {
- return Status;
- }
- GPUs->push_back(GPUName);
- return HSA_STATUS_SUCCESS;
-}
-
-int main() {
- hsa_status_t Status = hsa_init();
- if (Status != HSA_STATUS_SUCCESS) {
- return 1;
- }
-
- std::vector<std::string> GPUs;
- Status = hsa_iterate_agents(iterateAgentsCallback, &GPUs);
- if (Status != HSA_STATUS_SUCCESS) {
- return 1;
- }
-
- for (const auto &GPU : GPUs)
- printf("%s\n", GPU.c_str());
-
- if (GPUs.size() < 1)
- return 1;
-
- hsa_shut_down();
- return 0;
-}
-
-#endif
diff --git a/contrib/llvm-project/clang/tools/clang-format/ClangFormat.cpp b/contrib/llvm-project/clang/tools/clang-format/ClangFormat.cpp
index 144e87f78c64..c4b6209a71a8 100644
--- a/contrib/llvm-project/clang/tools/clang-format/ClangFormat.cpp
+++ b/contrib/llvm-project/clang/tools/clang-format/ClangFormat.cpp
@@ -12,6 +12,7 @@
///
//===----------------------------------------------------------------------===//
+#include "../../lib/Format/MatchFilePath.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
@@ -19,10 +20,12 @@
#include "clang/Basic/Version.h"
#include "clang/Format/Format.h"
#include "clang/Rewrite/Core/Rewriter.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Process.h"
+#include <fstream>
using namespace llvm;
using clang::tooling::Replacements;
@@ -68,16 +71,29 @@ static cl::opt<std::string>
cl::desc("The name of the predefined style used as a\n"
"fallback in case clang-format is invoked with\n"
"-style=file, but can not find the .clang-format\n"
- "file to use.\n"
+ "file to use. Defaults to 'LLVM'.\n"
"Use -fallback-style=none to skip formatting."),
cl::init(clang::format::DefaultFallbackStyle),
cl::cat(ClangFormatCategory));
static cl::opt<std::string> AssumeFileName(
"assume-filename",
- cl::desc("Override filename used to determine the language.\n"
- "When reading from stdin, clang-format assumes this\n"
- "filename to determine the language."),
+ cl::desc("Set filename used to determine the language and to find\n"
+ ".clang-format file.\n"
+ "Only used when reading from stdin.\n"
+ "If this is not passed, the .clang-format file is searched\n"
+ "relative to the current working directory when reading stdin.\n"
+ "Unrecognized filenames are treated as C++.\n"
+ "supported:\n"
+ " CSharp: .cs\n"
+ " Java: .java\n"
+ " JavaScript: .mjs .js .ts\n"
+ " Json: .json\n"
+ " Objective-C: .m .mm\n"
+ " Proto: .proto .protodevel\n"
+ " TableGen: .td\n"
+ " TextProto: .txtpb .textpb .pb.txt .textproto .asciipb\n"
+ " Verilog: .sv .svh .v .vh"),
cl::init("<stdin>"), cl::cat(ClangFormatCategory));
static cl::opt<bool> Inplace("i",
@@ -98,11 +114,22 @@ static cl::opt<unsigned>
"clang-format from an editor integration"),
cl::init(0), cl::cat(ClangFormatCategory));
-static cl::opt<bool> SortIncludes(
- "sort-includes",
- cl::desc("If set, overrides the include sorting behavior determined by the "
- "SortIncludes style flag"),
- cl::cat(ClangFormatCategory));
+static cl::opt<bool>
+ SortIncludes("sort-includes",
+ cl::desc("If set, overrides the include sorting behavior\n"
+ "determined by the SortIncludes style flag"),
+ cl::cat(ClangFormatCategory));
+
+static cl::opt<std::string> QualifierAlignment(
+ "qualifier-alignment",
+ cl::desc("If set, overrides the qualifier alignment style\n"
+ "determined by the QualifierAlignment style flag"),
+ cl::init(""), cl::cat(ClangFormatCategory));
+
+static cl::opt<std::string> Files(
+ "files",
+ cl::desc("A file containing a list of files to process, one per line."),
+ cl::value_desc("filename"), cl::init(""), cl::cat(ClangFormatCategory));
static cl::opt<bool>
Verbose("verbose", cl::desc("If set, shows the list of processed files"),
@@ -135,8 +162,9 @@ static cl::opt<bool>
static cl::opt<unsigned> ErrorLimit(
"ferror-limit",
- cl::desc("Set the maximum number of clang-format errors to emit before "
- "stopping (0 = no limit). Used only with --dry-run or -n"),
+ cl::desc("Set the maximum number of clang-format errors to emit\n"
+ "before stopping (0 = no limit).\n"
+ "Used only with --dry-run or -n"),
cl::init(0), cl::cat(ClangFormatCategory));
static cl::opt<bool>
@@ -173,9 +201,19 @@ static cl::opt<bool>
"whether or not to print diagnostics in color"),
cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
-static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
+static cl::list<std::string> FileNames(cl::Positional,
+ cl::desc("[@<file>] [<file> ...]"),
cl::cat(ClangFormatCategory));
+static cl::opt<bool> FailOnIncompleteFormat(
+ "fail-on-incomplete-format",
+ cl::desc("If set, fail with exit code 1 on incomplete format."),
+ cl::init(false), cl::cat(ClangFormatCategory));
+
+static cl::opt<bool> ListIgnored("list-ignored",
+ cl::desc("List ignored files."),
+ cl::cat(ClangFormatCategory), cl::Hidden);
+
namespace clang {
namespace format {
@@ -220,8 +258,12 @@ static bool fillRanges(MemoryBuffer *Code,
errs() << "error: invalid <start line>:<end line> pair\n";
return true;
}
+ if (FromLine < 1) {
+ errs() << "error: start line should be at least 1\n";
+ return true;
+ }
if (FromLine > ToLine) {
- errs() << "error: start line should be less than end line\n";
+ errs() << "error: start line should not exceed end line\n";
return true;
}
SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
@@ -314,7 +356,7 @@ emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
unsigned Errors = 0;
if (WarnFormat && !NoWarnFormat) {
- llvm::SourceMgr Mgr;
+ SourceMgr Mgr;
const char *StartBuf = Code->getBufferStart();
Mgr.AddNewSourceBuffer(
@@ -345,25 +387,40 @@ static void outputXML(const Replacements &Replaces,
if (!Status.FormatComplete)
outs() << " line='" << Status.Line << "'";
outs() << ">\n";
- if (Cursor.getNumOccurrences() != 0)
+ if (Cursor.getNumOccurrences() != 0) {
outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
<< "</cursor>\n";
+ }
outputReplacementsXML(Replaces);
outs() << "</replacements>\n";
}
+class ClangFormatDiagConsumer : public DiagnosticConsumer {
+ virtual void anchor() {}
+
+ void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+ const Diagnostic &Info) override {
+
+ SmallVector<char, 16> vec;
+ Info.FormatDiagnostic(vec);
+ errs() << "clang-format error:" << vec << "\n";
+ }
+};
+
// Returns true on error.
-static bool format(StringRef FileName) {
- if (!OutputXML && Inplace && FileName == "-") {
+static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
+ const bool IsSTDIN = FileName == "-";
+ if (!OutputXML && Inplace && IsSTDIN) {
errs() << "error: cannot use -i when reading from stdin.\n";
return false;
}
// On Windows, overwriting a file with an open file mapping doesn't work,
// so read the whole file into memory when formatting in-place.
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
- !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName)
- : MemoryBuffer::getFileOrSTDIN(FileName);
+ !OutputXML && Inplace
+ ? MemoryBuffer::getFileAsStream(FileName)
+ : MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/true);
if (std::error_code EC = CodeOrErr.getError()) {
errs() << EC.message() << "\n";
return true;
@@ -379,7 +436,7 @@ static bool format(StringRef FileName) {
if (InvalidBOM) {
errs() << "error: encoding with unsupported byte order mark \""
<< InvalidBOM << "\" detected";
- if (FileName != "-")
+ if (!IsSTDIN)
errs() << " in file '" << FileName << "'";
errs() << ".\n";
return true;
@@ -388,20 +445,41 @@ static bool format(StringRef FileName) {
std::vector<tooling::Range> Ranges;
if (fillRanges(Code.get(), Ranges))
return true;
- StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
+ StringRef AssumedFileName = IsSTDIN ? AssumeFileName : FileName;
if (AssumedFileName.empty()) {
llvm::errs() << "error: empty filenames are not allowed\n";
return true;
}
- llvm::Expected<FormatStyle> FormatStyle =
+ Expected<FormatStyle> FormatStyle =
getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
nullptr, WNoErrorList.isSet(WNoError::Unknown));
if (!FormatStyle) {
- llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
+ llvm::errs() << toString(FormatStyle.takeError()) << "\n";
return true;
}
+ StringRef QualifierAlignmentOrder = QualifierAlignment;
+
+ FormatStyle->QualifierAlignment =
+ StringSwitch<FormatStyle::QualifierAlignmentStyle>(
+ QualifierAlignmentOrder.lower())
+ .Case("right", FormatStyle::QAS_Right)
+ .Case("left", FormatStyle::QAS_Left)
+ .Default(FormatStyle->QualifierAlignment);
+
+ if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
+ FormatStyle->QualifierOrder = {"const", "volatile", "type"};
+ } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
+ FormatStyle->QualifierOrder = {"type", "const", "volatile"};
+ } else if (QualifierAlignmentOrder.contains("type")) {
+ FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
+ SmallVector<StringRef> Qualifiers;
+ QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
+ /*KeepEmpty=*/false);
+ FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
+ }
+
if (SortIncludes.getNumOccurrences() != 0) {
if (SortIncludes)
FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
@@ -414,17 +492,16 @@ static bool format(StringRef FileName) {
// To format JSON insert a variable to trick the code into thinking its
// JavaScript.
- if (FormatStyle->isJson()) {
+ if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
auto Err = Replaces.add(tooling::Replacement(
tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
- if (Err) {
+ if (Err)
llvm::errs() << "Bad Json variable insertion\n";
- }
}
auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
if (!ChangedCode) {
- llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+ llvm::errs() << toString(ChangedCode.takeError()) << "\n";
return true;
}
// Get new affected ranges after sorting `#includes`.
@@ -434,18 +511,19 @@ static bool format(StringRef FileName) {
reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
Replaces = Replaces.merge(FormatChanges);
if (OutputXML || DryRun) {
- if (DryRun) {
+ if (DryRun)
return emitReplacementWarnings(Replaces, AssumedFileName, Code);
- } else {
- outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
- }
+ outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
} else {
IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
new llvm::vfs::InMemoryFileSystem);
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
+
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+ ClangFormatDiagConsumer IgnoreDiagnostics;
DiagnosticsEngine Diagnostics(
- IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
- new DiagnosticOptions);
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
+ &IgnoreDiagnostics, false);
SourceManager Sources(Diagnostics, Files);
FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
InMemoryFileSystem.get());
@@ -467,7 +545,7 @@ static bool format(StringRef FileName) {
Rewrite.getEditBuffer(ID).write(outs());
}
}
- return false;
+ return ErrorOnIncompleteFormat && !Status.FormatComplete;
}
} // namespace format
@@ -479,29 +557,25 @@ static void PrintVersion(raw_ostream &OS) {
// Dump the configuration.
static int dumpConfig() {
- StringRef FileName;
std::unique_ptr<llvm::MemoryBuffer> Code;
- if (FileNames.empty()) {
- // We can't read the code to detect the language if there's no
- // file name, so leave Code empty here.
- FileName = AssumeFileName;
- } else {
- // Read in the code in case the filename alone isn't enough to
- // detect the language.
+ // We can't read the code to detect the language if there's no file name.
+ if (!FileNames.empty()) {
+ // Read in the code in case the filename alone isn't enough to detect the
+ // language.
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
- MemoryBuffer::getFileOrSTDIN(FileNames[0]);
+ MemoryBuffer::getFileOrSTDIN(FileNames[0], /*IsText=*/true);
if (std::error_code EC = CodeOrErr.getError()) {
llvm::errs() << EC.message() << "\n";
return 1;
}
- FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0];
Code = std::move(CodeOrErr.get());
}
- llvm::Expected<clang::format::FormatStyle> FormatStyle =
- clang::format::getStyle(Style, FileName, FallbackStyle,
- Code ? Code->getBuffer() : "");
+ Expected<clang::format::FormatStyle> FormatStyle = clang::format::getStyle(
+ Style,
+ FileNames.empty() || FileNames[0] == "-" ? AssumeFileName : FileNames[0],
+ FallbackStyle, Code ? Code->getBuffer() : "");
if (!FormatStyle) {
- llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
+ llvm::errs() << toString(FormatStyle.takeError()) << "\n";
return 1;
}
std::string Config = clang::format::configurationAsText(*FormatStyle);
@@ -509,8 +583,96 @@ static int dumpConfig() {
return 0;
}
+using String = SmallString<128>;
+static String IgnoreDir; // Directory of .clang-format-ignore file.
+static String PrevDir; // Directory of previous `FilePath`.
+static SmallVector<String> Patterns; // Patterns in .clang-format-ignore file.
+
+// Check whether `FilePath` is ignored according to the nearest
+// .clang-format-ignore file based on the rules below:
+// - A blank line is skipped.
+// - Leading and trailing spaces of a line are trimmed.
+// - A line starting with a hash (`#`) is a comment.
+// - A non-comment line is a single pattern.
+// - The slash (`/`) is used as the directory separator.
+// - A pattern is relative to the directory of the .clang-format-ignore file (or
+// the root directory if the pattern starts with a slash).
+// - A pattern is negated if it starts with a bang (`!`).
+static bool isIgnored(StringRef FilePath) {
+ using namespace llvm::sys::fs;
+ if (!is_regular_file(FilePath))
+ return false;
+
+ String Path;
+ String AbsPath{FilePath};
+
+ using namespace llvm::sys::path;
+ make_absolute(AbsPath);
+ remove_dots(AbsPath, /*remove_dot_dot=*/true);
+
+ if (StringRef Dir{parent_path(AbsPath)}; PrevDir != Dir) {
+ PrevDir = Dir;
+
+ for (;;) {
+ Path = Dir;
+ append(Path, ".clang-format-ignore");
+ if (is_regular_file(Path))
+ break;
+ Dir = parent_path(Dir);
+ if (Dir.empty())
+ return false;
+ }
+
+ IgnoreDir = convert_to_slash(Dir);
+
+ std::ifstream IgnoreFile{Path.c_str()};
+ if (!IgnoreFile.good())
+ return false;
+
+ Patterns.clear();
+
+ for (std::string Line; std::getline(IgnoreFile, Line);) {
+ if (const auto Pattern{StringRef{Line}.trim()};
+ // Skip empty and comment lines.
+ !Pattern.empty() && Pattern[0] != '#') {
+ Patterns.push_back(Pattern);
+ }
+ }
+ }
+
+ if (IgnoreDir.empty())
+ return false;
+
+ const auto Pathname{convert_to_slash(AbsPath)};
+ for (const auto &Pat : Patterns) {
+ const bool IsNegated = Pat[0] == '!';
+ StringRef Pattern{Pat};
+ if (IsNegated)
+ Pattern = Pattern.drop_front();
+
+ if (Pattern.empty())
+ continue;
+
+ Pattern = Pattern.ltrim();
+
+ // `Pattern` is relative to `IgnoreDir` unless it starts with a slash.
+ // This doesn't support patterns containing drive names (e.g. `C:`).
+ if (Pattern[0] != '/') {
+ Path = IgnoreDir;
+ append(Path, Style::posix, Pattern);
+ remove_dots(Path, /*remove_dot_dot=*/true, Style::posix);
+ Pattern = Path;
+ }
+
+ if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)
+ return true;
+ }
+
+ return false;
+}
+
int main(int argc, const char **argv) {
- llvm::InitLLVM X(argc, argv);
+ InitLLVM X(argc, argv);
cl::HideUnrelatedOptions(ClangFormatCategory);
@@ -530,25 +692,46 @@ int main(int argc, const char **argv) {
return 0;
}
- if (DumpConfig) {
+ if (DumpConfig)
return dumpConfig();
- }
- bool Error = false;
- if (FileNames.empty()) {
- Error = clang::format::format("-");
- return Error ? 1 : 0;
+ if (!Files.empty()) {
+ std::ifstream ExternalFileOfFiles{std::string(Files)};
+ std::string Line;
+ unsigned LineNo = 1;
+ while (std::getline(ExternalFileOfFiles, Line)) {
+ FileNames.push_back(Line);
+ LineNo++;
+ }
+ errs() << "Clang-formating " << LineNo << " files\n";
}
- if (FileNames.size() != 1 &&
+
+ if (FileNames.empty())
+ return clang::format::format("-", FailOnIncompleteFormat);
+
+ if (FileNames.size() > 1 &&
(!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
errs() << "error: -offset, -length and -lines can only be used for "
"single file.\n";
return 1;
}
+
+ unsigned FileNo = 1;
+ bool Error = false;
for (const auto &FileName : FileNames) {
- if (Verbose)
- errs() << "Formatting " << FileName << "\n";
- Error |= clang::format::format(FileName);
+ const bool Ignored = isIgnored(FileName);
+ if (ListIgnored) {
+ if (Ignored)
+ outs() << FileName << '\n';
+ continue;
+ }
+ if (Ignored)
+ continue;
+ if (Verbose) {
+ errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
+ << FileName << "\n";
+ }
+ Error |= clang::format::format(FileName, FailOnIncompleteFormat);
}
return Error ? 1 : 0;
}
diff --git a/contrib/llvm-project/clang/tools/clang-repl/ClangRepl.cpp b/contrib/llvm-project/clang/tools/clang-repl/ClangRepl.cpp
deleted file mode 100644
index ba6bb11abc86..000000000000
--- a/contrib/llvm-project/clang/tools/clang-repl/ClangRepl.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-//===--- tools/clang-repl/ClangRepl.cpp - clang-repl - the Clang REPL -----===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements a REPL tool on top of clang.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Basic/Diagnostic.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/FrontendDiagnostic.h"
-#include "clang/Interpreter/Interpreter.h"
-
-#include "llvm/ExecutionEngine/Orc/LLJIT.h"
-#include "llvm/LineEditor/LineEditor.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/ManagedStatic.h" // llvm_shutdown
-#include "llvm/Support/Signals.h"
-#include "llvm/Support/TargetSelect.h" // llvm::Initialize*
-
-static llvm::cl::list<std::string>
- ClangArgs("Xcc", llvm::cl::ZeroOrMore,
- llvm::cl::desc("Argument to pass to the CompilerInvocation"),
- llvm::cl::CommaSeparated);
-static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
- llvm::cl::Hidden);
-static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
- llvm::cl::ZeroOrMore,
- llvm::cl::desc("[code to run]"));
-
-static void LLVMErrorHandler(void *UserData, const std::string &Message,
- bool GenCrashDiag) {
- auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
-
- Diags.Report(clang::diag::err_fe_error_backend) << Message;
-
- // Run the interrupt handlers to make sure any special cleanups get done, in
- // particular that we remove files registered with RemoveFileOnSignal.
- llvm::sys::RunInterruptHandlers();
-
- // We cannot recover from llvm errors. When reporting a fatal error, exit
- // with status 70 to generate crash diagnostics. For BSD systems this is
- // defined as an internal software error. Otherwise, exit with status 1.
-
- exit(GenCrashDiag ? 70 : 1);
-}
-
-llvm::ExitOnError ExitOnErr;
-int main(int argc, const char **argv) {
- ExitOnErr.setBanner("clang-repl: ");
- llvm::cl::ParseCommandLineOptions(argc, argv);
-
- std::vector<const char *> ClangArgv(ClangArgs.size());
- std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
- [](const std::string &s) -> const char * { return s.data(); });
- llvm::InitializeNativeTarget();
- llvm::InitializeNativeTargetAsmPrinter();
-
- if (OptHostSupportsJit) {
- auto J = llvm::orc::LLJITBuilder().create();
- if (J)
- llvm::outs() << "true\n";
- else {
- llvm::consumeError(J.takeError());
- llvm::outs() << "false\n";
- }
- return 0;
- }
-
- // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
- // can replace the boilerplate code for creation of the compiler instance.
- auto CI = ExitOnErr(clang::IncrementalCompilerBuilder::create(ClangArgv));
-
- // Set an error handler, so that any LLVM backend diagnostics go through our
- // error handler.
- llvm::install_fatal_error_handler(LLVMErrorHandler,
- static_cast<void *>(&CI->getDiagnostics()));
-
- auto Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
- for (const std::string &input : OptInputs) {
- if (auto Err = Interp->ParseAndExecute(input))
- llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
- }
-
- if (OptInputs.empty()) {
- llvm::LineEditor LE("clang-repl");
- // FIXME: Add LE.setListCompleter
- while (llvm::Optional<std::string> Line = LE.readLine()) {
- if (*Line == "quit")
- break;
- if (auto Err = Interp->ParseAndExecute(*Line))
- llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
- }
- }
-
- // Our error handler depends on the Diagnostics object, which we're
- // potentially about to delete. Uninstall the handler now so that any
- // later errors use the default handling behavior instead.
- llvm::remove_fatal_error_handler();
-
- llvm::llvm_shutdown();
-
- return 0;
-}
diff --git a/contrib/llvm-project/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/contrib/llvm-project/clang/tools/clang-scan-deps/ClangScanDeps.cpp
new file mode 100644
index 000000000000..867df19c863f
--- /dev/null
+++ b/contrib/llvm-project/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -0,0 +1,1055 @@
+//===- ClangScanDeps.cpp - Implementation of clang-scan-deps --------------===//
+//
+// 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/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
+#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
+#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
+#include "clang/Tooling/JSONCompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/LLVMDriver.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/TargetParser/Host.h"
+#include <mutex>
+#include <optional>
+#include <thread>
+
+#include "Opts.inc"
+
+using namespace clang;
+using namespace tooling::dependencies;
+
+namespace {
+
+using namespace llvm::opt;
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) \
+ constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
+ constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
+ NAME##_init, std::size(NAME##_init) - 1);
+#include "Opts.inc"
+#undef PREFIX
+
+const llvm::opt::OptTable::Info InfoTable[] = {
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
+#include "Opts.inc"
+#undef OPTION
+};
+
+class ScanDepsOptTable : public llvm::opt::GenericOptTable {
+public:
+ ScanDepsOptTable() : GenericOptTable(InfoTable) {
+ setGroupedShortOptions(true);
+ }
+};
+
+enum ResourceDirRecipeKind {
+ RDRK_ModifyCompilerPath,
+ RDRK_InvokeCompiler,
+};
+
+static std::string OutputFileName = "-";
+static ScanningMode ScanMode = ScanningMode::DependencyDirectivesScan;
+static ScanningOutputFormat Format = ScanningOutputFormat::Make;
+static ScanningOptimizations OptimizeArgs;
+static std::string ModuleFilesDir;
+static bool EagerLoadModules;
+static unsigned NumThreads = 0;
+static std::string CompilationDB;
+static std::string ModuleName;
+static std::vector<std::string> ModuleDepTargets;
+static bool DeprecatedDriverCommand;
+static ResourceDirRecipeKind ResourceDirRecipe;
+static bool Verbose;
+static bool PrintTiming;
+static llvm::BumpPtrAllocator Alloc;
+static llvm::StringSaver Saver{Alloc};
+static std::vector<const char *> CommandLine;
+
+#ifndef NDEBUG
+static constexpr bool DoRoundTripDefault = true;
+#else
+static constexpr bool DoRoundTripDefault = false;
+#endif
+
+static bool RoundTripArgs = DoRoundTripDefault;
+
+static void ParseArgs(int argc, char **argv) {
+ ScanDepsOptTable Tbl;
+ llvm::StringRef ToolName = argv[0];
+ llvm::opt::InputArgList Args =
+ Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
+ llvm::errs() << Msg << '\n';
+ std::exit(1);
+ });
+
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(llvm::outs(), "clang-scan-deps [options]", "clang-scan-deps");
+ std::exit(0);
+ }
+ if (Args.hasArg(OPT_version)) {
+ llvm::outs() << ToolName << '\n';
+ llvm::cl::PrintVersionMessage();
+ std::exit(0);
+ }
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_mode_EQ)) {
+ auto ModeType =
+ llvm::StringSwitch<std::optional<ScanningMode>>(A->getValue())
+ .Case("preprocess-dependency-directives",
+ ScanningMode::DependencyDirectivesScan)
+ .Case("preprocess", ScanningMode::CanonicalPreprocessing)
+ .Default(std::nullopt);
+ if (!ModeType) {
+ llvm::errs() << ToolName
+ << ": for the --mode option: Cannot find option named '"
+ << A->getValue() << "'\n";
+ std::exit(1);
+ }
+ ScanMode = *ModeType;
+ }
+
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_format_EQ)) {
+ auto FormatType =
+ llvm::StringSwitch<std::optional<ScanningOutputFormat>>(A->getValue())
+ .Case("make", ScanningOutputFormat::Make)
+ .Case("p1689", ScanningOutputFormat::P1689)
+ .Case("experimental-full", ScanningOutputFormat::Full)
+ .Default(std::nullopt);
+ if (!FormatType) {
+ llvm::errs() << ToolName
+ << ": for the --format option: Cannot find option named '"
+ << A->getValue() << "'\n";
+ std::exit(1);
+ }
+ Format = *FormatType;
+ }
+
+ std::vector<std::string> OptimizationFlags =
+ Args.getAllArgValues(OPT_optimize_args_EQ);
+ OptimizeArgs = ScanningOptimizations::None;
+ for (const auto &Arg : OptimizationFlags) {
+ auto Optimization =
+ llvm::StringSwitch<std::optional<ScanningOptimizations>>(Arg)
+ .Case("none", ScanningOptimizations::None)
+ .Case("header-search", ScanningOptimizations::HeaderSearch)
+ .Case("system-warnings", ScanningOptimizations::SystemWarnings)
+ .Case("vfs", ScanningOptimizations::VFS)
+ .Case("canonicalize-macros", ScanningOptimizations::Macros)
+ .Case("all", ScanningOptimizations::All)
+ .Default(std::nullopt);
+ if (!Optimization) {
+ llvm::errs()
+ << ToolName
+ << ": for the --optimize-args option: Cannot find option named '"
+ << Arg << "'\n";
+ std::exit(1);
+ }
+ OptimizeArgs |= *Optimization;
+ }
+ if (OptimizationFlags.empty())
+ OptimizeArgs = ScanningOptimizations::Default;
+
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_module_files_dir_EQ))
+ ModuleFilesDir = A->getValue();
+
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_o))
+ OutputFileName = A->getValue();
+
+ EagerLoadModules = Args.hasArg(OPT_eager_load_pcm);
+
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_j)) {
+ StringRef S{A->getValue()};
+ if (!llvm::to_integer(S, NumThreads, 0)) {
+ llvm::errs() << ToolName << ": for the -j option: '" << S
+ << "' value invalid for uint argument!\n";
+ std::exit(1);
+ }
+ }
+
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_compilation_database_EQ))
+ CompilationDB = A->getValue();
+
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_module_name_EQ))
+ ModuleName = A->getValue();
+
+ for (const llvm::opt::Arg *A : Args.filtered(OPT_dependency_target_EQ))
+ ModuleDepTargets.emplace_back(A->getValue());
+
+ DeprecatedDriverCommand = Args.hasArg(OPT_deprecated_driver_command);
+
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_resource_dir_recipe_EQ)) {
+ auto Kind =
+ llvm::StringSwitch<std::optional<ResourceDirRecipeKind>>(A->getValue())
+ .Case("modify-compiler-path", RDRK_ModifyCompilerPath)
+ .Case("invoke-compiler", RDRK_InvokeCompiler)
+ .Default(std::nullopt);
+ if (!Kind) {
+ llvm::errs() << ToolName
+ << ": for the --resource-dir-recipe option: Cannot find "
+ "option named '"
+ << A->getValue() << "'\n";
+ std::exit(1);
+ }
+ ResourceDirRecipe = *Kind;
+ }
+
+ PrintTiming = Args.hasArg(OPT_print_timing);
+
+ Verbose = Args.hasArg(OPT_verbose);
+
+ RoundTripArgs = Args.hasArg(OPT_round_trip_args);
+
+ if (const llvm::opt::Arg *A = Args.getLastArgNoClaim(OPT_DASH_DASH))
+ CommandLine.assign(A->getValues().begin(), A->getValues().end());
+}
+
+class SharedStream {
+public:
+ SharedStream(raw_ostream &OS) : OS(OS) {}
+ void applyLocked(llvm::function_ref<void(raw_ostream &OS)> Fn) {
+ std::unique_lock<std::mutex> LockGuard(Lock);
+ Fn(OS);
+ OS.flush();
+ }
+
+private:
+ std::mutex Lock;
+ raw_ostream &OS;
+};
+
+class ResourceDirectoryCache {
+public:
+ /// findResourceDir finds the resource directory relative to the clang
+ /// compiler being used in Args, by running it with "-print-resource-dir"
+ /// option and cache the results for reuse. \returns resource directory path
+ /// associated with the given invocation command or empty string if the
+ /// compiler path is NOT an absolute path.
+ StringRef findResourceDir(const tooling::CommandLineArguments &Args,
+ bool ClangCLMode) {
+ if (Args.size() < 1)
+ return "";
+
+ const std::string &ClangBinaryPath = Args[0];
+ if (!llvm::sys::path::is_absolute(ClangBinaryPath))
+ return "";
+
+ const std::string &ClangBinaryName =
+ std::string(llvm::sys::path::filename(ClangBinaryPath));
+
+ std::unique_lock<std::mutex> LockGuard(CacheLock);
+ const auto &CachedResourceDir = Cache.find(ClangBinaryPath);
+ if (CachedResourceDir != Cache.end())
+ return CachedResourceDir->second;
+
+ std::vector<StringRef> PrintResourceDirArgs{ClangBinaryName};
+ if (ClangCLMode)
+ PrintResourceDirArgs.push_back("/clang:-print-resource-dir");
+ else
+ PrintResourceDirArgs.push_back("-print-resource-dir");
+
+ llvm::SmallString<64> OutputFile, ErrorFile;
+ llvm::sys::fs::createTemporaryFile("print-resource-dir-output",
+ "" /*no-suffix*/, OutputFile);
+ llvm::sys::fs::createTemporaryFile("print-resource-dir-error",
+ "" /*no-suffix*/, ErrorFile);
+ llvm::FileRemover OutputRemover(OutputFile.c_str());
+ llvm::FileRemover ErrorRemover(ErrorFile.c_str());
+ std::optional<StringRef> Redirects[] = {
+ {""}, // Stdin
+ OutputFile.str(),
+ ErrorFile.str(),
+ };
+ if (llvm::sys::ExecuteAndWait(ClangBinaryPath, PrintResourceDirArgs, {},
+ Redirects)) {
+ auto ErrorBuf = llvm::MemoryBuffer::getFile(ErrorFile.c_str());
+ llvm::errs() << ErrorBuf.get()->getBuffer();
+ return "";
+ }
+
+ auto OutputBuf = llvm::MemoryBuffer::getFile(OutputFile.c_str());
+ if (!OutputBuf)
+ return "";
+ StringRef Output = OutputBuf.get()->getBuffer().rtrim('\n');
+
+ Cache[ClangBinaryPath] = Output.str();
+ return Cache[ClangBinaryPath];
+ }
+
+private:
+ std::map<std::string, std::string> Cache;
+ std::mutex CacheLock;
+};
+
+} // end anonymous namespace
+
+/// Takes the result of a dependency scan and prints error / dependency files
+/// based on the result.
+///
+/// \returns True on error.
+static bool
+handleMakeDependencyToolResult(const std::string &Input,
+ llvm::Expected<std::string> &MaybeFile,
+ SharedStream &OS, SharedStream &Errs) {
+ if (!MaybeFile) {
+ llvm::handleAllErrors(
+ MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) {
+ Errs.applyLocked([&](raw_ostream &OS) {
+ OS << "Error while scanning dependencies for " << Input << ":\n";
+ OS << Err.getMessage();
+ });
+ });
+ return true;
+ }
+ OS.applyLocked([&](raw_ostream &OS) { OS << *MaybeFile; });
+ return false;
+}
+
+static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
+ std::vector<llvm::StringRef> Strings;
+ for (auto &&I : Set)
+ Strings.push_back(I.getKey());
+ llvm::sort(Strings);
+ return llvm::json::Array(Strings);
+}
+
+// Technically, we don't need to sort the dependency list to get determinism.
+// Leaving these be will simply preserve the import order.
+static llvm::json::Array toJSONSorted(std::vector<ModuleID> V) {
+ llvm::sort(V);
+
+ llvm::json::Array Ret;
+ for (const ModuleID &MID : V)
+ Ret.push_back(llvm::json::Object(
+ {{"module-name", MID.ModuleName}, {"context-hash", MID.ContextHash}}));
+ return Ret;
+}
+
+static llvm::json::Array
+toJSONSorted(llvm::SmallVector<Module::LinkLibrary, 2> &LinkLibs) {
+ llvm::sort(LinkLibs, [](const Module::LinkLibrary &lhs,
+ const Module::LinkLibrary &rhs) {
+ return lhs.Library < rhs.Library;
+ });
+
+ llvm::json::Array Ret;
+ for (const Module::LinkLibrary &LL : LinkLibs)
+ Ret.push_back(llvm::json::Object(
+ {{"link-name", LL.Library}, {"isFramework", LL.IsFramework}}));
+ return Ret;
+}
+
+// Thread safe.
+class FullDeps {
+public:
+ FullDeps(size_t NumInputs) : Inputs(NumInputs) {}
+
+ void mergeDeps(StringRef Input, TranslationUnitDeps TUDeps,
+ size_t InputIndex) {
+ mergeDeps(std::move(TUDeps.ModuleGraph), InputIndex);
+
+ InputDeps ID;
+ ID.FileName = std::string(Input);
+ ID.ContextHash = std::move(TUDeps.ID.ContextHash);
+ ID.FileDeps = std::move(TUDeps.FileDeps);
+ ID.ModuleDeps = std::move(TUDeps.ClangModuleDeps);
+ ID.DriverCommandLine = std::move(TUDeps.DriverCommandLine);
+ ID.Commands = std::move(TUDeps.Commands);
+
+ assert(InputIndex < Inputs.size() && "Input index out of bounds");
+ assert(Inputs[InputIndex].FileName.empty() && "Result already populated");
+ Inputs[InputIndex] = std::move(ID);
+ }
+
+ void mergeDeps(ModuleDepsGraph Graph, size_t InputIndex) {
+ std::vector<ModuleDeps *> NewMDs;
+ {
+ std::unique_lock<std::mutex> ul(Lock);
+ for (const ModuleDeps &MD : Graph) {
+ auto I = Modules.find({MD.ID, 0});
+ if (I != Modules.end()) {
+ I->first.InputIndex = std::min(I->first.InputIndex, InputIndex);
+ continue;
+ }
+ auto Res = Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)});
+ NewMDs.push_back(&Res->second);
+ }
+ // First call to \c getBuildArguments is somewhat expensive. Let's call it
+ // on the current thread (instead of the main one), and outside the
+ // critical section.
+ for (ModuleDeps *MD : NewMDs)
+ (void)MD->getBuildArguments();
+ }
+ }
+
+ bool roundTripCommand(ArrayRef<std::string> ArgStrs,
+ DiagnosticsEngine &Diags) {
+ if (ArgStrs.empty() || ArgStrs[0] != "-cc1")
+ return false;
+ SmallVector<const char *> Args;
+ for (const std::string &Arg : ArgStrs)
+ Args.push_back(Arg.c_str());
+ return !CompilerInvocation::checkCC1RoundTrip(Args, Diags);
+ }
+
+ // Returns \c true if any command lines fail to round-trip. We expect
+ // commands already be canonical when output by the scanner.
+ bool roundTripCommands(raw_ostream &ErrOS) {
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions{};
+ TextDiagnosticPrinter DiagConsumer(ErrOS, &*DiagOpts);
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ CompilerInstance::createDiagnostics(&*DiagOpts, &DiagConsumer,
+ /*ShouldOwnClient=*/false);
+
+ for (auto &&M : Modules)
+ if (roundTripCommand(M.second.getBuildArguments(), *Diags))
+ return true;
+
+ for (auto &&I : Inputs)
+ for (const auto &Cmd : I.Commands)
+ if (roundTripCommand(Cmd.Arguments, *Diags))
+ return true;
+
+ return false;
+ }
+
+ void printFullOutput(raw_ostream &OS) {
+ // Skip sorting modules and constructing the JSON object if the output
+ // cannot be observed anyway. This makes timings less noisy.
+ if (&OS == &llvm::nulls())
+ return;
+
+ // Sort the modules by name to get a deterministic order.
+ std::vector<IndexedModuleID> ModuleIDs;
+ for (auto &&M : Modules)
+ ModuleIDs.push_back(M.first);
+ llvm::sort(ModuleIDs);
+
+ using namespace llvm::json;
+
+ Array OutModules;
+ for (auto &&ModID : ModuleIDs) {
+ auto &MD = Modules[ModID];
+ Object O{{"name", MD.ID.ModuleName},
+ {"context-hash", MD.ID.ContextHash},
+ {"file-deps", toJSONSorted(MD.FileDeps)},
+ {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
+ {"clang-modulemap-file", MD.ClangModuleMapFile},
+ {"command-line", MD.getBuildArguments()},
+ {"link-libraries", toJSONSorted(MD.LinkLibraries)}};
+ OutModules.push_back(std::move(O));
+ }
+
+ Array TUs;
+ for (auto &&I : Inputs) {
+ Array Commands;
+ if (I.DriverCommandLine.empty()) {
+ for (const auto &Cmd : I.Commands) {
+ Object O{
+ {"input-file", I.FileName},
+ {"clang-context-hash", I.ContextHash},
+ {"file-deps", I.FileDeps},
+ {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
+ {"executable", Cmd.Executable},
+ {"command-line", Cmd.Arguments},
+ };
+ Commands.push_back(std::move(O));
+ }
+ } else {
+ Object O{
+ {"input-file", I.FileName},
+ {"clang-context-hash", I.ContextHash},
+ {"file-deps", I.FileDeps},
+ {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
+ {"executable", "clang"},
+ {"command-line", I.DriverCommandLine},
+ };
+ Commands.push_back(std::move(O));
+ }
+ TUs.push_back(Object{
+ {"commands", std::move(Commands)},
+ });
+ }
+
+ Object Output{
+ {"modules", std::move(OutModules)},
+ {"translation-units", std::move(TUs)},
+ };
+
+ OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
+ }
+
+private:
+ struct IndexedModuleID {
+ ModuleID ID;
+
+ // FIXME: This is mutable so that it can still be updated after insertion
+ // into an unordered associative container. This is "fine", since this
+ // field doesn't contribute to the hash, but it's a brittle hack.
+ mutable size_t InputIndex;
+
+ bool operator==(const IndexedModuleID &Other) const {
+ return ID == Other.ID;
+ }
+
+ bool operator<(const IndexedModuleID &Other) const {
+ /// We need the output of clang-scan-deps to be deterministic. However,
+ /// the dependency graph may contain two modules with the same name. How
+ /// do we decide which one to print first? If we made that decision based
+ /// on the context hash, the ordering would be deterministic, but
+ /// different across machines. This can happen for example when the inputs
+ /// or the SDKs (which both contribute to the "context" hash) live in
+ /// different absolute locations. We solve that by tracking the index of
+ /// the first input TU that (transitively) imports the dependency, which
+ /// is always the same for the same input, resulting in deterministic
+ /// sorting that's also reproducible across machines.
+ return std::tie(ID.ModuleName, InputIndex) <
+ std::tie(Other.ID.ModuleName, Other.InputIndex);
+ }
+
+ struct Hasher {
+ std::size_t operator()(const IndexedModuleID &IMID) const {
+ return llvm::hash_value(IMID.ID);
+ }
+ };
+ };
+
+ struct InputDeps {
+ std::string FileName;
+ std::string ContextHash;
+ std::vector<std::string> FileDeps;
+ std::vector<ModuleID> ModuleDeps;
+ std::vector<std::string> DriverCommandLine;
+ std::vector<Command> Commands;
+ };
+
+ std::mutex Lock;
+ std::unordered_map<IndexedModuleID, ModuleDeps, IndexedModuleID::Hasher>
+ Modules;
+ std::vector<InputDeps> Inputs;
+};
+
+static bool handleTranslationUnitResult(
+ StringRef Input, llvm::Expected<TranslationUnitDeps> &MaybeTUDeps,
+ FullDeps &FD, size_t InputIndex, SharedStream &OS, SharedStream &Errs) {
+ if (!MaybeTUDeps) {
+ llvm::handleAllErrors(
+ MaybeTUDeps.takeError(), [&Input, &Errs](llvm::StringError &Err) {
+ Errs.applyLocked([&](raw_ostream &OS) {
+ OS << "Error while scanning dependencies for " << Input << ":\n";
+ OS << Err.getMessage();
+ });
+ });
+ return true;
+ }
+ FD.mergeDeps(Input, std::move(*MaybeTUDeps), InputIndex);
+ return false;
+}
+
+static bool handleModuleResult(
+ StringRef ModuleName, llvm::Expected<ModuleDepsGraph> &MaybeModuleGraph,
+ FullDeps &FD, size_t InputIndex, SharedStream &OS, SharedStream &Errs) {
+ if (!MaybeModuleGraph) {
+ llvm::handleAllErrors(MaybeModuleGraph.takeError(),
+ [&ModuleName, &Errs](llvm::StringError &Err) {
+ Errs.applyLocked([&](raw_ostream &OS) {
+ OS << "Error while scanning dependencies for "
+ << ModuleName << ":\n";
+ OS << Err.getMessage();
+ });
+ });
+ return true;
+ }
+ FD.mergeDeps(std::move(*MaybeModuleGraph), InputIndex);
+ return false;
+}
+
+class P1689Deps {
+public:
+ void printDependencies(raw_ostream &OS) {
+ addSourcePathsToRequires();
+ // Sort the modules by name to get a deterministic order.
+ llvm::sort(Rules, [](const P1689Rule &A, const P1689Rule &B) {
+ return A.PrimaryOutput < B.PrimaryOutput;
+ });
+
+ using namespace llvm::json;
+ Array OutputRules;
+ for (const P1689Rule &R : Rules) {
+ Object O{{"primary-output", R.PrimaryOutput}};
+
+ if (R.Provides) {
+ Array Provides;
+ Object Provided{{"logical-name", R.Provides->ModuleName},
+ {"source-path", R.Provides->SourcePath},
+ {"is-interface", R.Provides->IsStdCXXModuleInterface}};
+ Provides.push_back(std::move(Provided));
+ O.insert({"provides", std::move(Provides)});
+ }
+
+ Array Requires;
+ for (const P1689ModuleInfo &Info : R.Requires) {
+ Object RequiredInfo{{"logical-name", Info.ModuleName}};
+ if (!Info.SourcePath.empty())
+ RequiredInfo.insert({"source-path", Info.SourcePath});
+ Requires.push_back(std::move(RequiredInfo));
+ }
+
+ if (!Requires.empty())
+ O.insert({"requires", std::move(Requires)});
+
+ OutputRules.push_back(std::move(O));
+ }
+
+ Object Output{
+ {"version", 1}, {"revision", 0}, {"rules", std::move(OutputRules)}};
+
+ OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
+ }
+
+ void addRules(P1689Rule &Rule) {
+ std::unique_lock<std::mutex> LockGuard(Lock);
+ Rules.push_back(Rule);
+ }
+
+private:
+ void addSourcePathsToRequires() {
+ llvm::DenseMap<StringRef, StringRef> ModuleSourceMapper;
+ for (const P1689Rule &R : Rules)
+ if (R.Provides && !R.Provides->SourcePath.empty())
+ ModuleSourceMapper[R.Provides->ModuleName] = R.Provides->SourcePath;
+
+ for (P1689Rule &R : Rules) {
+ for (P1689ModuleInfo &Info : R.Requires) {
+ auto Iter = ModuleSourceMapper.find(Info.ModuleName);
+ if (Iter != ModuleSourceMapper.end())
+ Info.SourcePath = Iter->second;
+ }
+ }
+ }
+
+ std::mutex Lock;
+ std::vector<P1689Rule> Rules;
+};
+
+static bool
+handleP1689DependencyToolResult(const std::string &Input,
+ llvm::Expected<P1689Rule> &MaybeRule,
+ P1689Deps &PD, SharedStream &Errs) {
+ if (!MaybeRule) {
+ llvm::handleAllErrors(
+ MaybeRule.takeError(), [&Input, &Errs](llvm::StringError &Err) {
+ Errs.applyLocked([&](raw_ostream &OS) {
+ OS << "Error while scanning dependencies for " << Input << ":\n";
+ OS << Err.getMessage();
+ });
+ });
+ return true;
+ }
+ PD.addRules(*MaybeRule);
+ return false;
+}
+
+/// Construct a path for the explicitly built PCM.
+static std::string constructPCMPath(ModuleID MID, StringRef OutputDir) {
+ SmallString<256> ExplicitPCMPath(OutputDir);
+ llvm::sys::path::append(ExplicitPCMPath, MID.ContextHash,
+ MID.ModuleName + "-" + MID.ContextHash + ".pcm");
+ return std::string(ExplicitPCMPath);
+}
+
+static std::string lookupModuleOutput(const ModuleID &MID, ModuleOutputKind MOK,
+ StringRef OutputDir) {
+ std::string PCMPath = constructPCMPath(MID, OutputDir);
+ switch (MOK) {
+ case ModuleOutputKind::ModuleFile:
+ return PCMPath;
+ case ModuleOutputKind::DependencyFile:
+ return PCMPath + ".d";
+ case ModuleOutputKind::DependencyTargets:
+ // Null-separate the list of targets.
+ return join(ModuleDepTargets, StringRef("\0", 1));
+ case ModuleOutputKind::DiagnosticSerializationFile:
+ return PCMPath + ".diag";
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+static std::string getModuleCachePath(ArrayRef<std::string> Args) {
+ for (StringRef Arg : llvm::reverse(Args)) {
+ Arg.consume_front("/clang:");
+ if (Arg.consume_front("-fmodules-cache-path="))
+ return std::string(Arg);
+ }
+ SmallString<128> Path;
+ driver::Driver::getDefaultModuleCachePath(Path);
+ return std::string(Path);
+}
+
+/// Attempts to construct the compilation database from '-compilation-database'
+/// or from the arguments following the positional '--'.
+static std::unique_ptr<tooling::CompilationDatabase>
+getCompilationDatabase(int argc, char **argv, std::string &ErrorMessage) {
+ ParseArgs(argc, argv);
+
+ if (!(CommandLine.empty() ^ CompilationDB.empty())) {
+ llvm::errs() << "The compilation command line must be provided either via "
+ "'-compilation-database' or after '--'.";
+ return nullptr;
+ }
+
+ if (!CompilationDB.empty())
+ return tooling::JSONCompilationDatabase::loadFromFile(
+ CompilationDB, ErrorMessage,
+ tooling::JSONCommandLineSyntax::AutoDetect);
+
+ llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ CompilerInstance::createDiagnostics(new DiagnosticOptions);
+ driver::Driver TheDriver(CommandLine[0], llvm::sys::getDefaultTargetTriple(),
+ *Diags);
+ TheDriver.setCheckInputsExist(false);
+ std::unique_ptr<driver::Compilation> C(
+ TheDriver.BuildCompilation(CommandLine));
+ if (!C || C->getJobs().empty())
+ return nullptr;
+
+ auto Cmd = C->getJobs().begin();
+ auto CI = std::make_unique<CompilerInvocation>();
+ CompilerInvocation::CreateFromArgs(*CI, Cmd->getArguments(), *Diags,
+ CommandLine[0]);
+ if (!CI)
+ return nullptr;
+
+ FrontendOptions &FEOpts = CI->getFrontendOpts();
+ if (FEOpts.Inputs.size() != 1) {
+ llvm::errs()
+ << "Exactly one input file is required in the per-file mode ('--').\n";
+ return nullptr;
+ }
+
+ // There might be multiple jobs for a compilation. Extract the specified
+ // output filename from the last job.
+ auto LastCmd = C->getJobs().end();
+ LastCmd--;
+ if (LastCmd->getOutputFilenames().size() != 1) {
+ llvm::errs()
+ << "Exactly one output file is required in the per-file mode ('--').\n";
+ return nullptr;
+ }
+ StringRef OutputFile = LastCmd->getOutputFilenames().front();
+
+ class InplaceCompilationDatabase : public tooling::CompilationDatabase {
+ public:
+ InplaceCompilationDatabase(StringRef InputFile, StringRef OutputFile,
+ ArrayRef<const char *> CommandLine)
+ : Command(".", InputFile, {}, OutputFile) {
+ for (auto *C : CommandLine)
+ Command.CommandLine.push_back(C);
+ }
+
+ std::vector<tooling::CompileCommand>
+ getCompileCommands(StringRef FilePath) const override {
+ if (FilePath != Command.Filename)
+ return {};
+ return {Command};
+ }
+
+ std::vector<std::string> getAllFiles() const override {
+ return {Command.Filename};
+ }
+
+ std::vector<tooling::CompileCommand>
+ getAllCompileCommands() const override {
+ return {Command};
+ }
+
+ private:
+ tooling::CompileCommand Command;
+ };
+
+ return std::make_unique<InplaceCompilationDatabase>(
+ FEOpts.Inputs[0].getFile(), OutputFile, CommandLine);
+}
+
+int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
+ llvm::InitializeAllTargetInfos();
+ std::string ErrorMessage;
+ std::unique_ptr<tooling::CompilationDatabase> Compilations =
+ getCompilationDatabase(argc, argv, ErrorMessage);
+ if (!Compilations) {
+ llvm::errs() << ErrorMessage << "\n";
+ return 1;
+ }
+
+ llvm::cl::PrintOptionValues();
+
+ // Expand response files in advance, so that we can "see" all the arguments
+ // when adjusting below.
+ Compilations = expandResponseFiles(std::move(Compilations),
+ llvm::vfs::getRealFileSystem());
+
+ Compilations = inferTargetAndDriverMode(std::move(Compilations));
+
+ Compilations = inferToolLocation(std::move(Compilations));
+
+ // The command options are rewritten to run Clang in preprocessor only mode.
+ auto AdjustingCompilations =
+ std::make_unique<tooling::ArgumentsAdjustingCompilations>(
+ std::move(Compilations));
+ ResourceDirectoryCache ResourceDirCache;
+
+ AdjustingCompilations->appendArgumentsAdjuster(
+ [&ResourceDirCache](const tooling::CommandLineArguments &Args,
+ StringRef FileName) {
+ std::string LastO;
+ bool HasResourceDir = false;
+ bool ClangCLMode = false;
+ auto FlagsEnd = llvm::find(Args, "--");
+ if (FlagsEnd != Args.begin()) {
+ ClangCLMode =
+ llvm::sys::path::stem(Args[0]).contains_insensitive("clang-cl") ||
+ llvm::is_contained(Args, "--driver-mode=cl");
+
+ // Reverse scan, starting at the end or at the element before "--".
+ auto R = std::make_reverse_iterator(FlagsEnd);
+ auto E = Args.rend();
+ // Don't include Args[0] in the iteration; that's the executable, not
+ // an option.
+ if (E != R)
+ E--;
+ for (auto I = R; I != E; ++I) {
+ StringRef Arg = *I;
+ if (ClangCLMode) {
+ // Ignore arguments that are preceded by "-Xclang".
+ if ((I + 1) != E && I[1] == "-Xclang")
+ continue;
+ if (LastO.empty()) {
+ // With clang-cl, the output obj file can be specified with
+ // "/opath", "/o path", "/Fopath", and the dash counterparts.
+ // Also, clang-cl adds ".obj" extension if none is found.
+ if ((Arg == "-o" || Arg == "/o") && I != R)
+ LastO = I[-1]; // Next argument (reverse iterator)
+ else if (Arg.starts_with("/Fo") || Arg.starts_with("-Fo"))
+ LastO = Arg.drop_front(3).str();
+ else if (Arg.starts_with("/o") || Arg.starts_with("-o"))
+ LastO = Arg.drop_front(2).str();
+
+ if (!LastO.empty() && !llvm::sys::path::has_extension(LastO))
+ LastO.append(".obj");
+ }
+ }
+ if (Arg == "-resource-dir")
+ HasResourceDir = true;
+ }
+ }
+ tooling::CommandLineArguments AdjustedArgs(Args.begin(), FlagsEnd);
+ // The clang-cl driver passes "-o -" to the frontend. Inject the real
+ // file here to ensure "-MT" can be deduced if need be.
+ if (ClangCLMode && !LastO.empty()) {
+ AdjustedArgs.push_back("/clang:-o");
+ AdjustedArgs.push_back("/clang:" + LastO);
+ }
+
+ if (!HasResourceDir && ResourceDirRecipe == RDRK_InvokeCompiler) {
+ StringRef ResourceDir =
+ ResourceDirCache.findResourceDir(Args, ClangCLMode);
+ if (!ResourceDir.empty()) {
+ AdjustedArgs.push_back("-resource-dir");
+ AdjustedArgs.push_back(std::string(ResourceDir));
+ }
+ }
+ AdjustedArgs.insert(AdjustedArgs.end(), FlagsEnd, Args.end());
+ return AdjustedArgs;
+ });
+
+ SharedStream Errs(llvm::errs());
+
+ std::optional<llvm::raw_fd_ostream> FileOS;
+ llvm::raw_ostream &ThreadUnsafeDependencyOS = [&]() -> llvm::raw_ostream & {
+ if (OutputFileName == "-")
+ return llvm::outs();
+
+ if (OutputFileName == "/dev/null")
+ return llvm::nulls();
+
+ std::error_code EC;
+ FileOS.emplace(OutputFileName, EC);
+ if (EC) {
+ llvm::errs() << "Failed to open output file '" << OutputFileName
+ << "': " << llvm::errorCodeToError(EC) << '\n';
+ std::exit(1);
+ }
+ return *FileOS;
+ }();
+ SharedStream DependencyOS(ThreadUnsafeDependencyOS);
+
+ std::vector<tooling::CompileCommand> Inputs =
+ AdjustingCompilations->getAllCompileCommands();
+
+ std::atomic<bool> HadErrors(false);
+ std::optional<FullDeps> FD;
+ P1689Deps PD;
+
+ std::mutex Lock;
+ size_t Index = 0;
+ auto GetNextInputIndex = [&]() -> std::optional<size_t> {
+ std::unique_lock<std::mutex> LockGuard(Lock);
+ if (Index < Inputs.size())
+ return Index++;
+ return {};
+ };
+
+ if (Format == ScanningOutputFormat::Full)
+ FD.emplace(ModuleName.empty() ? Inputs.size() : 0);
+
+ auto ScanningTask = [&](DependencyScanningService &Service) {
+ DependencyScanningTool WorkerTool(Service);
+
+ llvm::DenseSet<ModuleID> AlreadySeenModules;
+ while (auto MaybeInputIndex = GetNextInputIndex()) {
+ size_t LocalIndex = *MaybeInputIndex;
+ const tooling::CompileCommand *Input = &Inputs[LocalIndex];
+ std::string Filename = std::move(Input->Filename);
+ std::string CWD = std::move(Input->Directory);
+
+ std::optional<StringRef> MaybeModuleName;
+ if (!ModuleName.empty())
+ MaybeModuleName = ModuleName;
+
+ std::string OutputDir(ModuleFilesDir);
+ if (OutputDir.empty())
+ OutputDir = getModuleCachePath(Input->CommandLine);
+ auto LookupOutput = [&](const ModuleID &MID, ModuleOutputKind MOK) {
+ return ::lookupModuleOutput(MID, MOK, OutputDir);
+ };
+
+ // Run the tool on it.
+ if (Format == ScanningOutputFormat::Make) {
+ auto MaybeFile = WorkerTool.getDependencyFile(Input->CommandLine, CWD);
+ if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS,
+ Errs))
+ HadErrors = true;
+ } else if (Format == ScanningOutputFormat::P1689) {
+ // It is useful to generate the make-format dependency output during
+ // the scanning for P1689. Otherwise the users need to scan again for
+ // it. We will generate the make-format dependency output if we find
+ // `-MF` in the command lines.
+ std::string MakeformatOutputPath;
+ std::string MakeformatOutput;
+
+ auto MaybeRule = WorkerTool.getP1689ModuleDependencyFile(
+ *Input, CWD, MakeformatOutput, MakeformatOutputPath);
+
+ if (handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs))
+ HadErrors = true;
+
+ if (!MakeformatOutputPath.empty() && !MakeformatOutput.empty() &&
+ !HadErrors) {
+ static std::mutex Lock;
+ // With compilation database, we may open different files
+ // concurrently or we may write the same file concurrently. So we
+ // use a map here to allow multiple compile commands to write to the
+ // same file. Also we need a lock here to avoid data race.
+ static llvm::StringMap<llvm::raw_fd_ostream> OSs;
+ std::unique_lock<std::mutex> LockGuard(Lock);
+
+ auto OSIter = OSs.find(MakeformatOutputPath);
+ if (OSIter == OSs.end()) {
+ std::error_code EC;
+ OSIter =
+ OSs.try_emplace(MakeformatOutputPath, MakeformatOutputPath, EC)
+ .first;
+ if (EC)
+ llvm::errs() << "Failed to open P1689 make format output file \""
+ << MakeformatOutputPath << "\" for " << EC.message()
+ << "\n";
+ }
+
+ SharedStream MakeformatOS(OSIter->second);
+ llvm::Expected<std::string> MaybeOutput(MakeformatOutput);
+ if (handleMakeDependencyToolResult(Filename, MaybeOutput,
+ MakeformatOS, Errs))
+ HadErrors = true;
+ }
+ } else if (MaybeModuleName) {
+ auto MaybeModuleDepsGraph = WorkerTool.getModuleDependencies(
+ *MaybeModuleName, Input->CommandLine, CWD, AlreadySeenModules,
+ LookupOutput);
+ if (handleModuleResult(*MaybeModuleName, MaybeModuleDepsGraph, *FD,
+ LocalIndex, DependencyOS, Errs))
+ HadErrors = true;
+ } else {
+ auto MaybeTUDeps = WorkerTool.getTranslationUnitDependencies(
+ Input->CommandLine, CWD, AlreadySeenModules, LookupOutput);
+ if (handleTranslationUnitResult(Filename, MaybeTUDeps, *FD, LocalIndex,
+ DependencyOS, Errs))
+ HadErrors = true;
+ }
+ }
+ };
+
+ DependencyScanningService Service(ScanMode, Format, OptimizeArgs,
+ EagerLoadModules);
+
+ llvm::Timer T;
+ T.startTimer();
+
+ if (Inputs.size() == 1) {
+ ScanningTask(Service);
+ } else {
+ llvm::DefaultThreadPool Pool(llvm::hardware_concurrency(NumThreads));
+
+ if (Verbose) {
+ llvm::outs() << "Running clang-scan-deps on " << Inputs.size()
+ << " files using " << Pool.getMaxConcurrency()
+ << " workers\n";
+ }
+
+ for (unsigned I = 0; I < Pool.getMaxConcurrency(); ++I)
+ Pool.async([ScanningTask, &Service]() { ScanningTask(Service); });
+
+ Pool.wait();
+ }
+
+ T.stopTimer();
+ if (PrintTiming)
+ llvm::errs() << llvm::format(
+ "clang-scan-deps timing: %0.2fs wall, %0.2fs process\n",
+ T.getTotalTime().getWallTime(), T.getTotalTime().getProcessTime());
+
+ if (RoundTripArgs)
+ if (FD && FD->roundTripCommands(llvm::errs()))
+ HadErrors = true;
+
+ if (Format == ScanningOutputFormat::Full)
+ FD->printFullOutput(ThreadUnsafeDependencyOS);
+ else if (Format == ScanningOutputFormat::P1689)
+ PD.printDependencies(ThreadUnsafeDependencyOS);
+
+ return HadErrors;
+}
diff --git a/contrib/llvm-project/clang/tools/clang-scan-deps/Opts.td b/contrib/llvm-project/clang/tools/clang-scan-deps/Opts.td
new file mode 100644
index 000000000000..4837ce6f070d
--- /dev/null
+++ b/contrib/llvm-project/clang/tools/clang-scan-deps/Opts.td
@@ -0,0 +1,42 @@
+include "llvm/Option/OptParser.td"
+
+class F<string name, string help> : Flag<["-"], name>, HelpText<help>;
+class Arg<string name, string help> : Separate<["-"], name>, HelpText<help>;
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["-", "--"], name #"=">, HelpText<help>;
+ def : Separate<["-", "--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def help : Flag<["--"], "help">, HelpText<"Display this help">;
+def version : Flag<["--"], "version">, HelpText<"Display the version">;
+
+def o : Arg<"o", "Destination of the primary output">;
+
+defm mode : Eq<"mode", "The preprocessing mode used to compute the dependencies">;
+
+defm format : Eq<"format", "The output format for the dependencies">;
+
+defm module_files_dir : Eq<"module-files-dir",
+ "The build directory for modules. Defaults to the value of '-fmodules-cache-path=' from command lines for implicit modules">;
+
+def optimize_args_EQ : CommaJoined<["-", "--"], "optimize-args=">, HelpText<"Which command-line arguments of modules to optimize">;
+def eager_load_pcm : F<"eager-load-pcm", "Load PCM files eagerly (instead of lazily on import)">;
+
+def j : Arg<"j", "Number of worker threads to use (default: use all concurrent threads)">;
+
+defm compilation_database : Eq<"compilation-database", "Compilation database">;
+defm module_name : Eq<"module-name", "the module of which the dependencies are to be computed">;
+defm dependency_target : Eq<"dependency-target", "The names of dependency targets for the dependency file">;
+
+def deprecated_driver_command : F<"deprecated-driver-command", "use a single driver command to build the tu (deprecated)">;
+
+defm resource_dir_recipe : Eq<"resource-dir-recipe", "How to produce missing '-resource-dir' argument">;
+
+def print_timing : F<"print-timing", "Print timing information">;
+
+def verbose : F<"v", "Use verbose output">;
+
+def round_trip_args : F<"round-trip-args", "verify that command-line arguments are canonical by parsing and re-serializing">;
+
+def DASH_DASH : Option<["--"], "", KIND_REMAINING_ARGS>;
diff --git a/contrib/llvm-project/clang/tools/driver/cc1_main.cpp b/contrib/llvm-project/clang/tools/driver/cc1_main.cpp
index 396d6ff529f3..f5e5fad36573 100644
--- a/contrib/llvm-project/clang/tools/driver/cc1_main.cpp
+++ b/contrib/llvm-project/clang/tools/driver/cc1_main.cpp
@@ -26,8 +26,11 @@
#include "clang/Frontend/Utils.h"
#include "clang/FrontendTool/Utils.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/LinkAllPasses.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
@@ -38,12 +41,14 @@
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
-#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/AArch64TargetParser.h"
+#include "llvm/TargetParser/ARMTargetParser.h"
+#include "llvm/TargetParser/RISCVISAInfo.h"
#include <cstdio>
#ifdef CLANG_HAVE_RLIMITS
@@ -57,7 +62,7 @@ using namespace llvm::opt;
// Main driver
//===----------------------------------------------------------------------===//
-static void LLVMErrorHandler(void *UserData, const std::string &Message,
+static void LLVMErrorHandler(void *UserData, const char *Message,
bool GenCrashDiag) {
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
@@ -74,64 +79,6 @@ static void LLVMErrorHandler(void *UserData, const std::string &Message,
}
#ifdef CLANG_HAVE_RLIMITS
-#if defined(__linux__) && defined(__PIE__)
-static size_t getCurrentStackAllocation() {
- // If we can't compute the current stack usage, allow for 512K of command
- // line arguments and environment.
- size_t Usage = 512 * 1024;
- if (FILE *StatFile = fopen("/proc/self/stat", "r")) {
- // We assume that the stack extends from its current address to the end of
- // the environment space. In reality, there is another string literal (the
- // program name) after the environment, but this is close enough (we only
- // need to be within 100K or so).
- unsigned long StackPtr, EnvEnd;
- // Disable silly GCC -Wformat warning that complains about length
- // modifiers on ignored format specifiers. We want to retain these
- // for documentation purposes even though they have no effect.
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat"
-#endif
- if (fscanf(StatFile,
- "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %*lu "
- "%*lu %*ld %*ld %*ld %*ld %*ld %*ld %*llu %*lu %*ld %*lu %*lu "
- "%*lu %*lu %lu %*lu %*lu %*lu %*lu %*lu %*llu %*lu %*lu %*d %*d "
- "%*u %*u %*llu %*lu %*ld %*lu %*lu %*lu %*lu %*lu %*lu %lu %*d",
- &StackPtr, &EnvEnd) == 2) {
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
- Usage = StackPtr < EnvEnd ? EnvEnd - StackPtr : StackPtr - EnvEnd;
- }
- fclose(StatFile);
- }
- return Usage;
-}
-
-#include <alloca.h>
-
-LLVM_ATTRIBUTE_NOINLINE
-static void ensureStackAddressSpace() {
- // Linux kernels prior to 4.1 will sometimes locate the heap of a PIE binary
- // relatively close to the stack (they are only guaranteed to be 128MiB
- // apart). This results in crashes if we happen to heap-allocate more than
- // 128MiB before we reach our stack high-water mark.
- //
- // To avoid these crashes, ensure that we have sufficient virtual memory
- // pages allocated before we start running.
- size_t Curr = getCurrentStackAllocation();
- const int kTargetStack = DesiredStackSize - 256 * 1024;
- if (Curr < kTargetStack) {
- volatile char *volatile Alloc =
- static_cast<volatile char *>(alloca(kTargetStack - Curr));
- Alloc[0] = 0;
- Alloc[kTargetStack - Curr - 1] = 0;
- }
-}
-#else
-static void ensureStackAddressSpace() {}
-#endif
-
/// Attempt to ensure that we have at least 8MiB of usable stack space.
static void ensureSufficientStack() {
struct rlimit rlim;
@@ -155,10 +102,6 @@ static void ensureSufficientStack() {
rlim.rlim_cur != DesiredStackSize)
return;
}
-
- // We should now have a stack of size at least DesiredStackSize. Ensure
- // that we can actually use that much, if necessary.
- ensureStackAddressSpace();
}
#else
static void ensureSufficientStack() {}
@@ -177,7 +120,91 @@ static int PrintSupportedCPUs(std::string TargetStr) {
// the target machine will handle the mcpu printing
llvm::TargetOptions Options;
std::unique_ptr<llvm::TargetMachine> TheTargetMachine(
- TheTarget->createTargetMachine(TargetStr, "", "+cpuhelp", Options, None));
+ TheTarget->createTargetMachine(TargetStr, "", "+cpuhelp", Options,
+ std::nullopt));
+ return 0;
+}
+
+static int PrintSupportedExtensions(std::string TargetStr) {
+ std::string Error;
+ const llvm::Target *TheTarget =
+ llvm::TargetRegistry::lookupTarget(TargetStr, Error);
+ if (!TheTarget) {
+ llvm::errs() << Error;
+ return 1;
+ }
+
+ llvm::TargetOptions Options;
+ std::unique_ptr<llvm::TargetMachine> TheTargetMachine(
+ TheTarget->createTargetMachine(TargetStr, "", "", Options, std::nullopt));
+ const llvm::Triple &MachineTriple = TheTargetMachine->getTargetTriple();
+ const llvm::MCSubtargetInfo *MCInfo = TheTargetMachine->getMCSubtargetInfo();
+ const llvm::ArrayRef<llvm::SubtargetFeatureKV> Features =
+ MCInfo->getAllProcessorFeatures();
+
+ llvm::StringMap<llvm::StringRef> DescMap;
+ for (const llvm::SubtargetFeatureKV &feature : Features)
+ DescMap.insert({feature.Key, feature.Desc});
+
+ if (MachineTriple.isRISCV())
+ llvm::RISCVISAInfo::printSupportedExtensions(DescMap);
+ else if (MachineTriple.isAArch64())
+ llvm::AArch64::PrintSupportedExtensions();
+ else if (MachineTriple.isARM())
+ llvm::ARM::PrintSupportedExtensions(DescMap);
+ else {
+ // The option was already checked in Driver::HandleImmediateArgs,
+ // so we do not expect to get here if we are not a supported architecture.
+ assert(0 && "Unhandled triple for --print-supported-extensions option.");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int PrintEnabledExtensions(const TargetOptions& TargetOpts) {
+ std::string Error;
+ const llvm::Target *TheTarget =
+ llvm::TargetRegistry::lookupTarget(TargetOpts.Triple, Error);
+ if (!TheTarget) {
+ llvm::errs() << Error;
+ return 1;
+ }
+
+ // Create a target machine using the input features, the triple information
+ // and a dummy instance of llvm::TargetOptions. Note that this is _not_ the
+ // same as the `clang::TargetOptions` instance we have access to here.
+ llvm::TargetOptions BackendOptions;
+ std::string FeaturesStr = llvm::join(TargetOpts.FeaturesAsWritten, ",");
+ std::unique_ptr<llvm::TargetMachine> TheTargetMachine(
+ TheTarget->createTargetMachine(TargetOpts.Triple, TargetOpts.CPU, FeaturesStr, BackendOptions, std::nullopt));
+ const llvm::Triple &MachineTriple = TheTargetMachine->getTargetTriple();
+ const llvm::MCSubtargetInfo *MCInfo = TheTargetMachine->getMCSubtargetInfo();
+
+ // Extract the feature names that are enabled for the given target.
+ // We do that by capturing the key from the set of SubtargetFeatureKV entries
+ // provided by MCSubtargetInfo, which match the '-target-feature' values.
+ const std::vector<llvm::SubtargetFeatureKV> Features =
+ MCInfo->getEnabledProcessorFeatures();
+ std::set<llvm::StringRef> EnabledFeatureNames;
+ for (const llvm::SubtargetFeatureKV &feature : Features)
+ EnabledFeatureNames.insert(feature.Key);
+
+ if (MachineTriple.isAArch64())
+ llvm::AArch64::printEnabledExtensions(EnabledFeatureNames);
+ else if (MachineTriple.isRISCV()) {
+ llvm::StringMap<llvm::StringRef> DescMap;
+ for (const llvm::SubtargetFeatureKV &feature : Features)
+ DescMap.insert({feature.Key, feature.Desc});
+ llvm::RISCVISAInfo::printEnabledExtensions(MachineTriple.isArch64Bit(),
+ EnabledFeatureNames, DescMap);
+ } else {
+ // The option was already checked in Driver::HandleImmediateArgs,
+ // so we do not expect to get here if we are not a supported architecture.
+ assert(0 && "Unhandled triple for --print-enabled-extensions option.");
+ return 1;
+ }
+
return 0;
}
@@ -212,14 +239,23 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
bool Success = CompilerInvocation::CreateFromArgs(Clang->getInvocation(),
Argv, Diags, Argv0);
- if (Clang->getFrontendOpts().TimeTrace) {
+ if (!Clang->getFrontendOpts().TimeTracePath.empty()) {
llvm::timeTraceProfilerInitialize(
- Clang->getFrontendOpts().TimeTraceGranularity, Argv0);
+ Clang->getFrontendOpts().TimeTraceGranularity, Argv0,
+ Clang->getFrontendOpts().TimeTraceVerbose);
}
// --print-supported-cpus takes priority over the actual compilation.
if (Clang->getFrontendOpts().PrintSupportedCPUs)
return PrintSupportedCPUs(Clang->getTargetOpts().Triple);
+ // --print-supported-extensions takes priority over the actual compilation.
+ if (Clang->getFrontendOpts().PrintSupportedExtensions)
+ return PrintSupportedExtensions(Clang->getTargetOpts().Triple);
+
+ // --print-enabled-extensions takes priority over the actual compilation.
+ if (Clang->getFrontendOpts().PrintEnabledExtensions)
+ return PrintEnabledExtensions(Clang->getTargetOpts());
+
// Infer the builtin include path if unspecified.
if (Clang->getHeaderSearchOpts().UseBuiltinIncludes &&
Clang->getHeaderSearchOpts().ResourceDir.empty())
@@ -237,8 +273,10 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
static_cast<void*>(&Clang->getDiagnostics()));
DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics());
- if (!Success)
+ if (!Success) {
+ Clang->getDiagnosticClient().finish();
return 1;
+ }
// Execute the frontend actions.
{
@@ -252,14 +290,24 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
llvm::TimerGroup::clearAll();
if (llvm::timeTraceProfilerEnabled()) {
- SmallString<128> Path(Clang->getFrontendOpts().OutputFile);
- llvm::sys::path::replace_extension(Path, "json");
+ // It is possible that the compiler instance doesn't own a file manager here
+ // if we're compiling a module unit. Since the file manager are owned by AST
+ // when we're compiling a module unit. So the file manager may be invalid
+ // here.
+ //
+ // It should be fine to create file manager here since the file system
+ // options are stored in the compiler invocation and we can recreate the VFS
+ // from the compiler invocation.
+ if (!Clang->hasFileManager())
+ Clang->createFileManager(createVFSFromCompilerInvocation(
+ Clang->getInvocation(), Clang->getDiagnostics()));
+
if (auto profilerOutput = Clang->createOutputFile(
- Path.str(), /*Binary=*/false, /*RemoveFileOnSignal=*/false,
+ Clang->getFrontendOpts().TimeTracePath, /*Binary=*/false,
+ /*RemoveFileOnSignal=*/false,
/*useTemporary=*/false)) {
llvm::timeTraceProfilerWrite(*profilerOutput);
- // FIXME(ibiryukov): make profilerOutput flush in destructor instead.
- profilerOutput->flush();
+ profilerOutput.reset();
llvm::timeTraceProfilerCleanup();
Clang->clearOutputFiles(false);
}
diff --git a/contrib/llvm-project/clang/tools/driver/cc1as_main.cpp b/contrib/llvm-project/clang/tools/driver/cc1as_main.cpp
index 086ce0ea7787..b661a43c88b0 100644
--- a/contrib/llvm-project/clang/tools/driver/cc1as_main.cpp
+++ b/contrib/llvm-project/clang/tools/driver/cc1as_main.cpp
@@ -19,8 +19,8 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/Triple.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmInfo.h"
@@ -36,6 +36,7 @@
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/TargetRegistry.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
@@ -43,17 +44,18 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
-#include "llvm/Support/Host.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
-#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Host.h"
+#include "llvm/TargetParser/Triple.h"
#include <memory>
+#include <optional>
#include <system_error>
using namespace clang;
using namespace clang::driver;
@@ -87,16 +89,23 @@ struct AssemblerInvocation {
/// @{
std::vector<std::string> IncludePaths;
+ LLVM_PREFERRED_TYPE(bool)
unsigned NoInitialTextSection : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned SaveTemporaryLabels : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned GenDwarfForAssembly : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned RelaxELFRelocations : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned SSE2AVX : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned Dwarf64 : 1;
unsigned DwarfVersion;
std::string DwarfDebugFlags;
std::string DwarfDebugProducer;
std::string DebugCompilationDir;
- std::map<const std::string, const std::string> DebugPrefixMap;
+ llvm::SmallVector<std::pair<std::string, std::string>, 0> DebugPrefixMap;
llvm::DebugCompressionType CompressDebugSections =
llvm::DebugCompressionType::None;
std::string MainFileName;
@@ -115,7 +124,9 @@ struct AssemblerInvocation {
FT_Obj ///< Object file output.
};
FileType OutputType;
+ LLVM_PREFERRED_TYPE(bool)
unsigned ShowHelp : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned ShowVersion : 1;
/// @}
@@ -123,20 +134,41 @@ struct AssemblerInvocation {
/// @{
unsigned OutputAsmVariant;
+ LLVM_PREFERRED_TYPE(bool)
unsigned ShowEncoding : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned ShowInst : 1;
/// @}
/// @name Assembler Options
/// @{
+ LLVM_PREFERRED_TYPE(bool)
unsigned RelaxAll : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned NoExecStack : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned FatalWarnings : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned NoWarn : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned NoTypeCheck : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned IncrementalLinkerCompatible : 1;
+ LLVM_PREFERRED_TYPE(bool)
unsigned EmbedBitcode : 1;
+ /// Whether to emit DWARF unwind info.
+ EmitDwarfUnwindType EmitDwarfUnwind;
+
+ // Whether to emit compact-unwind for non-canonical entries.
+ // Note: maybe overriden by other constraints.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned EmitCompactUnwindNonCanonical : 1;
+
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Crel : 1;
+
/// The name of the relocation model to use.
std::string RelocationModel;
@@ -144,6 +176,16 @@ struct AssemblerInvocation {
/// otherwise.
std::string TargetABI;
+ /// Darwin target variant triple, the variant of the deployment target
+ /// for which the code is being compiled.
+ std::optional<llvm::Triple> DarwinTargetVariantTriple;
+
+ /// The version of the darwin target variant SDK which was used during the
+ /// compilation
+ llvm::VersionTuple DarwinTargetVariantSDKVersion;
+
+ /// The name of a file to use with \c .secure_log_unique directives.
+ std::string AsSecureLogFile;
/// @}
public:
@@ -157,13 +199,18 @@ public:
ShowInst = 0;
ShowEncoding = 0;
RelaxAll = 0;
+ SSE2AVX = 0;
NoExecStack = 0;
FatalWarnings = 0;
NoWarn = 0;
+ NoTypeCheck = 0;
IncrementalLinkerCompatible = 0;
Dwarf64 = 0;
DwarfVersion = 0;
EmbedBitcode = 0;
+ EmitDwarfUnwind = EmitDwarfUnwindType::Default;
+ EmitCompactUnwindNonCanonical = false;
+ Crel = false;
}
static bool CreateFromArgs(AssemblerInvocation &Res,
@@ -181,10 +228,10 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
// Parse the arguments.
const OptTable &OptTbl = getDriverOptTable();
- const unsigned IncludedFlagsBitmask = options::CC1AsOption;
+ llvm::opt::Visibility VisibilityMask(options::CC1AsOption);
unsigned MissingArgIndex, MissingArgCount;
- InputArgList Args = OptTbl.ParseArgs(Argv, MissingArgIndex, MissingArgCount,
- IncludedFlagsBitmask);
+ InputArgList Args =
+ OptTbl.ParseArgs(Argv, MissingArgIndex, MissingArgCount, VisibilityMask);
// Check for missing argument error.
if (MissingArgCount) {
@@ -197,7 +244,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
for (const Arg *A : Args.filtered(OPT_UNKNOWN)) {
auto ArgString = A->getAsString(Args);
std::string Nearest;
- if (OptTbl.findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1)
+ if (OptTbl.findNearest(ArgString, Nearest, VisibilityMask) > 1)
Diags.Report(diag::err_drv_unknown_argument) << ArgString;
else
Diags.Report(diag::err_drv_unknown_argument_with_suggestion)
@@ -209,6 +256,17 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
// Target Options
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
+ if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
+ Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
+ if (Arg *A = Args.getLastArg(OPT_darwin_target_variant_sdk_version_EQ)) {
+ VersionTuple Version;
+ if (Version.tryParse(A->getValue()))
+ Diags.Report(diag::err_drv_invalid_value)
+ << A->getAsString(Args) << A->getValue();
+ else
+ Opts.DarwinTargetVariantSDKVersion = Version;
+ }
+
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
Opts.Features = Args.getAllArgValues(OPT_target_feature);
@@ -227,12 +285,13 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.CompressDebugSections =
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
.Case("none", llvm::DebugCompressionType::None)
- .Case("zlib", llvm::DebugCompressionType::Z)
- .Case("zlib-gnu", llvm::DebugCompressionType::GNU)
+ .Case("zlib", llvm::DebugCompressionType::Zlib)
+ .Case("zstd", llvm::DebugCompressionType::Zstd)
.Default(llvm::DebugCompressionType::None);
}
- Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
+ Opts.RelaxELFRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
+ Opts.SSE2AVX = Args.hasArg(OPT_msse2avx);
if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32))
Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64);
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
@@ -247,8 +306,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
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)});
+ Opts.DebugPrefixMap.emplace_back(Split.first, Split.second);
}
// Frontend Options
@@ -295,6 +353,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn);
+ Opts.NoTypeCheck = Args.hasArg(OPT_mno_type_check);
Opts.RelocationModel =
std::string(Args.getLastArgValue(OPT_mrelocation_model, "pic"));
Opts.TargetABI = std::string(Args.getLastArgValue(OPT_target_abi));
@@ -312,6 +371,20 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
.Default(0);
}
+ if (auto *A = Args.getLastArg(OPT_femit_dwarf_unwind_EQ)) {
+ Opts.EmitDwarfUnwind =
+ llvm::StringSwitch<EmitDwarfUnwindType>(A->getValue())
+ .Case("always", EmitDwarfUnwindType::Always)
+ .Case("no-compact-unwind", EmitDwarfUnwindType::NoCompactUnwind)
+ .Case("default", EmitDwarfUnwindType::Default);
+ }
+
+ Opts.EmitCompactUnwindNonCanonical =
+ Args.hasArg(OPT_femit_compact_unwind_non_canonical);
+ Opts.Crel = Args.hasArg(OPT_crel);
+
+ Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
+
return Success;
}
@@ -345,8 +418,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
if (std::error_code EC = Buffer.getError()) {
- Error = EC.message();
- return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
+ return Diags.Report(diag::err_fe_error_reading)
+ << Opts.InputFile << EC.message();
}
SourceMgr SrcMgr;
@@ -362,15 +435,23 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
assert(MRI && "Unable to create target register info!");
MCTargetOptions MCOptions;
+ MCOptions.MCRelaxAll = Opts.RelaxAll;
+ MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
+ MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
+ MCOptions.MCSaveTempLabels = Opts.SaveTemporaryLabels;
+ MCOptions.Crel = Opts.Crel;
+ MCOptions.X86RelaxRelocations = Opts.RelaxELFRelocations;
+ MCOptions.X86Sse2Avx = Opts.SSE2AVX;
+ MCOptions.CompressDebugSections = Opts.CompressDebugSections;
+ MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
+
std::unique_ptr<MCAsmInfo> MAI(
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
assert(MAI && "Unable to create target asm info!");
// Ensure MCAsmInfo initialization occurs before any use, otherwise sections
// may be created with a combination of default and explicit settings.
- MAI->setCompressDebugSections(Opts.CompressDebugSections);
- MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations);
bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
if (Opts.OutputPath.empty())
@@ -408,10 +489,12 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
// MCObjectFileInfo needs a MCContext reference in order to initialize itself.
std::unique_ptr<MCObjectFileInfo> MOFI(
TheTarget->createMCObjectFileInfo(Ctx, PIC));
+ if (Opts.DarwinTargetVariantTriple)
+ MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple);
+ if (!Opts.DarwinTargetVariantSDKVersion.empty())
+ MOFI->setDarwinTargetVariantSDKVersion(Opts.DarwinTargetVariantSDKVersion);
Ctx.setObjectFileInfo(MOFI.get());
- if (Opts.SaveTemporaryLabels)
- Ctx.setAllowTemporaryLabels(false);
if (Opts.GenDwarfForAssembly)
Ctx.setGenDwarfForAssembly(true);
if (!Opts.DwarfDebugFlags.empty())
@@ -447,6 +530,10 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
MCOptions.MCNoWarn = Opts.NoWarn;
MCOptions.MCFatalWarnings = Opts.FatalWarnings;
+ MCOptions.MCNoTypeCheck = Opts.NoTypeCheck;
+ MCOptions.ShowMCInst = Opts.ShowInst;
+ MCOptions.AsmVerbose = true;
+ MCOptions.MCUseDwarfDirectory = MCTargetOptions::EnableDwarfDirectory;
MCOptions.ABIName = Opts.TargetABI;
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
@@ -456,15 +543,13 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
std::unique_ptr<MCCodeEmitter> CE;
if (Opts.ShowEncoding)
- CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
+ CE.reset(TheTarget->createMCCodeEmitter(*MCII, Ctx));
std::unique_ptr<MCAsmBackend> MAB(
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
auto FOut = std::make_unique<formatted_raw_ostream>(*Out);
- Str.reset(TheTarget->createAsmStreamer(
- Ctx, std::move(FOut), /*asmverbose*/ true,
- /*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB),
- Opts.ShowInst));
+ Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), IP,
+ std::move(CE), std::move(MAB)));
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
Str.reset(createNullStreamer(Ctx));
} else {
@@ -476,7 +561,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
}
std::unique_ptr<MCCodeEmitter> CE(
- TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
+ TheTarget->createMCCodeEmitter(*MCII, Ctx));
std::unique_ptr<MCAsmBackend> MAB(
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
assert(MAB && "Unable to create asm backend!");
@@ -487,10 +572,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
Triple T(Opts.Triple);
Str.reset(TheTarget->createMCObjectStreamer(
- T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
- Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
- /*DWARFMustBeAtTheEnd*/ true));
- Str.get()->InitSections(Opts.NoExecStack);
+ T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI));
+ Str.get()->initSections(Opts.NoExecStack, *STI);
}
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
@@ -498,13 +581,10 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
if (Opts.EmbedBitcode && Ctx.getObjectFileType() == MCContext::IsMachO) {
MCSection *AsmLabel = Ctx.getMachOSection(
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
- Str.get()->SwitchSection(AsmLabel);
+ Str.get()->switchSection(AsmLabel);
Str.get()->emitZeros(1);
}
- // Assembly to object compilation should leverage assembly info.
- Str->setUseAssemblerInfoForParsing(true);
-
bool Failed = false;
std::unique_ptr<MCAsmParser> Parser(
@@ -550,7 +630,7 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts,
return Failed;
}
-static void LLVMErrorHandler(void *UserData, const std::string &Message,
+static void LLVMErrorHandler(void *UserData, const char *Message,
bool GenCrashDiag) {
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
@@ -587,9 +667,10 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
if (Asm.ShowHelp) {
getDriverOptTable().printHelp(
llvm::outs(), "clang -cc1as [options] file...",
- "Clang Integrated Assembler",
- /*Include=*/driver::options::CC1AsOption, /*Exclude=*/0,
- /*ShowAllAliases=*/false);
+ "Clang Integrated Assembler", /*ShowHidden=*/false,
+ /*ShowAllAliases=*/false,
+ llvm::opt::Visibility(driver::options::CC1AsOption));
+
return 0;
}
diff --git a/contrib/llvm-project/clang/tools/driver/cc1gen_reproducer_main.cpp b/contrib/llvm-project/clang/tools/driver/cc1gen_reproducer_main.cpp
index 89b7227fdb17..e97fa3d27756 100644
--- a/contrib/llvm-project/clang/tools/driver/cc1gen_reproducer_main.cpp
+++ b/contrib/llvm-project/clang/tools/driver/cc1gen_reproducer_main.cpp
@@ -18,11 +18,13 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/Host.h"
+#include "llvm/Support/LLVMDriver.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Host.h"
+#include <optional>
using namespace clang;
@@ -108,9 +110,10 @@ static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) {
}
/// Generates a reproducer for a set of arguments from a specific invocation.
-static llvm::Optional<driver::Driver::CompilationDiagnosticReport>
+static std::optional<driver::Driver::CompilationDiagnosticReport>
generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
- const ClangInvocationInfo &Info) {
+ const ClangInvocationInfo &Info,
+ const llvm::ToolContext &ToolContext) {
using namespace driver;
auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);
@@ -119,8 +122,11 @@ generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());
ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
- Driver TheDriver(Argv[0], llvm::sys::getDefaultTargetTriple(), Diags);
+ Driver TheDriver(ToolContext.Path, llvm::sys::getDefaultTargetTriple(),
+ Diags);
TheDriver.setTargetAndMode(TargetAndMode);
+ if (ToolContext.NeedsPrependArg)
+ TheDriver.setPrependArg(ToolContext.PrependArg);
std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));
if (C && !C->containsError()) {
@@ -134,7 +140,7 @@ generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
}
}
- return None;
+ return std::nullopt;
}
std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);
@@ -154,7 +160,8 @@ static void printReproducerInformation(
}
int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,
- void *MainAddr) {
+ void *MainAddr,
+ const llvm::ToolContext &ToolContext) {
if (Argv.size() < 1) {
llvm::errs() << "error: missing invocation file\n";
return 1;
@@ -180,8 +187,9 @@ int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,
DriverArgs.push_back(Arg.c_str());
std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);
DriverArgs[0] = Path.c_str();
- llvm::Optional<driver::Driver::CompilationDiagnosticReport> Report =
- generateReproducerForInvocationArguments(DriverArgs, InvocationInfo);
+ std::optional<driver::Driver::CompilationDiagnosticReport> Report =
+ generateReproducerForInvocationArguments(DriverArgs, InvocationInfo,
+ ToolContext);
// Emit the information about the reproduce files to stdout.
int Result = 1;
diff --git a/contrib/llvm-project/clang/tools/driver/driver.cpp b/contrib/llvm-project/clang/tools/driver/driver.cpp
index 5a453429e79b..83b5bbb71f52 100644
--- a/contrib/llvm-project/clang/tools/driver/driver.cpp
+++ b/contrib/llvm-project/clang/tools/driver/driver.cpp
@@ -13,6 +13,7 @@
#include "clang/Driver/Driver.h"
#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/HeaderInclude.h"
#include "clang/Basic/Stack.h"
#include "clang/Config/config.h"
#include "clang/Driver/Compilation.h"
@@ -27,6 +28,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Option/Option.h"
@@ -35,19 +37,19 @@
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/Host.h"
-#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/LLVMDriver.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
-#include "llvm/Support/Regex.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Host.h"
#include <memory>
+#include <optional>
#include <set>
#include <system_error>
using namespace clang;
@@ -62,7 +64,7 @@ std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) {
if (llvm::ErrorOr<std::string> P =
llvm::sys::findProgramByName(ExecutablePath))
ExecutablePath = *P;
- return std::string(ExecutablePath.str());
+ return std::string(ExecutablePath);
}
// This just needs to be some symbol in the binary; C++ doesn't
@@ -71,136 +73,8 @@ std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) {
return llvm::sys::fs::getMainExecutable(Argv0, P);
}
-static const char *GetStableCStr(std::set<std::string> &SavedStrings,
- StringRef S) {
- return SavedStrings.insert(std::string(S)).first->c_str();
-}
-
-/// ApplyQAOverride - Apply a list of edits to the input argument lists.
-///
-/// The input string is a space separate list of edits to perform,
-/// they are applied in order to the input argument lists. Edits
-/// should be one of the following forms:
-///
-/// '#': Silence information about the changes to the command line arguments.
-///
-/// '^': Add FOO as a new argument at the beginning of the command line.
-///
-/// '+': Add FOO as a new argument at the end of the command line.
-///
-/// 's/XXX/YYY/': Substitute the regular expression XXX with YYY in the command
-/// line.
-///
-/// 'xOPTION': Removes all instances of the literal argument OPTION.
-///
-/// 'XOPTION': Removes all instances of the literal argument OPTION,
-/// and the following argument.
-///
-/// 'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox'
-/// at the end of the command line.
-///
-/// \param OS - The stream to write edit information to.
-/// \param Args - The vector of command line arguments.
-/// \param Edit - The override command to perform.
-/// \param SavedStrings - Set to use for storing string representations.
-static void ApplyOneQAOverride(raw_ostream &OS,
- SmallVectorImpl<const char*> &Args,
- StringRef Edit,
- std::set<std::string> &SavedStrings) {
- // This does not need to be efficient.
-
- if (Edit[0] == '^') {
- const char *Str =
- GetStableCStr(SavedStrings, Edit.substr(1));
- OS << "### Adding argument " << Str << " at beginning\n";
- Args.insert(Args.begin() + 1, Str);
- } else if (Edit[0] == '+') {
- const char *Str =
- GetStableCStr(SavedStrings, Edit.substr(1));
- OS << "### Adding argument " << Str << " at end\n";
- Args.push_back(Str);
- } else if (Edit[0] == 's' && Edit[1] == '/' && Edit.endswith("/") &&
- Edit.slice(2, Edit.size()-1).find('/') != StringRef::npos) {
- StringRef MatchPattern = Edit.substr(2).split('/').first;
- StringRef ReplPattern = Edit.substr(2).split('/').second;
- ReplPattern = ReplPattern.slice(0, ReplPattern.size()-1);
-
- for (unsigned i = 1, e = Args.size(); i != e; ++i) {
- // Ignore end-of-line response file markers
- if (Args[i] == nullptr)
- continue;
- std::string Repl = llvm::Regex(MatchPattern).sub(ReplPattern, Args[i]);
-
- if (Repl != Args[i]) {
- OS << "### Replacing '" << Args[i] << "' with '" << Repl << "'\n";
- Args[i] = GetStableCStr(SavedStrings, Repl);
- }
- }
- } else if (Edit[0] == 'x' || Edit[0] == 'X') {
- auto Option = Edit.substr(1);
- for (unsigned i = 1; i < Args.size();) {
- if (Option == Args[i]) {
- OS << "### Deleting argument " << Args[i] << '\n';
- Args.erase(Args.begin() + i);
- if (Edit[0] == 'X') {
- if (i < Args.size()) {
- OS << "### Deleting argument " << Args[i] << '\n';
- Args.erase(Args.begin() + i);
- } else
- OS << "### Invalid X edit, end of command line!\n";
- }
- } else
- ++i;
- }
- } else if (Edit[0] == 'O') {
- for (unsigned i = 1; i < Args.size();) {
- const char *A = Args[i];
- // Ignore end-of-line response file markers
- if (A == nullptr)
- continue;
- if (A[0] == '-' && A[1] == 'O' &&
- (A[2] == '\0' ||
- (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' ||
- ('0' <= A[2] && A[2] <= '9'))))) {
- OS << "### Deleting argument " << Args[i] << '\n';
- Args.erase(Args.begin() + i);
- } else
- ++i;
- }
- OS << "### Adding argument " << Edit << " at end\n";
- Args.push_back(GetStableCStr(SavedStrings, '-' + Edit.str()));
- } else {
- OS << "### Unrecognized edit: " << Edit << "\n";
- }
-}
-
-/// ApplyQAOverride - Apply a comma separate list of edits to the
-/// input argument lists. See ApplyOneQAOverride.
-static void ApplyQAOverride(SmallVectorImpl<const char*> &Args,
- const char *OverrideStr,
- std::set<std::string> &SavedStrings) {
- raw_ostream *OS = &llvm::errs();
-
- if (OverrideStr[0] == '#') {
- ++OverrideStr;
- OS = &llvm::nulls();
- }
-
- *OS << "### CCC_OVERRIDE_OPTIONS: " << OverrideStr << "\n";
-
- // This does not need to be efficient.
-
- const char *S = OverrideStr;
- while (*S) {
- const char *End = ::strchr(S, ' ');
- if (!End)
- End = S + strlen(S);
- if (End != S)
- ApplyOneQAOverride(*OS, Args, std::string(S, End), SavedStrings);
- S = End;
- if (*S != '\0')
- ++S;
- }
+static const char *GetStableCStr(llvm::StringSet<> &SavedStrings, StringRef S) {
+ return SavedStrings.insert(S).first->getKeyData();
}
extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0,
@@ -208,11 +82,12 @@ extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0,
extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
void *MainAddr);
extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv,
- const char *Argv0, void *MainAddr);
+ const char *Argv0, void *MainAddr,
+ const llvm::ToolContext &);
static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
SmallVectorImpl<const char *> &ArgVector,
- std::set<std::string> &SavedStrings) {
+ llvm::StringSet<> &SavedStrings) {
// Put target and mode arguments at the start of argument list so that
// arguments specified in command line could override them. Avoid putting
// them at index 0, as an option like '-cc1' must remain the first.
@@ -243,29 +118,71 @@ static void getCLEnvVarOptions(std::string &EnvValue, llvm::StringSaver &Saver,
*NumberSignPtr = '=';
}
-static void SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) {
- auto CheckEnvVar = [](const char *EnvOptSet, const char *EnvOptFile,
- std::string &OptFile) {
- bool OptSet = !!::getenv(EnvOptSet);
- if (OptSet) {
- if (const char *Var = ::getenv(EnvOptFile))
- OptFile = Var;
- }
- return OptSet;
- };
+template <class T>
+static T checkEnvVar(const char *EnvOptSet, const char *EnvOptFile,
+ std::string &OptFile) {
+ const char *Str = ::getenv(EnvOptSet);
+ if (!Str)
+ return T{};
+
+ T OptVal = Str;
+ if (const char *Var = ::getenv(EnvOptFile))
+ OptFile = Var;
+ return OptVal;
+}
+static bool SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) {
TheDriver.CCPrintOptions =
- CheckEnvVar("CC_PRINT_OPTIONS", "CC_PRINT_OPTIONS_FILE",
- TheDriver.CCPrintOptionsFilename);
- TheDriver.CCPrintHeaders =
- CheckEnvVar("CC_PRINT_HEADERS", "CC_PRINT_HEADERS_FILE",
- TheDriver.CCPrintHeadersFilename);
+ checkEnvVar<bool>("CC_PRINT_OPTIONS", "CC_PRINT_OPTIONS_FILE",
+ TheDriver.CCPrintOptionsFilename);
+ if (checkEnvVar<bool>("CC_PRINT_HEADERS", "CC_PRINT_HEADERS_FILE",
+ TheDriver.CCPrintHeadersFilename)) {
+ TheDriver.CCPrintHeadersFormat = HIFMT_Textual;
+ TheDriver.CCPrintHeadersFiltering = HIFIL_None;
+ } else {
+ std::string EnvVar = checkEnvVar<std::string>(
+ "CC_PRINT_HEADERS_FORMAT", "CC_PRINT_HEADERS_FILE",
+ TheDriver.CCPrintHeadersFilename);
+ if (!EnvVar.empty()) {
+ TheDriver.CCPrintHeadersFormat =
+ stringToHeaderIncludeFormatKind(EnvVar.c_str());
+ if (!TheDriver.CCPrintHeadersFormat) {
+ TheDriver.Diag(clang::diag::err_drv_print_header_env_var)
+ << 0 << EnvVar;
+ return false;
+ }
+
+ const char *FilteringStr = ::getenv("CC_PRINT_HEADERS_FILTERING");
+ HeaderIncludeFilteringKind Filtering;
+ if (!stringToHeaderIncludeFiltering(FilteringStr, Filtering)) {
+ TheDriver.Diag(clang::diag::err_drv_print_header_env_var)
+ << 1 << FilteringStr;
+ return false;
+ }
+
+ if ((TheDriver.CCPrintHeadersFormat == HIFMT_Textual &&
+ Filtering != HIFIL_None) ||
+ (TheDriver.CCPrintHeadersFormat == HIFMT_JSON &&
+ Filtering != HIFIL_Only_Direct_System)) {
+ TheDriver.Diag(clang::diag::err_drv_print_header_env_var_combination)
+ << EnvVar << FilteringStr;
+ return false;
+ }
+ TheDriver.CCPrintHeadersFiltering = Filtering;
+ }
+ }
+
TheDriver.CCLogDiagnostics =
- CheckEnvVar("CC_LOG_DIAGNOSTICS", "CC_LOG_DIAGNOSTICS_FILE",
- TheDriver.CCLogDiagnosticsFilename);
+ checkEnvVar<bool>("CC_LOG_DIAGNOSTICS", "CC_LOG_DIAGNOSTICS_FILE",
+ TheDriver.CCLogDiagnosticsFilename);
TheDriver.CCPrintProcessStats =
- CheckEnvVar("CC_PRINT_PROC_STAT", "CC_PRINT_PROC_STAT_FILE",
- TheDriver.CCPrintStatReportFilename);
+ checkEnvVar<bool>("CC_PRINT_PROC_STAT", "CC_PRINT_PROC_STAT_FILE",
+ TheDriver.CCPrintStatReportFilename);
+ TheDriver.CCPrintInternalStats =
+ checkEnvVar<bool>("CC_PRINT_INTERNAL_STAT", "CC_PRINT_INTERNAL_STAT_FILE",
+ TheDriver.CCPrintInternalStatReportFilename);
+
+ return true;
}
static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient,
@@ -278,50 +195,8 @@ static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient,
DiagClient->setPrefix(std::string(ExeBasename));
}
-// This lets us create the DiagnosticsEngine with a properly-filled-out
-// DiagnosticOptions instance.
-static DiagnosticOptions *
-CreateAndPopulateDiagOpts(ArrayRef<const char *> argv, bool &UseNewCC1Process) {
- auto *DiagOpts = new DiagnosticOptions;
- unsigned MissingArgIndex, MissingArgCount;
- InputArgList Args = getDriverOptTable().ParseArgs(
- argv.slice(1), MissingArgIndex, MissingArgCount);
- // We ignore MissingArgCount and the return value of ParseDiagnosticArgs.
- // Any errors that would be diagnosed here will also be diagnosed later,
- // when the DiagnosticsEngine actually exists.
- (void)ParseDiagnosticArgs(*DiagOpts, Args);
-
- UseNewCC1Process =
- Args.hasFlag(clang::driver::options::OPT_fno_integrated_cc1,
- clang::driver::options::OPT_fintegrated_cc1,
- /*Default=*/CLANG_SPAWN_CC1);
-
- return DiagOpts;
-}
-
-static void SetInstallDir(SmallVectorImpl<const char *> &argv,
- Driver &TheDriver, bool CanonicalPrefixes) {
- // Attempt to find the original path used to invoke the driver, to determine
- // the installed path. We do this manually, because we want to support that
- // path being a symlink.
- SmallString<128> InstalledPath(argv[0]);
-
- // Do a PATH lookup, if there are no directory components.
- if (llvm::sys::path::filename(InstalledPath) == InstalledPath)
- if (llvm::ErrorOr<std::string> Tmp = llvm::sys::findProgramByName(
- llvm::sys::path::filename(InstalledPath.str())))
- InstalledPath = *Tmp;
-
- // FIXME: We don't actually canonicalize this, we just make it absolute.
- if (CanonicalPrefixes)
- llvm::sys::fs::make_absolute(InstalledPath);
-
- StringRef InstalledPathParent(llvm::sys::path::parent_path(InstalledPath));
- if (llvm::sys::fs::exists(InstalledPathParent))
- TheDriver.setInstalledDir(InstalledPathParent);
-}
-
-static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV) {
+static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV,
+ const llvm::ToolContext &ToolContext) {
// If we call the cc1 tool from the clangDriver library (through
// Driver::CC1Main), we need to clean up the options usage count. The options
// are currently global, and they might have been used previously by the
@@ -329,28 +204,29 @@ static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV) {
llvm::cl::ResetAllOptionOccurrences();
llvm::BumpPtrAllocator A;
- llvm::StringSaver Saver(A);
- llvm::cl::ExpandResponseFiles(Saver, &llvm::cl::TokenizeGNUCommandLine, ArgV,
- /*MarkEOLs=*/false);
+ llvm::cl::ExpansionContext ECtx(A, llvm::cl::TokenizeGNUCommandLine);
+ if (llvm::Error Err = ECtx.expandResponseFiles(ArgV)) {
+ llvm::errs() << toString(std::move(Err)) << '\n';
+ return 1;
+ }
StringRef Tool = ArgV[1];
void *GetExecutablePathVP = (void *)(intptr_t)GetExecutablePath;
if (Tool == "-cc1")
- return cc1_main(makeArrayRef(ArgV).slice(1), ArgV[0], GetExecutablePathVP);
+ return cc1_main(ArrayRef(ArgV).slice(1), ArgV[0], GetExecutablePathVP);
if (Tool == "-cc1as")
- return cc1as_main(makeArrayRef(ArgV).slice(2), ArgV[0],
- GetExecutablePathVP);
+ return cc1as_main(ArrayRef(ArgV).slice(2), ArgV[0], GetExecutablePathVP);
if (Tool == "-cc1gen-reproducer")
- return cc1gen_reproducer_main(makeArrayRef(ArgV).slice(2), ArgV[0],
- GetExecutablePathVP);
+ return cc1gen_reproducer_main(ArrayRef(ArgV).slice(2), ArgV[0],
+ GetExecutablePathVP, ToolContext);
// Reject unknown tools.
- llvm::errs() << "error: unknown integrated tool '" << Tool << "'. "
- << "Valid tools include '-cc1' and '-cc1as'.\n";
+ llvm::errs()
+ << "error: unknown integrated tool '" << Tool << "'. "
+ << "Valid tools include '-cc1', '-cc1as' and '-cc1gen-reproducer'.\n";
return 1;
}
-int main(int Argc, const char **Argv) {
+int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) {
noteBottomOfStack();
- llvm::InitLLVM X(Argc, Argv);
llvm::setBugReportMsg("PLEASE submit a bug report to " BUG_REPORT_URL
" and include the crash backtrace, preprocessed "
"source, and associated run script.\n");
@@ -364,51 +240,21 @@ int main(int Argc, const char **Argv) {
llvm::BumpPtrAllocator A;
llvm::StringSaver Saver(A);
- // Parse response files using the GNU syntax, unless we're in CL mode. There
- // are two ways to put clang in CL compatibility mode: Args[0] is either
- // clang-cl or cl, or --driver-mode=cl is on the command line. The normal
- // command line parsing can't happen until after response file parsing, so we
- // have to manually search for a --driver-mode=cl argument the hard way.
- // Finally, our -cc1 tools don't care which tokenization mode we use because
- // response files written by clang will tokenize the same way in either mode.
+ const char *ProgName =
+ ToolContext.NeedsPrependArg ? ToolContext.PrependArg : ToolContext.Path;
+
bool ClangCLMode =
- IsClangCL(getDriverMode(Args[0], llvm::makeArrayRef(Args).slice(1)));
- enum { Default, POSIX, Windows } RSPQuoting = Default;
- for (const char *F : Args) {
- if (strcmp(F, "--rsp-quoting=posix") == 0)
- RSPQuoting = POSIX;
- else if (strcmp(F, "--rsp-quoting=windows") == 0)
- RSPQuoting = Windows;
- }
+ IsClangCL(getDriverMode(ProgName, llvm::ArrayRef(Args).slice(1)));
- // Determines whether we want nullptr markers in Args to indicate response
- // files end-of-lines. We only use this for the /LINK driver argument with
- // clang-cl.exe on Windows.
- bool MarkEOLs = ClangCLMode;
-
- llvm::cl::TokenizerCallback Tokenizer;
- if (RSPQuoting == Windows || (RSPQuoting == Default && ClangCLMode))
- Tokenizer = &llvm::cl::TokenizeWindowsCommandLine;
- else
- Tokenizer = &llvm::cl::TokenizeGNUCommandLine;
-
- if (MarkEOLs && Args.size() > 1 && StringRef(Args[1]).startswith("-cc1"))
- MarkEOLs = false;
- llvm::cl::ExpandResponseFiles(Saver, Tokenizer, Args, MarkEOLs);
-
- // Handle -cc1 integrated tools, even if -cc1 was expanded from a response
- // file.
- auto FirstArg = std::find_if(Args.begin() + 1, Args.end(),
- [](const char *A) { return A != nullptr; });
- if (FirstArg != Args.end() && StringRef(*FirstArg).startswith("-cc1")) {
- // If -cc1 came from a response file, remove the EOL sentinels.
- if (MarkEOLs) {
- auto newEnd = std::remove(Args.begin(), Args.end(), nullptr);
- Args.resize(newEnd - Args.begin());
- }
- return ExecuteCC1Tool(Args);
+ if (llvm::Error Err = expandResponseFiles(Args, ClangCLMode, A)) {
+ llvm::errs() << toString(std::move(Err)) << '\n';
+ return 1;
}
+ // Handle -cc1 integrated tools.
+ if (Args.size() >= 2 && StringRef(Args[1]).starts_with("-cc1"))
+ return ExecuteCC1Tool(Args, ToolContext);
+
// Handle options that need handling before the real command line parsing in
// Driver::BuildCompilation()
bool CanonicalPrefixes = true;
@@ -416,57 +262,63 @@ int main(int Argc, const char **Argv) {
// Skip end-of-line response file markers
if (Args[i] == nullptr)
continue;
- if (StringRef(Args[i]) == "-no-canonical-prefixes") {
+ if (StringRef(Args[i]) == "-canonical-prefixes")
+ CanonicalPrefixes = true;
+ else if (StringRef(Args[i]) == "-no-canonical-prefixes")
CanonicalPrefixes = false;
- break;
- }
}
// Handle CL and _CL_ which permits additional command line options to be
// prepended or appended.
if (ClangCLMode) {
// Arguments in "CL" are prepended.
- llvm::Optional<std::string> OptCL = llvm::sys::Process::GetEnv("CL");
- if (OptCL.hasValue()) {
+ std::optional<std::string> OptCL = llvm::sys::Process::GetEnv("CL");
+ if (OptCL) {
SmallVector<const char *, 8> PrependedOpts;
- getCLEnvVarOptions(OptCL.getValue(), Saver, PrependedOpts);
+ getCLEnvVarOptions(*OptCL, Saver, PrependedOpts);
// Insert right after the program name to prepend to the argument list.
Args.insert(Args.begin() + 1, PrependedOpts.begin(), PrependedOpts.end());
}
// Arguments in "_CL_" are appended.
- llvm::Optional<std::string> Opt_CL_ = llvm::sys::Process::GetEnv("_CL_");
- if (Opt_CL_.hasValue()) {
+ std::optional<std::string> Opt_CL_ = llvm::sys::Process::GetEnv("_CL_");
+ if (Opt_CL_) {
SmallVector<const char *, 8> AppendedOpts;
- getCLEnvVarOptions(Opt_CL_.getValue(), Saver, AppendedOpts);
+ getCLEnvVarOptions(*Opt_CL_, Saver, AppendedOpts);
// Insert at the end of the argument list to append.
Args.append(AppendedOpts.begin(), AppendedOpts.end());
}
}
- std::set<std::string> SavedStrings;
+ llvm::StringSet<> SavedStrings;
// Handle CCC_OVERRIDE_OPTIONS, used for editing a command line behind the
// scenes.
if (const char *OverrideStr = ::getenv("CCC_OVERRIDE_OPTIONS")) {
// FIXME: Driver shouldn't take extra initial argument.
- ApplyQAOverride(Args, OverrideStr, SavedStrings);
+ driver::applyOverrideOptions(Args, OverrideStr, SavedStrings,
+ &llvm::errs());
}
- std::string Path = GetExecutablePath(Args[0], CanonicalPrefixes);
+ std::string Path = GetExecutablePath(ToolContext.Path, CanonicalPrefixes);
// Whether the cc1 tool should be called inside the current process, or if we
// should spawn a new clang subprocess (old behavior).
// Not having an additional process saves some execution time of Windows,
// and makes debugging and profiling easier.
- bool UseNewCC1Process;
+ bool UseNewCC1Process = CLANG_SPAWN_CC1;
+ for (const char *Arg : Args)
+ UseNewCC1Process = llvm::StringSwitch<bool>(Arg)
+ .Case("-fno-integrated-cc1", true)
+ .Case("-fintegrated-cc1", false)
+ .Default(UseNewCC1Process);
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
- CreateAndPopulateDiagOpts(Args, UseNewCC1Process);
+ CreateAndPopulateDiagOpts(Args);
TextDiagnosticPrinter *DiagClient
= new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
- FixupDiagPrefixExeName(DiagClient, Path);
+ FixupDiagPrefixExeName(DiagClient, ProgName);
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
@@ -483,47 +335,64 @@ int main(int Argc, const char **Argv) {
ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);
- SetInstallDir(Args, TheDriver, CanonicalPrefixes);
- auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Args[0]);
+ auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(ProgName);
TheDriver.setTargetAndMode(TargetAndMode);
+ // If -canonical-prefixes is set, GetExecutablePath will have resolved Path
+ // to the llvm driver binary, not clang. In this case, we need to use
+ // PrependArg which should be clang-*. Checking just CanonicalPrefixes is
+ // safe even in the normal case because PrependArg will be null so
+ // setPrependArg will be a no-op.
+ if (ToolContext.NeedsPrependArg || CanonicalPrefixes)
+ TheDriver.setPrependArg(ToolContext.PrependArg);
insertTargetAndModeArgs(TargetAndMode, Args, SavedStrings);
- SetBackdoorDriverOutputsFromEnvVars(TheDriver);
+ if (!SetBackdoorDriverOutputsFromEnvVars(TheDriver))
+ return 1;
if (!UseNewCC1Process) {
- TheDriver.CC1Main = &ExecuteCC1Tool;
+ TheDriver.CC1Main = [ToolContext](SmallVectorImpl<const char *> &ArgV) {
+ return ExecuteCC1Tool(ArgV, ToolContext);
+ };
// Ensure the CC1Command actually catches cc1 crashes
llvm::CrashRecoveryContext::Enable();
}
std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Args));
+
+ Driver::ReproLevel ReproLevel = Driver::ReproLevel::OnCrash;
+ if (Arg *A = C->getArgs().getLastArg(options::OPT_gen_reproducer_eq)) {
+ auto Level =
+ llvm::StringSwitch<std::optional<Driver::ReproLevel>>(A->getValue())
+ .Case("off", Driver::ReproLevel::Off)
+ .Case("crash", Driver::ReproLevel::OnCrash)
+ .Case("error", Driver::ReproLevel::OnError)
+ .Case("always", Driver::ReproLevel::Always)
+ .Default(std::nullopt);
+ if (!Level) {
+ llvm::errs() << "Unknown value for " << A->getSpelling() << ": '"
+ << A->getValue() << "'\n";
+ return 1;
+ }
+ ReproLevel = *Level;
+ }
+ if (!!::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"))
+ ReproLevel = Driver::ReproLevel::Always;
+
int Res = 1;
bool IsCrash = false;
+ Driver::CommandStatus CommandStatus = Driver::CommandStatus::Ok;
+ // Pretend the first command failed if ReproStatus is Always.
+ const Command *FailingCommand = nullptr;
+ if (!C->getJobs().empty())
+ FailingCommand = &*C->getJobs().begin();
if (C && !C->containsError()) {
SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
- // Force a crash to test the diagnostics.
- if (TheDriver.GenReproducer) {
- Diags.Report(diag::err_drv_force_crash)
- << !::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH");
-
- // Pretend that every command failed.
- FailingCommands.clear();
- for (const auto &J : C->getJobs())
- if (const Command *C = dyn_cast<Command>(&J))
- FailingCommands.push_back(std::make_pair(-1, C));
-
- // Print the bug report message that would be printed if we did actually
- // crash, but only if we're crashing due to FORCE_CLANG_DIAGNOSTICS_CRASH.
- if (::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"))
- llvm::dbgs() << llvm::getBugReportMsg();
- }
-
for (const auto &P : FailingCommands) {
int CommandRes = P.first;
- const Command *FailingCommand = P.second;
+ FailingCommand = P.second;
if (!Res)
Res = CommandRes;
@@ -542,13 +411,22 @@ int main(int Argc, const char **Argv) {
// https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html
IsCrash |= CommandRes > 128;
#endif
- if (IsCrash) {
- TheDriver.generateCompilationDiagnostics(*C, *FailingCommand);
+ CommandStatus =
+ IsCrash ? Driver::CommandStatus::Crash : Driver::CommandStatus::Error;
+ if (IsCrash)
break;
- }
}
}
+ // Print the bug report message that would be printed if we did actually
+ // crash, but only if we're crashing due to FORCE_CLANG_DIAGNOSTICS_CRASH.
+ if (::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"))
+ llvm::dbgs() << llvm::getBugReportMsg();
+ if (FailingCommand != nullptr &&
+ TheDriver.maybeGenerateCompilationDiagnostics(CommandStatus, ReproLevel,
+ *C, *FailingCommand))
+ Res = 1;
+
Diags.getClient()->finish();
if (!UseNewCC1Process && IsCrash) {