diff options
Diffstat (limited to 'contrib/llvm-project/clang/tools')
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) { |