diff options
author | Robert Nagy <robert@cvs.openbsd.org> | 2023-11-11 18:17:06 +0000 |
---|---|---|
committer | Robert Nagy <robert@cvs.openbsd.org> | 2023-11-11 18:17:06 +0000 |
commit | a388acc7d111eece16e7c46f357872ec47588443 (patch) | |
tree | a636add3cb3f4afb8c45bb8302545f79f9e37d37 /gnu/llvm/clang/tools | |
parent | 77976eff798a4c323c3194ff91190574c44d047d (diff) |
import of clang from LLVM-16.0.6
Diffstat (limited to 'gnu/llvm/clang/tools')
94 files changed, 5575 insertions, 2028 deletions
diff --git a/gnu/llvm/clang/tools/CMakeLists.txt b/gnu/llvm/clang/tools/CMakeLists.txt index c929f6e665e..f60db6ef0ba 100644 --- a/gnu/llvm/clang/tools/CMakeLists.txt +++ b/gnu/llvm/clang/tools/CMakeLists.txt @@ -8,10 +8,13 @@ add_clang_subdirectory(clang-format) add_clang_subdirectory(clang-format-vs) add_clang_subdirectory(clang-fuzzer) add_clang_subdirectory(clang-import-test) +add_clang_subdirectory(clang-linker-wrapper) +add_clang_subdirectory(clang-offload-packager) add_clang_subdirectory(clang-offload-bundler) -add_clang_subdirectory(clang-offload-wrapper) add_clang_subdirectory(clang-scan-deps) -add_clang_subdirectory(clang-repl) +if(HAVE_CLANG_REPL_SUPPORT) + add_clang_subdirectory(clang-repl) +endif() add_clang_subdirectory(c-index-test) @@ -47,3 +50,4 @@ add_llvm_external_project(clang-tools-extra extra) add_clang_subdirectory(libclang) add_clang_subdirectory(amdgpu-arch) +add_clang_subdirectory(nvptx-arch) diff --git a/gnu/llvm/clang/tools/amdgpu-arch/AMDGPUArch.cpp b/gnu/llvm/clang/tools/amdgpu-arch/AMDGPUArch.cpp index 4fae78b4f12..2fdd398c9c6 100644 --- a/gnu/llvm/clang/tools/amdgpu-arch/AMDGPUArch.cpp +++ b/gnu/llvm/clang/tools/amdgpu-arch/AMDGPUArch.cpp @@ -11,26 +11,75 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Error.h" +#include <memory> +#include <string> +#include <vector> + +#if DYNAMIC_HSA +typedef enum { + HSA_STATUS_SUCCESS = 0x0, +} hsa_status_t; + +typedef enum { + HSA_DEVICE_TYPE_CPU = 0, + HSA_DEVICE_TYPE_GPU = 1, +} hsa_device_type_t; + +typedef enum { + HSA_AGENT_INFO_NAME = 0, + HSA_AGENT_INFO_DEVICE = 17, +} hsa_agent_info_t; + +typedef struct hsa_agent_s { + uint64_t handle; +} hsa_agent_t; + +hsa_status_t (*hsa_init)(); +hsa_status_t (*hsa_shut_down)(); +hsa_status_t (*hsa_agent_get_info)(hsa_agent_t, hsa_agent_info_t, void *); +hsa_status_t (*hsa_iterate_agents)(hsa_status_t (*)(hsa_agent_t, void *), + void *); + +constexpr const char *DynamicHSAPath = "libhsa-runtime64.so"; + +llvm::Error loadHSA() { + std::string ErrMsg; + auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>( + llvm::sys::DynamicLibrary::getPermanentLibrary(DynamicHSAPath, &ErrMsg)); + if (!DynlibHandle->isValid()) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Failed to 'dlopen' %s", DynamicHSAPath); + } +#define DYNAMIC_INIT(SYMBOL) \ + { \ + void *SymbolPtr = DynlibHandle->getAddressOfSymbol(#SYMBOL); \ + if (!SymbolPtr) \ + return llvm::createStringError(llvm::inconvertibleErrorCode(), \ + "Failed to 'dlsym' " #SYMBOL); \ + SYMBOL = reinterpret_cast<decltype(SYMBOL)>(SymbolPtr); \ + } + DYNAMIC_INIT(hsa_init); + DYNAMIC_INIT(hsa_shut_down); + DYNAMIC_INIT(hsa_agent_get_info); + DYNAMIC_INIT(hsa_iterate_agents); +#undef DYNAMIC_INIT + return llvm::Error::success(); +} +#else + #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 +#if __has_include("hsa/hsa.h") #include "hsa/hsa.h" -#else -#define HSA_HEADER_FOUND 0 +#elif __has_include("hsa.h") +#include "hsa.h" #endif -#else -#define HSA_HEADER_FOUND 0 +#include "hsa/hsa.h" #endif -#if !HSA_HEADER_FOUND -int main() { return 1; } -#else - -#include <string> -#include <vector> +llvm::Error loadHSA() { return llvm::Error::success(); } +#endif static hsa_status_t iterateAgentsCallback(hsa_agent_t Agent, void *Data) { hsa_device_type_t DeviceType; @@ -53,7 +102,13 @@ static hsa_status_t iterateAgentsCallback(hsa_agent_t Agent, void *Data) { return HSA_STATUS_SUCCESS; } -int main() { +int main(int argc, char *argv[]) { + // Attempt to load the HSA runtime. + if (llvm::Error Err = loadHSA()) { + logAllUnhandledErrors(std::move(Err), llvm::errs()); + return 1; + } + hsa_status_t Status = hsa_init(); if (Status != HSA_STATUS_SUCCESS) { return 1; @@ -74,5 +129,3 @@ int main() { hsa_shut_down(); return 0; } - -#endif diff --git a/gnu/llvm/clang/tools/amdgpu-arch/CMakeLists.txt b/gnu/llvm/clang/tools/amdgpu-arch/CMakeLists.txt index caead440c5c..2028cc266b5 100644 --- a/gnu/llvm/clang/tools/amdgpu-arch/CMakeLists.txt +++ b/gnu/llvm/clang/tools/amdgpu-arch/CMakeLists.txt @@ -6,14 +6,15 @@ # // # //===----------------------------------------------------------------------===// -find_package(hsa-runtime64 QUIET 1.2.0 HINTS ${CMAKE_INSTALL_PREFIX} PATHS /opt/rocm) -if (NOT ${hsa-runtime64_FOUND}) - message(STATUS "Not building amdgpu-arch: hsa-runtime64 not found") - return() -endif() +set(LLVM_LINK_COMPONENTS Support) add_clang_tool(amdgpu-arch AMDGPUArch.cpp) -set_target_properties(amdgpu-arch PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON) - -clang_target_link_libraries(amdgpu-arch PRIVATE hsa-runtime64::hsa-runtime64) +# If we find the HSA runtime we link with it directly. +find_package(hsa-runtime64 QUIET 1.2.0 HINTS ${CMAKE_INSTALL_PREFIX} PATHS /opt/rocm) +if (${hsa-runtime64_FOUND}) + set_target_properties(amdgpu-arch PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON) + target_link_libraries(amdgpu-arch PRIVATE hsa-runtime64::hsa-runtime64) +else() + target_compile_definitions(amdgpu-arch PRIVATE "DYNAMIC_HSA") +endif() diff --git a/gnu/llvm/clang/tools/c-index-test/CMakeLists.txt b/gnu/llvm/clang/tools/c-index-test/CMakeLists.txt index ceef4b08637..0ae1b4e5524 100644 --- a/gnu/llvm/clang/tools/c-index-test/CMakeLists.txt +++ b/gnu/llvm/clang/tools/c-index-test/CMakeLists.txt @@ -40,12 +40,7 @@ set_target_properties(c-index-test # If libxml2 is available, make it available for c-index-test. if (CLANG_HAVE_LIBXML) - if ((CMAKE_OSX_SYSROOT) AND (EXISTS ${CMAKE_OSX_SYSROOT}/${LIBXML2_INCLUDE_DIR})) - include_directories(SYSTEM ${CMAKE_OSX_SYSROOT}/${LIBXML2_INCLUDE_DIR}) - else() - include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) - endif() - target_link_libraries(c-index-test PRIVATE ${LIBXML2_LIBRARIES}) + target_link_libraries(c-index-test PRIVATE LibXml2::LibXml2) endif() if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) @@ -54,7 +49,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) set_property(TARGET c-index-test APPEND PROPERTY INSTALL_RPATH "@executable_path/../../lib") else() - set(INSTALL_DESTINATION bin) + set(INSTALL_DESTINATION "${CMAKE_INSTALL_BINDIR}") endif() install(TARGETS c-index-test diff --git a/gnu/llvm/clang/tools/c-index-test/c-index-test.c b/gnu/llvm/clang/tools/c-index-test/c-index-test.c index a32062caf88..cc425fc51ef 100644 --- a/gnu/llvm/clang/tools/c-index-test/c-index-test.c +++ b/gnu/llvm/clang/tools/c-index-test/c-index-test.c @@ -1,15 +1,17 @@ /* c-index-test.c */ -#include "clang/Config/config.h" -#include "clang-c/Index.h" -#include "clang-c/CXCompilationDatabase.h" #include "clang-c/BuildSystem.h" +#include "clang-c/CXCompilationDatabase.h" +#include "clang-c/CXErrorCode.h" +#include "clang-c/CXString.h" #include "clang-c/Documentation.h" +#include "clang-c/Index.h" +#include "clang/Config/config.h" +#include <assert.h> #include <ctype.h> -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <assert.h> #ifdef CLANG_HAVE_LIBXML #include <libxml/parser.h> @@ -66,7 +68,7 @@ extern char *dirname(char *); #endif /** Return the default parsing options. */ -static unsigned getDefaultParsingOptions() { +static unsigned getDefaultParsingOptions(void) { unsigned options = CXTranslationUnit_DetailedPreprocessingRecord; if (getenv("CINDEXTEST_EDITING")) @@ -900,6 +902,8 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { printf(" (mutable)"); if (clang_CXXMethod_isDefaulted(Cursor)) printf(" (defaulted)"); + if (clang_CXXMethod_isDeleted(Cursor)) + printf(" (deleted)"); if (clang_CXXMethod_isStatic(Cursor)) printf(" (static)"); if (clang_CXXMethod_isVirtual(Cursor)) @@ -908,6 +912,10 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { printf(" (const)"); if (clang_CXXMethod_isPureVirtual(Cursor)) printf(" (pure)"); + if (clang_CXXMethod_isCopyAssignmentOperator(Cursor)) + printf(" (copy-assignment operator)"); + if (clang_CXXMethod_isMoveAssignmentOperator(Cursor)) + printf(" (move-assignment operator)"); if (clang_CXXRecord_isAbstract(Cursor)) printf(" (abstract)"); if (clang_EnumDecl_isScoped(Cursor)) @@ -1000,7 +1008,10 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { clang_getCString(Name), line, column); clang_disposeString(Name); - if (Cursor.kind == CXCursor_FunctionDecl) { + if (Cursor.kind == CXCursor_FunctionDecl + || Cursor.kind == CXCursor_StructDecl + || Cursor.kind == CXCursor_ClassDecl + || Cursor.kind == CXCursor_ClassTemplatePartialSpecialization) { /* Collect the template parameter kinds from the base template. */ int NumTemplateArgs = clang_Cursor_getNumTemplateArguments(Cursor); int I; @@ -1831,6 +1842,18 @@ static enum CXChildVisitResult PrintManglings(CXCursor cursor, CXCursor p, return CXChildVisit_Recurse; } +static enum CXChildVisitResult +PrintSingleSymbolSGFs(CXCursor cursor, CXCursor parent, CXClientData data) { + CXString SGFData = clang_getSymbolGraphForCursor(cursor); + const char *SGF = clang_getCString(SGFData); + if (SGF) + printf("%s\n", SGF); + + clang_disposeString(SGFData); + + return CXChildVisit_Recurse; +} + /******************************************************************************/ /* Bitwidth testing. */ /******************************************************************************/ @@ -3316,7 +3339,7 @@ typedef struct { unsigned num_files; } ImportedASTFilesData; -static ImportedASTFilesData *importedASTs_create() { +static ImportedASTFilesData *importedASTs_create(void) { ImportedASTFilesData *p; p = malloc(sizeof(ImportedASTFilesData)); assert(p); @@ -3504,6 +3527,8 @@ static const char *getEntityKindString(CXIdxEntityKind kind) { case CXIdxEntity_CXXConversionFunction: return "conversion-func"; case CXIdxEntity_CXXTypeAlias: return "type-alias"; case CXIdxEntity_CXXInterface: return "c++-__interface"; + case CXIdxEntity_CXXConcept: + return "concept"; } assert(0 && "Garbage entity kind"); return 0; @@ -4407,7 +4432,7 @@ static void print_usr(CXString usr) { clang_disposeString(usr); } -static void display_usrs() { +static void display_usrs(void) { fprintf(stderr, "-print-usrs options:\n" " ObjCCategory <class name> <category name>\n" " ObjCClass <class name>\n" @@ -4780,6 +4805,64 @@ static int perform_print_build_session_timestamp(void) { return 0; } +static int perform_test_single_symbol_sgf(const char *input, int argc, + const char *argv[]) { + CXIndex Idx; + CXTranslationUnit TU; + CXAPISet API; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + enum CXErrorCode Err; + int result = 0; + const char *InvocationPath; + CXString SGF; + const char *usr; + + usr = input + strlen("-single-symbol-sgf-for="); + + Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, + /* displayDiagnostics=*/0); + InvocationPath = getenv("CINDEXTEST_INVOCATION_EMISSION_PATH"); + if (InvocationPath) + clang_CXIndex_setInvocationEmissionPathOption(Idx, InvocationPath); + + if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { + result = -1; + goto dispose_index; + } + + Err = clang_parseTranslationUnit2( + Idx, 0, argv + num_unsaved_files, argc - num_unsaved_files, unsaved_files, + num_unsaved_files, getDefaultParsingOptions(), &TU); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to load translation unit!\n"); + describeLibclangFailure(Err); + result = 1; + goto free_remapped_files; + } + + Err = clang_createAPISet(TU, &API); + if (Err != CXError_Success) { + fprintf(stderr, + "Unable to create API Set for API information extraction!\n"); + result = 2; + goto dispose_tu; + } + + SGF = clang_getSymbolGraphForUSR(usr, API); + printf("%s", clang_getCString(SGF)); + + clang_disposeString(SGF); + clang_disposeAPISet(API); +dispose_tu: + clang_disposeTranslationUnit(TU); +free_remapped_files: + free_remapped_files(unsaved_files, num_unsaved_files); +dispose_index: + clang_disposeIndex(Idx); + return result; +} + /******************************************************************************/ /* Command line processing. */ /******************************************************************************/ @@ -4838,6 +4921,9 @@ static void print_usage(void) { " c-index-test -print-usr [<CursorKind> {<args>}]*\n" " c-index-test -print-usr-file <file>\n"); fprintf(stderr, + " c-index-test -single-symbol-sgfs <symbol filter> {<args>*}\n" + " c-index-test -single-symbol-sgf-for=<usr> {<args>}*\n"); + fprintf(stderr, " c-index-test -write-pch <file> <compiler arguments>\n" " c-index-test -compilation-db [lookup <filename>] database\n"); fprintf(stderr, @@ -4969,6 +5055,11 @@ int cindextest_main(int argc, const char **argv) { return perform_test_compilation_db(argv[argc-1], argc - 3, argv + 2); else if (argc == 2 && strcmp(argv[1], "-print-build-session-timestamp") == 0) return perform_print_build_session_timestamp(); + else if (argc > 3 && strcmp(argv[1], "-single-symbol-sgfs") == 0) + return perform_test_load_source(argc - 3, argv + 3, argv[2], + PrintSingleSymbolSGFs, NULL); + else if (argc > 2 && strstr(argv[1], "-single-symbol-sgf-for=") == argv[1]) + return perform_test_single_symbol_sgf(argv[1], argc - 2, argv + 2); print_usage(); return 1; diff --git a/gnu/llvm/clang/tools/c-index-test/core_main.cpp b/gnu/llvm/clang/tools/c-index-test/core_main.cpp index 7037252ffa0..ea15b2bcd5c 100644 --- a/gnu/llvm/clang/tools/c-index-test/core_main.cpp +++ b/gnu/llvm/clang/tools/c-index-test/core_main.cpp @@ -13,6 +13,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/Utils.h" #include "clang/Index/IndexDataConsumer.h" #include "clang/Index/IndexingAction.h" #include "clang/Index/USRGeneration.h" @@ -220,7 +221,10 @@ static bool printSourceSymbols(const char *Executable, ArgsWithProgName.append(Args.begin(), Args.end()); IntrusiveRefCntPtr<DiagnosticsEngine> Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); - auto CInvok = createInvocationFromCommandLine(ArgsWithProgName, Diags); + CreateInvocationOptions CIOpts; + CIOpts.Diags = Diags; + CIOpts.ProbePrecompiled = true; // FIXME: historical default. Needed? + auto CInvok = createInvocation(ArgsWithProgName, std::move(CIOpts)); if (!CInvok) return true; diff --git a/gnu/llvm/clang/tools/clang-check/ClangCheck.cpp b/gnu/llvm/clang/tools/clang-check/ClangCheck.cpp index 11fdeb71fd9..0524becf4f4 100644 --- a/gnu/llvm/clang/tools/clang-check/ClangCheck.cpp +++ b/gnu/llvm/clang/tools/clang-check/ClangCheck.cpp @@ -25,6 +25,7 @@ #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Syntax/BuildTree.h" +#include "clang/Tooling/Syntax/TokenBufferTokenManager.h" #include "clang/Tooling/Syntax/Tokens.h" #include "clang/Tooling/Syntax/Tree.h" #include "clang/Tooling/Tooling.h" @@ -76,6 +77,10 @@ static cl::opt<bool> Analyze("analyze", cl::desc(Options.getOptionHelpText(options::OPT_analyze)), cl::cat(ClangCheckCategory)); +static cl::opt<std::string> + AnalyzerOutput("analyzer-output-path", + cl::desc(Options.getOptionHelpText(options::OPT_o)), + cl::cat(ClangCheckCategory)); static cl::opt<bool> Fixit("fixit", cl::desc(Options.getOptionHelpText(options::OPT_fixit)), @@ -153,9 +158,11 @@ public: clang::syntax::TokenBuffer TB = std::move(Collector).consume(); if (TokensDump) llvm::outs() << TB.dumpForTests(); - clang::syntax::Arena A(AST.getSourceManager(), AST.getLangOpts(), TB); - llvm::outs() << clang::syntax::buildSyntaxTree(A, AST)->dump( - AST.getSourceManager()); + clang::syntax::TokenBufferTokenManager TBTM(TB, AST.getLangOpts(), + AST.getSourceManager()); + clang::syntax::Arena A; + llvm::outs() + << clang::syntax::buildSyntaxTree(A, TBTM, AST)->dump(TBTM); } private: @@ -204,15 +211,37 @@ int main(int argc, const char **argv) { ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); - // Clear adjusters because -fsyntax-only is inserted by the default chain. - Tool.clearArgumentsAdjusters(); - Tool.appendArgumentsAdjuster(getClangStripOutputAdjuster()); - Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); + if (Analyze) { + // Set output path if is provided by user. + // + // As the original -o options have been removed by default via the + // strip-output adjuster, we only need to add the analyzer -o options here + // when it is provided by users. + if (!AnalyzerOutput.empty()) + Tool.appendArgumentsAdjuster( + getInsertArgumentAdjuster(CommandLineArguments{"-o", AnalyzerOutput}, + ArgumentInsertPosition::END)); - // Running the analyzer requires --analyze. Other modes can work with the - // -fsyntax-only option. - Tool.appendArgumentsAdjuster(getInsertArgumentAdjuster( - Analyze ? "--analyze" : "-fsyntax-only", ArgumentInsertPosition::BEGIN)); + // Running the analyzer requires --analyze. Other modes can work with the + // -fsyntax-only option. + // + // The syntax-only adjuster is installed by default. + // Good: It also strips options that trigger extra output, like -save-temps. + // Bad: We don't want the -fsyntax-only when executing the static analyzer. + // + // To enable the static analyzer, we first strip all -fsyntax-only options + // and then add an --analyze option to the front. + Tool.appendArgumentsAdjuster( + [&](const CommandLineArguments &Args, StringRef /*unused*/) { + CommandLineArguments AdjustedArgs; + for (const std::string &Arg : Args) + if (Arg != "-fsyntax-only") + AdjustedArgs.emplace_back(Arg); + return AdjustedArgs; + }); + Tool.appendArgumentsAdjuster( + getInsertArgumentAdjuster("--analyze", ArgumentInsertPosition::BEGIN)); + } ClangCheckActionFactory CheckFactory; std::unique_ptr<FrontendActionFactory> FrontendFactory; diff --git a/gnu/llvm/clang/tools/clang-extdef-mapping/ClangExtDefMapGen.cpp b/gnu/llvm/clang/tools/clang-extdef-mapping/ClangExtDefMapGen.cpp index 8aba1301ef9..2a1e605ec4f 100644 --- a/gnu/llvm/clang/tools/clang-extdef-mapping/ClangExtDefMapGen.cpp +++ b/gnu/llvm/clang/tools/clang-extdef-mapping/ClangExtDefMapGen.cpp @@ -1,4 +1,4 @@ -//===- ClangExtDefMapGen.cpp -----------------------------------------------===// +//===- ClangExtDefMapGen.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,14 +13,17 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/CrossTU/CrossTranslationUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Signals.h" +#include <optional> #include <sstream> #include <string> @@ -29,12 +32,16 @@ using namespace clang; using namespace clang::cross_tu; using namespace clang::tooling; -static cl::OptionCategory ClangExtDefMapGenCategory("clang-extdefmapgen options"); +static cl::OptionCategory + ClangExtDefMapGenCategory("clang-extdefmapgen options"); class MapExtDefNamesConsumer : public ASTConsumer { public: - MapExtDefNamesConsumer(ASTContext &Context) - : Ctx(Context), SM(Context.getSourceManager()) {} + MapExtDefNamesConsumer(ASTContext &Context, + StringRef astFilePath = StringRef()) + : Ctx(Context), SM(Context.getSourceManager()) { + CurrentFileName = astFilePath.str(); + } ~MapExtDefNamesConsumer() { // Flush results to standard output. @@ -64,7 +71,7 @@ void MapExtDefNamesConsumer::handleDecl(const Decl *D) { if (const Stmt *Body = FD->getBody()) addIfInMain(FD, Body->getBeginLoc()); } else if (const auto *VD = dyn_cast<VarDecl>(D)) { - if (cross_tu::containsConst(VD, Ctx) && VD->hasInit()) + if (cross_tu::shouldImport(VD, Ctx) && VD->hasInit()) if (const Expr *Init = VD->getInit()) addIfInMain(VD, Init->getBeginLoc()); } @@ -76,7 +83,7 @@ void MapExtDefNamesConsumer::handleDecl(const Decl *D) { void MapExtDefNamesConsumer::addIfInMain(const DeclaratorDecl *DD, SourceLocation defStart) { - llvm::Optional<std::string> LookupName = + std::optional<std::string> LookupName = CrossTranslationUnitContext::getLookupName(DD); if (!LookupName) return; @@ -111,6 +118,82 @@ protected: static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); +static IntrusiveRefCntPtr<DiagnosticsEngine> Diags; + +IntrusiveRefCntPtr<DiagnosticsEngine> GetDiagnosticsEngine() { + if (Diags) { + // Call reset to make sure we don't mix errors + Diags->Reset(false); + return Diags; + } + + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + TextDiagnosticPrinter *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); + DiagClient->setPrefix("clang-extdef-mappping"); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + + IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine( + new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); + Diags.swap(DiagEngine); + + // Retain this one time so it's not destroyed by ASTUnit::LoadFromASTFile + Diags->Retain(); + return Diags; +} + +static CompilerInstance *CI = nullptr; + +static bool HandleAST(StringRef AstPath) { + + if (!CI) + CI = new CompilerInstance(); + + IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine = GetDiagnosticsEngine(); + + std::unique_ptr<ASTUnit> Unit = ASTUnit::LoadFromASTFile( + AstPath.str(), CI->getPCHContainerOperations()->getRawReader(), + ASTUnit::LoadASTOnly, DiagEngine, CI->getFileSystemOpts()); + + if (!Unit) + return false; + + FileManager FM(CI->getFileSystemOpts()); + SmallString<128> AbsPath(AstPath); + FM.makeAbsolutePath(AbsPath); + + MapExtDefNamesConsumer Consumer = + MapExtDefNamesConsumer(Unit->getASTContext(), AbsPath); + Consumer.HandleTranslationUnit(Unit->getASTContext()); + + return true; +} + +static int HandleFiles(ArrayRef<std::string> SourceFiles, + CompilationDatabase &compilations) { + std::vector<std::string> SourcesToBeParsed; + + // Loop over all input files, if they are pre-compiled AST + // process them directly in HandleAST, otherwise put them + // on a list for ClangTool to handle. + for (StringRef Src : SourceFiles) { + if (Src.endswith(".ast")) { + if (!HandleAST(Src)) { + return 1; + } + } else { + SourcesToBeParsed.push_back(Src.str()); + } + } + + if (!SourcesToBeParsed.empty()) { + ClangTool Tool(compilations, SourcesToBeParsed); + return Tool.run(newFrontendActionFactory<MapExtDefNamesAction>().get()); + } + + return 0; +} + int main(int argc, const char **argv) { // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal(argv[0], false); @@ -118,7 +201,10 @@ int main(int argc, const char **argv) { const char *Overview = "\nThis tool collects the USR name and location " "of external definitions in the source files " - "(excluding headers).\n"; + "(excluding headers).\n" + "Input can be either source files that are compiled " + "with compile database or .ast files that are " + "created from clang's -emit-ast option.\n"; auto ExpectedParser = CommonOptionsParser::create( argc, argv, ClangExtDefMapGenCategory, cl::ZeroOrMore, Overview); if (!ExpectedParser) { @@ -127,8 +213,6 @@ int main(int argc, const char **argv) { } CommonOptionsParser &OptionsParser = ExpectedParser.get(); - ClangTool Tool(OptionsParser.getCompilations(), - OptionsParser.getSourcePathList()); - - return Tool.run(newFrontendActionFactory<MapExtDefNamesAction>().get()); + return HandleFiles(OptionsParser.getSourcePathList(), + OptionsParser.getCompilations()); } diff --git a/gnu/llvm/clang/tools/clang-format/.clang-format b/gnu/llvm/clang/tools/clang-format/.clang-format new file mode 100644 index 00000000000..60f4950c01a --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/.clang-format @@ -0,0 +1,4 @@ +BasedOnStyle: LLVM +InsertBraces: true +LineEnding: LF +RemoveBracesLLVM: true diff --git a/gnu/llvm/clang/tools/clang-format/CMakeLists.txt b/gnu/llvm/clang/tools/clang-format/CMakeLists.txt index 35ecdb11253..30c93f8667c 100644 --- a/gnu/llvm/clang/tools/clang-format/CMakeLists.txt +++ b/gnu/llvm/clang/tools/clang-format/CMakeLists.txt @@ -20,21 +20,21 @@ if( LLVM_LIB_FUZZING_ENGINE OR LLVM_USE_SANITIZE_COVERAGE ) add_subdirectory(fuzzer) endif() -install(PROGRAMS clang-format-bbedit.applescript - DESTINATION share/clang +install(FILES clang-format-bbedit.applescript + DESTINATION "${CMAKE_INSTALL_DATADIR}/clang" COMPONENT clang-format) install(PROGRAMS clang-format-diff.py - DESTINATION share/clang + DESTINATION "${CMAKE_INSTALL_DATADIR}/clang" COMPONENT clang-format) -install(PROGRAMS clang-format-sublime.py - DESTINATION share/clang +install(FILES clang-format-sublime.py + DESTINATION "${CMAKE_INSTALL_DATADIR}/clang" COMPONENT clang-format) -install(PROGRAMS clang-format.el - DESTINATION share/clang +install(FILES clang-format.el + DESTINATION "${CMAKE_INSTALL_DATADIR}/clang" COMPONENT clang-format) -install(PROGRAMS clang-format.py - DESTINATION share/clang +install(FILES clang-format.py + DESTINATION "${CMAKE_INSTALL_DATADIR}/clang" COMPONENT clang-format) install(PROGRAMS git-clang-format - DESTINATION bin + DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT clang-format) diff --git a/gnu/llvm/clang/tools/clang-format/ClangFormat.cpp b/gnu/llvm/clang/tools/clang-format/ClangFormat.cpp index 144e87f78c6..dab8a7f2f8c 100644 --- a/gnu/llvm/clang/tools/clang-format/ClangFormat.cpp +++ b/gnu/llvm/clang/tools/clang-format/ClangFormat.cpp @@ -19,10 +19,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 +70,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: .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 +113,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 +161,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> @@ -220,8 +247,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); @@ -345,14 +376,27 @@ 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 == "-") { @@ -402,6 +446,27 @@ static bool format(StringRef FileName) { 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,12 +479,11 @@ 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); @@ -434,18 +498,20 @@ 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 { + else 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()); @@ -530,8 +596,18 @@ int main(int argc, const char **argv) { return 0; } - if (DumpConfig) { + if (DumpConfig) return dumpConfig(); + + 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"; } bool Error = false; @@ -545,9 +621,13 @@ int main(int argc, const char **argv) { "single file.\n"; return 1; } + + unsigned FileNo = 1; for (const auto &FileName : FileNames) { - if (Verbose) - errs() << "Formatting " << FileName << "\n"; + if (Verbose) { + errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] " + << FileName << "\n"; + } Error |= clang::format::format(FileName); } return Error ? 1 : 0; diff --git a/gnu/llvm/clang/tools/clang-format/clang-format-diff.py b/gnu/llvm/clang/tools/clang-format/clang-format-diff.py index ea483f59e96..1f6ff0fe295 100755 --- a/gnu/llvm/clang/tools/clang-format/clang-format-diff.py +++ b/gnu/llvm/clang/tools/clang-format/clang-format-diff.py @@ -47,8 +47,8 @@ def main(): help='custom pattern selecting file paths to reformat ' '(case sensitive, overrides -iregex)') parser.add_argument('-iregex', metavar='PATTERN', default= - r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hh|hpp|hxx|m|mm|inc|js|ts' - r'|proto|protodevel|java|cs|json)', + r'.*\.(cpp|cc|c\+\+|cxx|cppm|ccm|cxxm|c\+\+m|c|cl|h|hh|hpp|hxx' + r'|m|mm|inc|js|ts|proto|protodevel|java|cs|json)', help='custom pattern selecting file paths to reformat ' '(case insensitive, overridden by -regex)') parser.add_argument('-sort-includes', action='store_true', default=False, @@ -58,6 +58,11 @@ def main(): parser.add_argument('-style', help='formatting style to apply (LLVM, GNU, Google, Chromium, ' 'Microsoft, Mozilla, WebKit)') + parser.add_argument('-fallback-style', + help='The name of the predefined style used as a' + 'fallback in case clang-format is invoked with' + '-style=file, but can not find the .clang-format' + 'file to use.') parser.add_argument('-binary', default='clang-format', help='location of binary to use for clang-format') args = parser.parse_args() @@ -85,9 +90,11 @@ def main(): line_count = 1 if match.group(3): line_count = int(match.group(3)) - if line_count == 0: - continue - end_line = start_line + line_count - 1 + # Also format lines range if line_count is 0 in case of deleting + # surrounding statements. + end_line = start_line + if line_count != 0: + end_line += line_count - 1 lines_by_file.setdefault(filename, []).extend( ['-lines', str(start_line) + ':' + str(end_line)]) @@ -103,6 +110,8 @@ def main(): command.extend(lines) if args.style: command.extend(['-style', args.style]) + if args.fallback_style: + command.extend(['-fallback-style', args.fallback_style]) try: p = subprocess.Popen(command, diff --git a/gnu/llvm/clang/tools/clang-format/clang-format.el b/gnu/llvm/clang/tools/clang-format/clang-format.el index 768acb3a5d0..4e6daa82d4a 100644 --- a/gnu/llvm/clang/tools/clang-format/clang-format.el +++ b/gnu/llvm/clang/tools/clang-format/clang-format.el @@ -147,7 +147,7 @@ uses the function `buffer-file-name'." (setq style clang-format-style)) (unless assume-file-name - (setq assume-file-name buffer-file-name)) + (setq assume-file-name (buffer-file-name (buffer-base-buffer)))) (let ((file-start (clang-format--bufferpos-to-filepos start 'approximate 'utf-8-unix)) diff --git a/gnu/llvm/clang/tools/clang-format/clang-format.py b/gnu/llvm/clang/tools/clang-format/clang-format.py index 76fedb64814..7933dbcfadd 100644 --- a/gnu/llvm/clang/tools/clang-format/clang-format.py +++ b/gnu/llvm/clang/tools/clang-format/clang-format.py @@ -10,9 +10,9 @@ # imap <C-I> <c-o>:py3f <path-to-this-file>/clang-format.py<cr> # endif # -# The if-elseif-endif conditional should pick either the python3 or python2 +# The if-elseif-endif conditional should pick either the python3 or python2 # integration depending on your vim setup. -# +# # The first mapping enables clang-format for NORMAL and VISUAL mode, the second # mapping adds support for INSERT mode. Change "C-I" to another binding if you # need clang-format on a different key (C-I stands for Ctrl+i). @@ -41,6 +41,7 @@ from __future__ import absolute_import, division, print_function import difflib import json +import os.path import platform import subprocess import sys @@ -76,7 +77,8 @@ def main(): # Determine range to format. if vim.eval('exists("l:lines")') == '1': lines = ['-lines', vim.eval('l:lines')] - elif vim.eval('exists("l:formatdiff")') == '1': + elif vim.eval('exists("l:formatdiff")') == '1' and \ + os.path.exists(vim.current.buffer.name): with open(vim.current.buffer.name, 'r') as f: ondisk = f.read().splitlines(); sequence = difflib.SequenceMatcher(None, ondisk, vim.current.buffer) @@ -134,7 +136,7 @@ def main(): ) else: header, content = stdout.split(b'\n', 1) - header = json.loads(header) + header = json.loads(header.decode('utf-8')) # Strip off the trailing newline (added above). # This maintains trailing empty lines present in the buffer if # the -lines specification requests them to remain unchanged. diff --git a/gnu/llvm/clang/tools/clang-format/git-clang-format b/gnu/llvm/clang/tools/clang-format/git-clang-format index 0233ceb3a86..054978c3dbd 100755 --- a/gnu/llvm/clang/tools/clang-format/git-clang-format +++ b/gnu/llvm/clang/tools/clang-format/git-clang-format @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # #===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===# # @@ -32,12 +32,23 @@ import re import subprocess import sys -usage = 'git clang-format [OPTIONS] [<commit>] [<commit>] [--] [<file>...]' +usage = ('git clang-format [OPTIONS] [<commit>] [<commit>|--staged] ' + '[--] [<file>...]') desc = ''' If zero or one commits are given, run clang-format on all lines that differ between the working directory and <commit>, which defaults to HEAD. Changes are -only applied to the working directory. +only applied to the working directory, or in the stage/index. + +Examples: + To format staged changes, i.e everything that's been `git add`ed: + git clang-format + + To also format everything touched in the most recent commit: + git clang-format HEAD~1 + + If you're on a branch off main, to format everything touched on your branch: + git clang-format main If two commits are given (requires --diff), run clang-format on all lines in the second <commit> that differ from the first <commit>. @@ -77,7 +88,8 @@ def main(): 'c', 'h', # C 'm', # ObjC 'mm', # ObjC++ - 'cc', 'cp', 'cpp', 'c++', 'cxx', 'hh', 'hpp', 'hxx', # C++ + 'cc', 'cp', 'cpp', 'c++', 'cxx', 'hh', 'hpp', 'hxx', 'inc', # C++ + 'ccm', 'cppm', 'cxxm', 'c++m', # C++ Modules 'cu', 'cuh', # CUDA # Other languages that clang-format supports 'proto', 'protodevel', # Protocol Buffers @@ -99,6 +111,8 @@ def main(): help='default commit to use if none is specified'), p.add_argument('--diff', action='store_true', help='print a diff instead of applying the changes') + p.add_argument('--diffstat', action='store_true', + help='print a diffstat instead of applying the changes') p.add_argument('--extensions', default=config.get('clangformat.extensions', default_extensions), @@ -110,6 +124,8 @@ def main(): help='select hunks interactively') p.add_argument('-q', '--quiet', action='count', default=0, help='print less information') + p.add_argument('--staged', '--cached', action='store_true', + help='format lines in the stage instead of the working dir') p.add_argument('--style', default=config.get('clangformat.style', None), help='passed to clang-format'), @@ -129,12 +145,14 @@ def main(): commits, files = interpret_args(opts.args, dash_dash, opts.commit) if len(commits) > 1: + if opts.staged: + die('--staged is not allowed when two commits are given') if not opts.diff: die('--diff is required when two commits are given') else: if len(commits) > 2: die('at most two commits allowed; %d given' % len(commits)) - changed_lines = compute_diff_and_extract_lines(commits, files) + changed_lines = compute_diff_and_extract_lines(commits, files, opts.staged) if opts.verbose >= 1: ignored_files = set(changed_lines) filter_by_extension(changed_lines, opts.extensions.lower().split(',')) @@ -153,36 +171,47 @@ def main(): print('Running clang-format on the following files:') for filename in changed_lines: print(' %s' % filename) + if not changed_lines: if opts.verbose >= 0: print('no modified files to format') - return + return 0 + if len(commits) > 1: old_tree = commits[1] - new_tree = run_clang_format_and_save_to_tree(changed_lines, - revision=commits[1], - binary=opts.binary, - style=opts.style) + revision = old_tree + elif opts.staged: + old_tree = create_tree_from_index(changed_lines) + revision = '' else: old_tree = create_tree_from_workdir(changed_lines) - new_tree = run_clang_format_and_save_to_tree(changed_lines, - binary=opts.binary, - style=opts.style) + revision = None + new_tree = run_clang_format_and_save_to_tree(changed_lines, + revision, + binary=opts.binary, + style=opts.style) if opts.verbose >= 1: print('old tree: %s' % old_tree) print('new tree: %s' % new_tree) + if old_tree == new_tree: if opts.verbose >= 0: print('clang-format did not modify any files') - elif opts.diff: - print_diff(old_tree, new_tree) - else: - changed_files = apply_changes(old_tree, new_tree, force=opts.force, - patch_mode=opts.patch) - if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1: - print('changed files:') - for filename in changed_files: - print(' %s' % filename) + return 0 + + if opts.diff: + return print_diff(old_tree, new_tree) + if opts.diffstat: + return print_diffstat(old_tree, new_tree) + + changed_files = apply_changes(old_tree, new_tree, force=opts.force, + patch_mode=opts.patch) + if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1: + print('changed files:') + for filename in changed_files: + print(' %s' % filename) + + return 1 def load_git_config(non_string_options=None): @@ -271,9 +300,9 @@ def get_object_type(value): return convert_string(stdout.strip()) -def compute_diff_and_extract_lines(commits, files): +def compute_diff_and_extract_lines(commits, files, staged): """Calls compute_diff() followed by extract_lines().""" - diff_process = compute_diff(commits, files) + diff_process = compute_diff(commits, files, staged) changed_lines = extract_lines(diff_process.stdout) diff_process.stdout.close() diff_process.wait() @@ -283,17 +312,21 @@ def compute_diff_and_extract_lines(commits, files): return changed_lines -def compute_diff(commits, files): +def compute_diff(commits, files, staged): """Return a subprocess object producing the diff from `commits`. The return value's `stdin` file object will produce a patch with the - differences between the working directory and the first commit if a single - one was specified, or the difference between both specified commits, filtered - on `files` (if non-empty). Zero context lines are used in the patch.""" + differences between the working directory (or stage if --staged is used) and + the first commit if a single one was specified, or the difference between + both specified commits, filtered on `files` (if non-empty). + Zero context lines are used in the patch.""" git_tool = 'diff-index' + extra_args = [] if len(commits) > 1: git_tool = 'diff-tree' - cmd = ['git', git_tool, '-p', '-U0'] + commits + ['--'] + elif staged: + extra_args += ['--cached'] + cmd = ['git', git_tool, '-p', '-U0'] + extra_args + commits + ['--'] cmd.extend(files) p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.stdin.close() @@ -321,8 +354,11 @@ def extract_lines(patch_file): line_count = 1 if match.group(3): line_count = int(match.group(3)) - if line_count > 0: - matches.setdefault(filename, []).append(Range(start_line, line_count)) + if line_count == 0: + line_count = 1 + if start_line == 0: + continue + matches.setdefault(filename, []).append(Range(start_line, line_count)) return matches @@ -360,11 +396,29 @@ def create_tree_from_workdir(filenames): return create_tree(filenames, '--stdin') +def create_tree_from_index(filenames): + # Copy the environment, because the files have to be read from the original + # index. + env = os.environ.copy() + def index_contents_generator(): + for filename in filenames: + git_ls_files_cmd = ['git', 'ls-files', '--stage', '-z', '--', filename] + git_ls_files = subprocess.Popen(git_ls_files_cmd, env=env, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + stdout = git_ls_files.communicate()[0] + yield convert_string(stdout.split(b'\0')[0]) + return create_tree(index_contents_generator(), '--index-info') + + def run_clang_format_and_save_to_tree(changed_lines, revision=None, binary='clang-format', style=None): """Run clang-format on each file and save the result to a git tree. Returns the object ID (SHA-1) of the created tree.""" + # Copy the environment when formatting the files in the index, because the + # files have to be read from the original index. + env = os.environ.copy() if revision == '' else None def iteritems(container): try: return container.iteritems() # Python 2 @@ -372,11 +426,15 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None, return container.items() # Python 3 def index_info_generator(): for filename, line_ranges in iteritems(changed_lines): - if revision: - git_metadata_cmd = ['git', 'ls-tree', - '%s:%s' % (revision, os.path.dirname(filename)), - os.path.basename(filename)] - git_metadata = subprocess.Popen(git_metadata_cmd, stdin=subprocess.PIPE, + if revision is not None: + if len(revision) > 0: + git_metadata_cmd = ['git', 'ls-tree', + '%s:%s' % (revision, os.path.dirname(filename)), + os.path.basename(filename)] + else: + git_metadata_cmd = ['git', 'ls-files', '--stage', '--', filename] + git_metadata = subprocess.Popen(git_metadata_cmd, env=env, + stdin=subprocess.PIPE, stdout=subprocess.PIPE) stdout = git_metadata.communicate()[0] mode = oct(int(stdout.split()[0], 8)) @@ -388,7 +446,8 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None, blob_id = clang_format_to_blob(filename, line_ranges, revision=revision, binary=binary, - style=style) + style=style, + env=env) yield '%s %s\t%s' % (mode, blob_id, filename) return create_tree(index_info_generator(), '--index-info') @@ -414,11 +473,12 @@ def create_tree(input_lines, mode): def clang_format_to_blob(filename, line_ranges, revision=None, - binary='clang-format', style=None): + binary='clang-format', style=None, env=None): """Run clang-format on the given file and save the result to a git blob. Runs on the file in `revision` if not None, or on the file in the working - directory if `revision` is None. + directory if `revision` is None. Revision can be set to an empty string to run + clang-format on the file in the index. Returns the object ID (SHA-1) of the created blob.""" clang_format_cmd = [binary] @@ -427,10 +487,10 @@ def clang_format_to_blob(filename, line_ranges, revision=None, clang_format_cmd.extend([ '-lines=%s:%s' % (start_line, start_line+line_count-1) for start_line, line_count in line_ranges]) - if revision: + if revision is not None: clang_format_cmd.extend(['-assume-filename='+filename]) git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)] - git_show = subprocess.Popen(git_show_cmd, stdin=subprocess.PIPE, + git_show = subprocess.Popen(git_show_cmd, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE) git_show.stdin.close() clang_format_stdin = git_show.stdout @@ -502,9 +562,20 @@ def print_diff(old_tree, new_tree): # We also only print modified files since `new_tree` only contains the files # that were modified, so unmodified files would show as deleted without the # filter. - subprocess.check_call(['git', 'diff', '--diff-filter=M', old_tree, new_tree, - '--']) + return subprocess.run(['git', 'diff', '--diff-filter=M', + '--exit-code', old_tree, new_tree]).returncode +def print_diffstat(old_tree, new_tree): + """Print the diffstat between the two trees to stdout.""" + # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output + # is expected to be viewed by the user, and only the former does nice things + # like color and pagination. + # + # We also only print modified files since `new_tree` only contains the files + # that were modified, so unmodified files would show as deleted without the + # filter. + return subprocess.run(['git', 'diff', '--diff-filter=M', '--exit-code', + '--stat', old_tree, new_tree]).returncode def apply_changes(old_tree, new_tree, force=False, patch_mode=False): """Apply the changes in `new_tree` to the working directory. @@ -530,7 +601,7 @@ def apply_changes(old_tree, new_tree, force=False, patch_mode=False): # better message, "Apply ... to index and worktree". This is not quite # right, since it won't be applied to the user's index, but oh well. with temporary_index_file(old_tree): - subprocess.check_call(['git', 'checkout', '--patch', new_tree]) + subprocess.run(['git', 'checkout', '--patch', new_tree], check=True) index_tree = old_tree else: with temporary_index_file(new_tree): @@ -593,4 +664,4 @@ def convert_string(bytes_input): return str(bytes_input) if __name__ == '__main__': - main() + sys.exit(main()) diff --git a/gnu/llvm/clang/tools/clang-fuzzer/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/CMakeLists.txt index 4b2243c5ceb..e68ed8bbcb0 100644 --- a/gnu/llvm/clang/tools/clang-fuzzer/CMakeLists.txt +++ b/gnu/llvm/clang/tools/clang-fuzzer/CMakeLists.txt @@ -1,4 +1,4 @@ -set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} FuzzMutate) +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} FuzzerCLI) set(CXX_FLAGS_NOFUZZ ${CMAKE_CXX_FLAGS}) set(DUMMY_MAIN DummyClangFuzzer.cpp) if(LLVM_LIB_FUZZING_ENGINE) @@ -109,6 +109,7 @@ endif() add_clang_subdirectory(handle-cxx) add_clang_subdirectory(handle-llvm) +add_clang_subdirectory(dictionary) add_clang_executable(clang-fuzzer EXCLUDE_FROM_ALL diff --git a/gnu/llvm/clang/tools/clang-fuzzer/dictionary/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/dictionary/CMakeLists.txt new file mode 100644 index 00000000000..ee4aa587ea5 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/dictionary/CMakeLists.txt @@ -0,0 +1,4 @@ +add_clang_executable(clang-fuzzer-dictionary + dictionary.c + ) + diff --git a/gnu/llvm/clang/tools/clang-fuzzer/dictionary/dictionary.c b/gnu/llvm/clang/tools/clang-fuzzer/dictionary/dictionary.c new file mode 100644 index 00000000000..90490477f70 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/dictionary/dictionary.c @@ -0,0 +1,57 @@ +//===-- dictionary.c - Generate fuzzing dictionary for clang --------------===// +// +// 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 binary emits a fuzzing dictionary describing strings that are +// significant to the clang parser: keywords and other tokens. +// +// The dictionary can be used by a fuzzer to reach interesting parser states +// much more quickly. +// +// The output is a single-file dictionary supported by libFuzzer and AFL: +// https://llvm.org/docs/LibFuzzer.html#dictionaries +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +static void emit(const char *Name, const char *Spelling) { + static char Hex[] = "0123456789abcdef"; + + printf("%s=\"", Name); + unsigned char C; + while ((C = *Spelling++)) { + if (C < 32 || C == '"' || C == '\\') + printf("\\x%c%c", Hex[C>>4], Hex[C%16]); + else + printf("%c", C); + } + printf("\"\n"); +} + +int main(int argc, char **argv) { +#define PUNCTUATOR(Name, Spelling) emit(#Name, Spelling); +#define KEYWORD(Name, Criteria) emit(#Name, #Name); +#define PPKEYWORD(Name) emit(#Name, #Name); +#define CXX_KEYWORD_OPERATOR(Name, Equivalent) emit(#Name, #Name); +#define OBJC_AT_KEYWORD(Name) emit(#Name, #Name); +#define ALIAS(Spelling, Equivalent, Criteria) emit(Spelling, Spelling); +#include "clang/Basic/TokenKinds.def" + // Some other sub-token chunks significant to the lexer. + emit("ucn16", "\\u0000"); + emit("ucn32", "\\U00000000"); + emit("rawstart", "R\"("); + emit("rawend", ")\""); + emit("quote", "\""); + emit("squote", "'"); + emit("u8quote", "u8\""); + emit("u16quote", "u\""); + emit("u32quote", "U\""); + emit("esc_nl", "\\\n"); + emit("hex", "0x"); +} + diff --git a/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.cpp b/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.cpp index 20cf98896e2..94f3b937d83 100644 --- a/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.cpp +++ b/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.cpp @@ -48,8 +48,6 @@ extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { initializeAnalysis(Registry); initializeTransformUtils(Registry); initializeInstCombine(Registry); - initializeAggressiveInstCombine(Registry); - initializeInstrumentation(Registry); initializeTarget(Registry); CLArgs.push_back("-O2"); diff --git a/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/CMakeLists.txt index 6d62421d9a6..184d467b9c3 100644 --- a/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/CMakeLists.txt +++ b/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/CMakeLists.txt @@ -11,3 +11,5 @@ add_clang_library(clangHandleCXX clangSerialization clangTooling ) + +target_include_directories(clangHandleCXX PRIVATE .) diff --git a/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/CMakeLists.txt index 9ceb1d33182..9962f9850f5 100644 --- a/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/CMakeLists.txt +++ b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/CMakeLists.txt @@ -4,14 +4,17 @@ set(LLVM_LINK_COMPONENTS Core ExecutionEngine IPO + IRPrinter IRReader MC MCJIT Object + Passes RuntimeDyld SelectionDAG Support Target + TargetParser TransformUtils native ) diff --git a/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp index 4adb6eb39d0..06df39dcdc4 100644 --- a/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp +++ b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp @@ -30,47 +30,39 @@ #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/LegacyPassNameParser.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" +#include "llvm/IRPrinter/IRPrintingPasses.h" #include "llvm/IRReader/IRReader.h" -#include "llvm/Pass.h" -#include "llvm/PassRegistry.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Passes/OptimizationLevel.h" +#include "llvm/Passes/PassBuilder.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" -#include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Target/TargetMachine.h" -#include "llvm/Transforms/IPO.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Vectorize.h" using namespace llvm; -static codegen::RegisterCodeGenFlags CGF; - // Define a type for the functions that are compiled and executed typedef void (*LLVMFunc)(int*, int*, int*, int); // Helper function to parse command line args and find the optimization level -static void getOptLevel(const std::vector<const char *> &ExtraArgs, - CodeGenOpt::Level &OLvl) { +static CodeGenOpt::Level +getOptLevel(const std::vector<const char *> &ExtraArgs) { // Find the optimization level from the command line args - OLvl = CodeGenOpt::Default; + CodeGenOpt::Level OLvl = CodeGenOpt::Default; for (auto &A : ExtraArgs) { if (A[0] == '-' && A[1] == 'O') { - switch(A[2]) { - case '0': OLvl = CodeGenOpt::None; break; - case '1': OLvl = CodeGenOpt::Less; break; - case '2': OLvl = CodeGenOpt::Default; break; - case '3': OLvl = CodeGenOpt::Aggressive; break; - default: - errs() << "error: opt level must be between 0 and 3.\n"; - std::exit(1); + if (auto Level = CodeGenOpt::parseLevel(A[2])) { + OLvl = *Level; + } else { + errs() << "error: opt level must be between 0 and 3.\n"; + std::exit(1); } } } + return OLvl; } static void ErrorAndExit(std::string message) { @@ -80,16 +72,45 @@ static void ErrorAndExit(std::string message) { // Helper function to add optimization passes to the TargetMachine at the // specified optimization level, OptLevel -static void AddOptimizationPasses(legacy::PassManagerBase &MPM, - CodeGenOpt::Level OptLevel, - unsigned SizeLevel) { - // Create and initialize a PassManagerBuilder - PassManagerBuilder Builder; - Builder.OptLevel = OptLevel; - Builder.SizeLevel = SizeLevel; - Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false); - Builder.LoopVectorize = true; - Builder.populateModulePassManager(MPM); +static void RunOptimizationPasses(raw_ostream &OS, Module &M, + CodeGenOpt::Level OptLevel) { + llvm::OptimizationLevel OL; + switch (OptLevel) { + case CodeGenOpt::None: + OL = OptimizationLevel::O0; + break; + case CodeGenOpt::Less: + OL = OptimizationLevel::O1; + break; + case CodeGenOpt::Default: + OL = OptimizationLevel::O2; + break; + case CodeGenOpt::Aggressive: + OL = OptimizationLevel::O3; + break; + } + + LoopAnalysisManager LAM; + FunctionAnalysisManager FAM; + CGSCCAnalysisManager CGAM; + ModuleAnalysisManager MAM; + + PassBuilder PB; + + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + ModulePassManager MPM; + if (OL == OptimizationLevel::O0) + MPM = PB.buildO0DefaultPipeline(OL); + else + MPM = PB.buildPerModuleDefaultPipeline(OL); + MPM.addPass(PrintModulePass(OS)); + + MPM.run(M, MAM); } // Mimics the opt tool to run an optimization pass over the provided IR @@ -120,26 +141,12 @@ static std::string OptLLVM(const std::string &IR, CodeGenOpt::Level OLvl) { codegen::setFunctionAttributes(codegen::getCPUStr(), codegen::getFeaturesStr(), *M); - legacy::PassManager Passes; - - Passes.add(new TargetLibraryInfoWrapperPass(ModuleTriple)); - Passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); - - LLVMTargetMachine <M = static_cast<LLVMTargetMachine &>(*TM); - Passes.add(LTM.createPassConfig(Passes)); - - Passes.add(createVerifierPass()); - - AddOptimizationPasses(Passes, OLvl, 0); - // Add a pass that writes the optimized IR to an output stream std::string outString; raw_string_ostream OS(outString); - Passes.add(createPrintModulePass(OS, "", false)); - - Passes.run(*M); + RunOptimizationPasses(OS, *M, OLvl); - return OS.str(); + return outString; } // Takes a function and runs it on a set of inputs @@ -216,8 +223,7 @@ void clang_fuzzer::HandleLLVM(const std::string &IR, memcpy(UnoptArrays, InputArrays, kTotalSize); // Parse ExtraArgs to set the optimization level - CodeGenOpt::Level OLvl; - getOptLevel(ExtraArgs, OLvl); + CodeGenOpt::Level OLvl = getOptLevel(ExtraArgs); // First we optimize the IR by running a loop vectorizer pass std::string OptIR = OptLLVM(IR, OLvl); @@ -227,6 +233,4 @@ void clang_fuzzer::HandleLLVM(const std::string &IR, if (memcmp(OptArrays, UnoptArrays, kTotalSize)) ErrorAndExit("!!!BUG!!!"); - - return; } diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt index 339959b81af..baefc8a3014 100644 --- a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt @@ -14,6 +14,8 @@ add_clang_library(clangLoopProtoToCXX loop_proto_to_cxx.cpp DEPENDS clangCXXLoopProto LINK_LIBS clangCXXLoopProto ${PROTOBUF_LIBRARIES} ) +target_include_directories(clangProtoToCXX PRIVATE .) +target_include_directories(clangLoopProtoToCXX PRIVATE .) add_clang_executable(clang-proto-to-cxx proto_to_cxx_main.cpp) add_clang_executable(clang-loop-proto-to-cxx loop_proto_to_cxx_main.cpp) diff --git a/gnu/llvm/clang/tools/clang-import-test/CMakeLists.txt b/gnu/llvm/clang/tools/clang-import-test/CMakeLists.txt index e459de8f635..6b70bebd885 100644 --- a/gnu/llvm/clang/tools/clang-import-test/CMakeLists.txt +++ b/gnu/llvm/clang/tools/clang-import-test/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_LINK_COMPONENTS Core Support + TargetParser ) add_clang_executable(clang-import-test diff --git a/gnu/llvm/clang/tools/clang-import-test/clang-import-test.cpp b/gnu/llvm/clang/tools/clang-import-test/clang-import-test.cpp index a5206319bbb..6403d1120fd 100644 --- a/gnu/llvm/clang/tools/clang-import-test/clang-import-test.cpp +++ b/gnu/llvm/clang/tools/clang-import-test/clang-import-test.cpp @@ -43,7 +43,7 @@ static llvm::cl::opt<std::string> Expression( llvm::cl::desc("Path to a file containing the expression to parse")); static llvm::cl::list<std::string> - Imports("import", llvm::cl::ZeroOrMore, + Imports("import", llvm::cl::desc("Path to a file containing declarations to import")); static llvm::cl::opt<bool> @@ -56,7 +56,7 @@ static llvm::cl::opt<bool> UseOrigins( "Use DeclContext origin information for more accurate lookups")); static llvm::cl::list<std::string> - ClangArgs("Xcc", llvm::cl::ZeroOrMore, + ClangArgs("Xcc", llvm::cl::desc("Argument to pass to the CompilerInvocation"), llvm::cl::CommaSeparated); @@ -84,18 +84,18 @@ public: TestDiagnosticConsumer() : Passthrough(std::make_unique<TextDiagnosticBuffer>()) {} - virtual void BeginSourceFile(const LangOptions &LangOpts, - const Preprocessor *PP = nullptr) override { + void BeginSourceFile(const LangOptions &LangOpts, + const Preprocessor *PP = nullptr) override { this->LangOpts = &LangOpts; return Passthrough->BeginSourceFile(LangOpts, PP); } - virtual void EndSourceFile() override { + void EndSourceFile() override { this->LangOpts = nullptr; Passthrough->EndSourceFile(); } - virtual bool IncludeInDiagnosticCounts() const override { + bool IncludeInDiagnosticCounts() const override { return Passthrough->IncludeInDiagnosticCounts(); } @@ -130,8 +130,8 @@ private: llvm::errs() << '\n'; } - virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, - const Diagnostic &Info) override { + void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) override { if (Info.hasSourceManager() && LangOpts) { SourceManager &SM = Info.getSourceManager(); @@ -230,8 +230,9 @@ std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx) { StringRef ModuleName("$__module"); return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen( - CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(), - CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx)); + CI.getDiagnostics(), ModuleName, &CI.getVirtualFileSystem(), + CI.getHeaderSearchOpts(), CI.getPreprocessorOpts(), CI.getCodeGenOpts(), + LLVMCtx)); } } // namespace init_convenience diff --git a/gnu/llvm/clang/tools/clang-linker-wrapper/CMakeLists.txt b/gnu/llvm/clang/tools/clang-linker-wrapper/CMakeLists.txt new file mode 100644 index 00000000000..cf0774c8c6c --- /dev/null +++ b/gnu/llvm/clang/tools/clang-linker-wrapper/CMakeLists.txt @@ -0,0 +1,45 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + BitWriter + Core + BinaryFormat + MC + Target + TransformUtils + Analysis + Passes + IRReader + Object + Option + Support + TargetParser + CodeGen + LTO + ) + +set(LLVM_TARGET_DEFINITIONS LinkerWrapperOpts.td) +tablegen(LLVM LinkerWrapperOpts.inc -gen-opt-parser-defs) +add_public_tablegen_target(LinkerWrapperOpts) + +if(NOT CLANG_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen LinkerWrapperOpts) +endif() + +add_clang_tool(clang-linker-wrapper + ClangLinkerWrapper.cpp + OffloadWrapper.cpp + + DEPENDS + ${tablegen_deps} + ) + +set(CLANG_LINKER_WRAPPER_LIB_DEPS + clangBasic + ) + +add_dependencies(clang clang-linker-wrapper) + +target_link_libraries(clang-linker-wrapper + PRIVATE + ${CLANG_LINKER_WRAPPER_LIB_DEPS} + ) diff --git a/gnu/llvm/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/gnu/llvm/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp new file mode 100644 index 00000000000..16ac5f4c8be --- /dev/null +++ b/gnu/llvm/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -0,0 +1,1442 @@ +//===-- clang-linker-wrapper/ClangLinkerWrapper.cpp - wrapper over linker-===// +// +// 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 tool works as a wrapper over a linking job. This tool is used to create +// linked device images for offloading. It scans the linker's input for embedded +// device offloading data stored in sections `.llvm.offloading` and extracts it +// as a temporary file. The extracted device files will then be passed to a +// device linking job to create a final device image. +// +//===---------------------------------------------------------------------===// + +#include "OffloadWrapper.h" +#include "clang/Basic/Version.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/CodeGen/CommandFlags.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/LTO/LTO.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/IRObjectFile.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/OffloadBinary.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Parallel.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +#include <atomic> +#include <optional> + +using namespace llvm; +using namespace llvm::opt; +using namespace llvm::object; + +/// Path of the current binary. +static const char *LinkerExecutable; + +/// Ssave intermediary results. +static bool SaveTemps = false; + +/// Print arguments without executing. +static bool DryRun = false; + +/// Print verbose output. +static bool Verbose = false; + +/// Filename of the executable being created. +static StringRef ExecutableName; + +/// Binary path for the CUDA installation. +static std::string CudaBinaryPath; + +/// Mutex lock to protect writes to shared TempFiles in parallel. +static std::mutex TempFilesMutex; + +/// Temporary files created by the linker wrapper. +static std::list<SmallString<128>> TempFiles; + +/// Codegen flags for LTO backend. +static codegen::RegisterCodeGenFlags CodeGenFlags; + +/// Global flag to indicate that the LTO pipeline threw an error. +static std::atomic<bool> LTOError; + +using OffloadingImage = OffloadBinary::OffloadingImage; + +namespace llvm { +// Provide DenseMapInfo so that OffloadKind can be used in a DenseMap. +template <> struct DenseMapInfo<OffloadKind> { + static inline OffloadKind getEmptyKey() { return OFK_LAST; } + static inline OffloadKind getTombstoneKey() { + return static_cast<OffloadKind>(OFK_LAST + 1); + } + static unsigned getHashValue(const OffloadKind &Val) { return Val; } + + static bool isEqual(const OffloadKind &LHS, const OffloadKind &RHS) { + return LHS == RHS; + } +}; +} // namespace llvm + +namespace { +using std::error_code; + +/// Must not overlap with llvm::opt::DriverFlag. +enum WrapperFlags { + WrapperOnlyOption = (1 << 4), // Options only used by the linker wrapper. + DeviceOnlyOption = (1 << 5), // Options only used for device linking. +}; + +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "LinkerWrapperOpts.inc" + LastOption +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) \ + static constexpr StringLiteral NAME##_init[] = VALUE; \ + static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ + std::size(NAME##_init) - 1); +#include "LinkerWrapperOpts.inc" +#undef PREFIX + +static constexpr OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS, VALUES}, +#include "LinkerWrapperOpts.inc" +#undef OPTION +}; + +class WrapperOptTable : public opt::GenericOptTable { +public: + WrapperOptTable() : opt::GenericOptTable(InfoTable) {} +}; + +const OptTable &getOptTable() { + static const WrapperOptTable *Table = []() { + auto Result = std::make_unique<WrapperOptTable>(); + return Result.release(); + }(); + return *Table; +} + +void printCommands(ArrayRef<StringRef> CmdArgs) { + if (CmdArgs.empty()) + return; + + llvm::errs() << " \"" << CmdArgs.front() << "\" "; + for (auto IC = std::next(CmdArgs.begin()), IE = CmdArgs.end(); IC != IE; ++IC) + llvm::errs() << *IC << (std::next(IC) != IE ? " " : "\n"); +} + +[[noreturn]] void reportError(Error E) { + outs().flush(); + logAllUnhandledErrors(std::move(E), + WithColor::error(errs(), LinkerExecutable)); + exit(EXIT_FAILURE); +} + +/// Create an extra user-specified \p OffloadFile. +/// TODO: We should find a way to wrap these as libraries instead. +Expected<OffloadFile> getInputBitcodeLibrary(StringRef Input) { + auto [Device, Path] = StringRef(Input).split('='); + auto [String, Arch] = Device.rsplit('-'); + auto [Kind, Triple] = String.split('-'); + + llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> ImageOrError = + llvm::MemoryBuffer::getFileOrSTDIN(Path); + if (std::error_code EC = ImageOrError.getError()) + return createFileError(Path, EC); + + OffloadingImage Image{}; + Image.TheImageKind = IMG_Bitcode; + Image.TheOffloadKind = getOffloadKind(Kind); + Image.StringData = {{"triple", Triple}, {"arch", Arch}}; + Image.Image = std::move(*ImageOrError); + + std::unique_ptr<MemoryBuffer> Binary = OffloadBinary::write(Image); + auto NewBinaryOrErr = OffloadBinary::create(*Binary); + if (!NewBinaryOrErr) + return NewBinaryOrErr.takeError(); + return OffloadFile(std::move(*NewBinaryOrErr), std::move(Binary)); +} + +std::string getMainExecutable(const char *Name) { + void *Ptr = (void *)(intptr_t)&getMainExecutable; + auto COWPath = sys::fs::getMainExecutable(Name, Ptr); + return sys::path::parent_path(COWPath).str(); +} + +/// Get a temporary filename suitable for output. +Expected<StringRef> createOutputFile(const Twine &Prefix, StringRef Extension) { + std::scoped_lock<decltype(TempFilesMutex)> Lock(TempFilesMutex); + SmallString<128> OutputFile; + if (SaveTemps) { + (Prefix + "." + Extension).toNullTerminatedStringRef(OutputFile); + } else { + if (std::error_code EC = + sys::fs::createTemporaryFile(Prefix, Extension, OutputFile)) + return createFileError(OutputFile, EC); + } + + TempFiles.emplace_back(std::move(OutputFile)); + return TempFiles.back(); +} + +/// Execute the command \p ExecutablePath with the arguments \p Args. +Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) { + if (Verbose || DryRun) + printCommands(Args); + + if (!DryRun) + if (sys::ExecuteAndWait(ExecutablePath, Args)) + return createStringError(inconvertibleErrorCode(), + "'" + sys::path::filename(ExecutablePath) + "'" + + " failed"); + return Error::success(); +} + +Expected<std::string> findProgram(StringRef Name, ArrayRef<StringRef> Paths) { + + ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths); + if (!Path) + Path = sys::findProgramByName(Name); + if (!Path && DryRun) + return Name.str(); + if (!Path) + return createStringError(Path.getError(), + "Unable to find '" + Name + "' in path"); + return *Path; +} + +/// Runs the wrapped linker job with the newly created input. +Error runLinker(ArrayRef<StringRef> Files, const ArgList &Args) { + llvm::TimeTraceScope TimeScope("Execute host linker"); + + // Render the linker arguments and add the newly created image. We add it + // after the output file to ensure it is linked with the correct libraries. + StringRef LinkerPath = Args.getLastArgValue(OPT_linker_path_EQ); + ArgStringList NewLinkerArgs; + for (const opt::Arg *Arg : Args) { + // Do not forward arguments only intended for the linker wrapper. + if (Arg->getOption().hasFlag(WrapperOnlyOption)) + continue; + + Arg->render(Args, NewLinkerArgs); + if (Arg->getOption().matches(OPT_o)) + llvm::transform(Files, std::back_inserter(NewLinkerArgs), + [&](StringRef Arg) { return Args.MakeArgString(Arg); }); + } + + SmallVector<StringRef> LinkerArgs({LinkerPath}); + for (StringRef Arg : NewLinkerArgs) + LinkerArgs.push_back(Arg); + if (Error Err = executeCommands(LinkerPath, LinkerArgs)) + return Err; + return Error::success(); +} + +void printVersion(raw_ostream &OS) { + OS << clang::getClangToolFullVersion("clang-linker-wrapper") << '\n'; +} + +namespace nvptx { +Expected<StringRef> +fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles, + const ArgList &Args) { + llvm::TimeTraceScope TimeScope("NVPTX fatbinary"); + // NVPTX uses the fatbinary program to bundle the linked images. + Expected<std::string> FatBinaryPath = + findProgram("fatbinary", {CudaBinaryPath + "/bin"}); + if (!FatBinaryPath) + return FatBinaryPath.takeError(); + + llvm::Triple Triple( + Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple())); + + // Create a new file to write the linked device image to. + auto TempFileOrErr = + createOutputFile(sys::path::filename(ExecutableName), "fatbin"); + if (!TempFileOrErr) + return TempFileOrErr.takeError(); + + SmallVector<StringRef, 16> CmdArgs; + CmdArgs.push_back(*FatBinaryPath); + CmdArgs.push_back(Triple.isArch64Bit() ? "-64" : "-32"); + CmdArgs.push_back("--create"); + CmdArgs.push_back(*TempFileOrErr); + for (const auto &[File, Arch] : InputFiles) + CmdArgs.push_back( + Args.MakeArgString("--image=profile=" + Arch + ",file=" + File)); + + if (Error Err = executeCommands(*FatBinaryPath, CmdArgs)) + return std::move(Err); + + return *TempFileOrErr; +} +} // namespace nvptx + +namespace amdgcn { +Expected<StringRef> +fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles, + const ArgList &Args) { + llvm::TimeTraceScope TimeScope("AMDGPU Fatbinary"); + + // AMDGPU uses the clang-offload-bundler to bundle the linked images. + Expected<std::string> OffloadBundlerPath = findProgram( + "clang-offload-bundler", {getMainExecutable("clang-offload-bundler")}); + if (!OffloadBundlerPath) + return OffloadBundlerPath.takeError(); + + llvm::Triple Triple( + Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple())); + + // Create a new file to write the linked device image to. + auto TempFileOrErr = + createOutputFile(sys::path::filename(ExecutableName), "hipfb"); + if (!TempFileOrErr) + return TempFileOrErr.takeError(); + + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + + SmallVector<StringRef, 16> CmdArgs; + CmdArgs.push_back(*OffloadBundlerPath); + CmdArgs.push_back("-type=o"); + CmdArgs.push_back("-bundle-align=4096"); + + SmallVector<StringRef> Targets = {"-targets=host-x86_64-unknown-linux"}; + for (const auto &[File, Arch] : InputFiles) + Targets.push_back(Saver.save("hipv4-amdgcn-amd-amdhsa--" + Arch)); + CmdArgs.push_back(Saver.save(llvm::join(Targets, ","))); + + CmdArgs.push_back("-input=/dev/null"); + for (const auto &[File, Arch] : InputFiles) + CmdArgs.push_back(Saver.save("-input=" + File)); + + CmdArgs.push_back(Saver.save("-output=" + *TempFileOrErr)); + + if (Error Err = executeCommands(*OffloadBundlerPath, CmdArgs)) + return std::move(Err); + + return *TempFileOrErr; +} +} // namespace amdgcn + +namespace generic { +Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args) { + llvm::TimeTraceScope TimeScope("Clang"); + // Use `clang` to invoke the appropriate device tools. + Expected<std::string> ClangPath = + findProgram("clang", {getMainExecutable("clang")}); + if (!ClangPath) + return ClangPath.takeError(); + + const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ)); + StringRef Arch = Args.getLastArgValue(OPT_arch_EQ); + if (Arch.empty()) + Arch = "native"; + // Create a new file to write the linked device image to. Assume that the + // input filename already has the device and architecture. + auto TempFileOrErr = + createOutputFile(sys::path::filename(ExecutableName) + "." + + Triple.getArchName() + "." + Arch, + "img"); + if (!TempFileOrErr) + return TempFileOrErr.takeError(); + + StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2"); + SmallVector<StringRef, 16> CmdArgs{ + *ClangPath, + "-o", + *TempFileOrErr, + Args.MakeArgString("--target=" + Triple.getTriple()), + Triple.isAMDGPU() ? Args.MakeArgString("-mcpu=" + Arch) + : Args.MakeArgString("-march=" + Arch), + Args.MakeArgString("-" + OptLevel), + "-Wl,--no-undefined", + }; + + // If this is CPU offloading we copy the input libraries. + if (!Triple.isAMDGPU() && !Triple.isNVPTX()) { + CmdArgs.push_back("-Bsymbolic"); + CmdArgs.push_back("-shared"); + ArgStringList LinkerArgs; + for (const opt::Arg *Arg : + Args.filtered(OPT_library, OPT_rpath, OPT_library_path)) + Arg->render(Args, LinkerArgs); + llvm::copy(LinkerArgs, std::back_inserter(CmdArgs)); + } + + if (Args.hasArg(OPT_debug)) + CmdArgs.push_back("-g"); + + if (SaveTemps) + CmdArgs.push_back("-save-temps"); + + if (Verbose) + CmdArgs.push_back("-v"); + + if (!CudaBinaryPath.empty()) + CmdArgs.push_back(Args.MakeArgString("--cuda-path=" + CudaBinaryPath)); + + for (StringRef Arg : Args.getAllArgValues(OPT_ptxas_arg)) + llvm::copy(SmallVector<StringRef>({"-Xcuda-ptxas", Arg}), + std::back_inserter(CmdArgs)); + + for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ)) + CmdArgs.push_back(Args.MakeArgString("-Wl," + Arg)); + + for (StringRef InputFile : InputFiles) + CmdArgs.push_back(InputFile); + + if (Error Err = executeCommands(*ClangPath, CmdArgs)) + return std::move(Err); + + return *TempFileOrErr; +} +} // namespace generic + +Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles, + const ArgList &Args) { + const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ)); + switch (Triple.getArch()) { + case Triple::nvptx: + case Triple::nvptx64: + case Triple::amdgcn: + case Triple::x86: + case Triple::x86_64: + case Triple::aarch64: + case Triple::aarch64_be: + case Triple::ppc64: + case Triple::ppc64le: + return generic::clang(InputFiles, Args); + default: + return createStringError(inconvertibleErrorCode(), + Triple.getArchName() + + " linking is not supported"); + } +} + +void diagnosticHandler(const DiagnosticInfo &DI) { + std::string ErrStorage; + raw_string_ostream OS(ErrStorage); + DiagnosticPrinterRawOStream DP(OS); + DI.print(DP); + + switch (DI.getSeverity()) { + case DS_Error: + WithColor::error(errs(), LinkerExecutable) << ErrStorage << "\n"; + LTOError = true; + break; + case DS_Warning: + WithColor::warning(errs(), LinkerExecutable) << ErrStorage << "\n"; + break; + case DS_Note: + WithColor::note(errs(), LinkerExecutable) << ErrStorage << "\n"; + break; + case DS_Remark: + WithColor::remark(errs()) << ErrStorage << "\n"; + break; + } +} + +// Get the list of target features from the input file and unify them such that +// if there are multiple +xxx or -xxx features we only keep the last one. +std::vector<std::string> getTargetFeatures(ArrayRef<OffloadFile> InputFiles) { + SmallVector<StringRef> Features; + for (const OffloadFile &File : InputFiles) { + for (auto Arg : llvm::split(File.getBinary()->getString("feature"), ",")) + Features.emplace_back(Arg); + } + + // Only add a feature if it hasn't been seen before starting from the end. + std::vector<std::string> UnifiedFeatures; + DenseSet<StringRef> UsedFeatures; + for (StringRef Feature : llvm::reverse(Features)) { + if (UsedFeatures.insert(Feature.drop_front()).second) + UnifiedFeatures.push_back(Feature.str()); + } + + return UnifiedFeatures; +} + +template <typename ModuleHook = function_ref<bool(size_t, const Module &)>> +std::unique_ptr<lto::LTO> createLTO( + const ArgList &Args, const std::vector<std::string> &Features, + ModuleHook Hook = [](size_t, const Module &) { return true; }) { + const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ)); + StringRef Arch = Args.getLastArgValue(OPT_arch_EQ); + lto::Config Conf; + lto::ThinBackend Backend; + // TODO: Handle index-only thin-LTO + Backend = + lto::createInProcessThinBackend(llvm::heavyweight_hardware_concurrency()); + + Conf.CPU = Arch.str(); + Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple); + + StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2"); + Conf.MAttrs = Features; + std::optional<CodeGenOpt::Level> CGOptLevelOrNone = + CodeGenOpt::parseLevel(OptLevel[1]); + assert(CGOptLevelOrNone && "Invalid optimization level"); + Conf.CGOptLevel = *CGOptLevelOrNone; + Conf.OptLevel = OptLevel[1] - '0'; + Conf.DefaultTriple = Triple.getTriple(); + + LTOError = false; + Conf.DiagHandler = diagnosticHandler; + + Conf.PTO.LoopVectorization = Conf.OptLevel > 1; + Conf.PTO.SLPVectorization = Conf.OptLevel > 1; + + if (SaveTemps) { + std::string TempName = (sys::path::filename(ExecutableName) + "." + + Triple.getTriple() + "." + Arch) + .str(); + Conf.PostInternalizeModuleHook = [=](size_t Task, const Module &M) { + std::string File = + !Task ? TempName + ".postlink.bc" + : TempName + "." + std::to_string(Task) + ".postlink.bc"; + error_code EC; + raw_fd_ostream LinkedBitcode(File, EC, sys::fs::OF_None); + if (EC) + reportError(errorCodeToError(EC)); + WriteBitcodeToFile(M, LinkedBitcode); + return true; + }; + Conf.PreCodeGenModuleHook = [=](size_t Task, const Module &M) { + std::string File = + !Task ? TempName + ".postopt.bc" + : TempName + "." + std::to_string(Task) + ".postopt.bc"; + error_code EC; + raw_fd_ostream LinkedBitcode(File, EC, sys::fs::OF_None); + if (EC) + reportError(errorCodeToError(EC)); + WriteBitcodeToFile(M, LinkedBitcode); + return true; + }; + } + Conf.PostOptModuleHook = Hook; + Conf.CGFileType = + (Triple.isNVPTX() || SaveTemps) ? CGFT_AssemblyFile : CGFT_ObjectFile; + + // TODO: Handle remark files + Conf.HasWholeProgramVisibility = Args.hasArg(OPT_whole_program); + + return std::make_unique<lto::LTO>(std::move(Conf), Backend); +} + +// Returns true if \p S is valid as a C language identifier and will be given +// `__start_` and `__stop_` symbols. +bool isValidCIdentifier(StringRef S) { + return !S.empty() && (isAlpha(S[0]) || S[0] == '_') && + llvm::all_of(llvm::drop_begin(S), + [](char C) { return C == '_' || isAlnum(C); }); +} + +Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles, + SmallVectorImpl<StringRef> &OutputFiles, + const ArgList &Args) { + llvm::TimeTraceScope TimeScope("Link bitcode files"); + const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ)); + StringRef Arch = Args.getLastArgValue(OPT_arch_EQ); + + SmallVector<OffloadFile, 4> BitcodeInputFiles; + DenseSet<StringRef> UsedInRegularObj; + DenseSet<StringRef> UsedInSharedLib; + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + + // Search for bitcode files in the input and create an LTO input file. If it + // is not a bitcode file, scan its symbol table for symbols we need to save. + for (OffloadFile &File : InputFiles) { + MemoryBufferRef Buffer = MemoryBufferRef(File.getBinary()->getImage(), ""); + + file_magic Type = identify_magic(Buffer.getBuffer()); + switch (Type) { + case file_magic::bitcode: { + BitcodeInputFiles.emplace_back(std::move(File)); + continue; + } + case file_magic::elf_relocatable: + case file_magic::elf_shared_object: { + Expected<std::unique_ptr<ObjectFile>> ObjFile = + ObjectFile::createObjectFile(Buffer); + if (!ObjFile) + continue; + + for (SymbolRef Sym : (*ObjFile)->symbols()) { + Expected<StringRef> Name = Sym.getName(); + if (!Name) + return Name.takeError(); + + // Record if we've seen these symbols in any object or shared libraries. + if ((*ObjFile)->isRelocatableObject()) + UsedInRegularObj.insert(Saver.save(*Name)); + else + UsedInSharedLib.insert(Saver.save(*Name)); + } + continue; + } + default: + continue; + } + } + + if (BitcodeInputFiles.empty()) + return Error::success(); + + // Remove all the bitcode files that we moved from the original input. + llvm::erase_if(InputFiles, [](OffloadFile &F) { return !F.getBinary(); }); + + // LTO Module hook to output bitcode without running the backend. + SmallVector<StringRef, 4> BitcodeOutput; + auto OutputBitcode = [&](size_t, const Module &M) { + auto TempFileOrErr = createOutputFile(sys::path::filename(ExecutableName) + + "-jit-" + Triple.getTriple(), + "bc"); + if (!TempFileOrErr) + reportError(TempFileOrErr.takeError()); + + std::error_code EC; + raw_fd_ostream LinkedBitcode(*TempFileOrErr, EC, sys::fs::OF_None); + if (EC) + reportError(errorCodeToError(EC)); + WriteBitcodeToFile(M, LinkedBitcode); + BitcodeOutput.push_back(*TempFileOrErr); + return false; + }; + + // We assume visibility of the whole program if every input file was bitcode. + auto Features = getTargetFeatures(BitcodeInputFiles); + auto LTOBackend = Args.hasArg(OPT_embed_bitcode) + ? createLTO(Args, Features, OutputBitcode) + : createLTO(Args, Features); + + // We need to resolve the symbols so the LTO backend knows which symbols need + // to be kept or can be internalized. This is a simplified symbol resolution + // scheme to approximate the full resolution a linker would do. + uint64_t Idx = 0; + DenseSet<StringRef> PrevailingSymbols; + for (auto &BitcodeInput : BitcodeInputFiles) { + // Get a semi-unique buffer identifier for Thin-LTO. + StringRef Identifier = Saver.save( + std::to_string(Idx++) + "." + + BitcodeInput.getBinary()->getMemoryBufferRef().getBufferIdentifier()); + MemoryBufferRef Buffer = + MemoryBufferRef(BitcodeInput.getBinary()->getImage(), Identifier); + Expected<std::unique_ptr<lto::InputFile>> BitcodeFileOrErr = + llvm::lto::InputFile::create(Buffer); + if (!BitcodeFileOrErr) + return BitcodeFileOrErr.takeError(); + + // Save the input file and the buffer associated with its memory. + const auto Symbols = (*BitcodeFileOrErr)->symbols(); + SmallVector<lto::SymbolResolution, 16> Resolutions(Symbols.size()); + size_t Idx = 0; + for (auto &Sym : Symbols) { + lto::SymbolResolution &Res = Resolutions[Idx++]; + + // We will use this as the prevailing symbol definition in LTO unless + // it is undefined or another definition has already been used. + Res.Prevailing = + !Sym.isUndefined() && + PrevailingSymbols.insert(Saver.save(Sym.getName())).second; + + // We need LTO to preseve the following global symbols: + // 1) Symbols used in regular objects. + // 2) Sections that will be given a __start/__stop symbol. + // 3) Prevailing symbols that are needed visible to external libraries. + Res.VisibleToRegularObj = + UsedInRegularObj.contains(Sym.getName()) || + isValidCIdentifier(Sym.getSectionName()) || + (Res.Prevailing && + (Sym.getVisibility() != GlobalValue::HiddenVisibility && + !Sym.canBeOmittedFromSymbolTable())); + + // Identify symbols that must be exported dynamically and can be + // referenced by other files. + Res.ExportDynamic = + Sym.getVisibility() != GlobalValue::HiddenVisibility && + (UsedInSharedLib.contains(Sym.getName()) || + !Sym.canBeOmittedFromSymbolTable()); + + // The final definition will reside in this linkage unit if the symbol is + // defined and local to the module. This only checks for bitcode files, + // full assertion will require complete symbol resolution. + Res.FinalDefinitionInLinkageUnit = + Sym.getVisibility() != GlobalValue::DefaultVisibility && + (!Sym.isUndefined() && !Sym.isCommon()); + + // We do not support linker redefined symbols (e.g. --wrap) for device + // image linking, so the symbols will not be changed after LTO. + Res.LinkerRedefined = false; + } + + // Add the bitcode file with its resolved symbols to the LTO job. + if (Error Err = LTOBackend->add(std::move(*BitcodeFileOrErr), Resolutions)) + return Err; + } + + // Run the LTO job to compile the bitcode. + size_t MaxTasks = LTOBackend->getMaxTasks(); + SmallVector<StringRef> Files(MaxTasks); + auto AddStream = + [&](size_t Task, + const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> { + int FD = -1; + auto &TempFile = Files[Task]; + StringRef Extension = (Triple.isNVPTX() || SaveTemps) ? "s" : "o"; + std::string TaskStr = Task ? "." + std::to_string(Task) : ""; + auto TempFileOrErr = + createOutputFile(sys::path::filename(ExecutableName) + "." + + Triple.getTriple() + "." + Arch + TaskStr, + Extension); + if (!TempFileOrErr) + reportError(TempFileOrErr.takeError()); + TempFile = *TempFileOrErr; + if (std::error_code EC = sys::fs::openFileForWrite(TempFile, FD)) + reportError(errorCodeToError(EC)); + return std::make_unique<CachedFileStream>( + std::make_unique<llvm::raw_fd_ostream>(FD, true)); + }; + + if (Error Err = LTOBackend->run(AddStream)) + return Err; + + if (LTOError) + return createStringError(inconvertibleErrorCode(), + "Errors encountered inside the LTO pipeline."); + + // If we are embedding bitcode we only need the intermediate output. + bool SingleOutput = Files.size() == 1; + if (Args.hasArg(OPT_embed_bitcode)) { + if (BitcodeOutput.size() != 1 || !SingleOutput) + return createStringError(inconvertibleErrorCode(), + "Cannot embed bitcode with multiple files."); + OutputFiles.push_back(Args.MakeArgString(BitcodeOutput.front())); + return Error::success(); + } + + // Append the new inputs to the device linker input. + for (StringRef File : Files) + OutputFiles.push_back(File); + + return Error::success(); +} + +Expected<StringRef> writeOffloadFile(const OffloadFile &File) { + const OffloadBinary &Binary = *File.getBinary(); + + StringRef Prefix = + sys::path::stem(Binary.getMemoryBufferRef().getBufferIdentifier()); + StringRef Suffix = getImageKindName(Binary.getImageKind()); + + auto TempFileOrErr = createOutputFile( + Prefix + "-" + Binary.getTriple() + "-" + Binary.getArch(), Suffix); + if (!TempFileOrErr) + return TempFileOrErr.takeError(); + + Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr = + FileOutputBuffer::create(*TempFileOrErr, Binary.getImage().size()); + if (!OutputOrErr) + return OutputOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr); + llvm::copy(Binary.getImage(), Output->getBufferStart()); + if (Error E = Output->commit()) + return std::move(E); + + return *TempFileOrErr; +} + +// Compile the module to an object file using the appropriate target machine for +// the host triple. +Expected<StringRef> compileModule(Module &M) { + llvm::TimeTraceScope TimeScope("Compile module"); + std::string Msg; + const Target *T = TargetRegistry::lookupTarget(M.getTargetTriple(), Msg); + if (!T) + return createStringError(inconvertibleErrorCode(), Msg); + + auto Options = + codegen::InitTargetOptionsFromCodeGenFlags(Triple(M.getTargetTriple())); + StringRef CPU = ""; + StringRef Features = ""; + std::unique_ptr<TargetMachine> TM( + T->createTargetMachine(M.getTargetTriple(), CPU, Features, Options, + Reloc::PIC_, M.getCodeModel())); + + if (M.getDataLayout().isDefault()) + M.setDataLayout(TM->createDataLayout()); + + int FD = -1; + auto TempFileOrErr = createOutputFile( + sys::path::filename(ExecutableName) + ".image.wrapper", "o"); + if (!TempFileOrErr) + return TempFileOrErr.takeError(); + if (std::error_code EC = sys::fs::openFileForWrite(*TempFileOrErr, FD)) + return errorCodeToError(EC); + + auto OS = std::make_unique<llvm::raw_fd_ostream>(FD, true); + + legacy::PassManager CodeGenPasses; + TargetLibraryInfoImpl TLII(Triple(M.getTargetTriple())); + CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII)); + if (TM->addPassesToEmitFile(CodeGenPasses, *OS, nullptr, CGFT_ObjectFile)) + return createStringError(inconvertibleErrorCode(), + "Failed to execute host backend"); + CodeGenPasses.run(M); + + return *TempFileOrErr; +} + +/// Creates the object file containing the device image and runtime +/// registration code from the device images stored in \p Images. +Expected<StringRef> +wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers, + const ArgList &Args, OffloadKind Kind) { + llvm::TimeTraceScope TimeScope("Wrap bundled images"); + + SmallVector<ArrayRef<char>, 4> BuffersToWrap; + for (const auto &Buffer : Buffers) + BuffersToWrap.emplace_back( + ArrayRef<char>(Buffer->getBufferStart(), Buffer->getBufferSize())); + + LLVMContext Context; + Module M("offload.wrapper.module", Context); + M.setTargetTriple( + Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple())); + + switch (Kind) { + case OFK_OpenMP: + if (Error Err = wrapOpenMPBinaries(M, BuffersToWrap)) + return std::move(Err); + break; + case OFK_Cuda: + if (Error Err = wrapCudaBinary(M, BuffersToWrap.front())) + return std::move(Err); + break; + case OFK_HIP: + if (Error Err = wrapHIPBinary(M, BuffersToWrap.front())) + return std::move(Err); + break; + default: + return createStringError(inconvertibleErrorCode(), + getOffloadKindName(Kind) + + " wrapping is not supported"); + } + + if (Args.hasArg(OPT_print_wrapped_module)) + errs() << M; + + auto FileOrErr = compileModule(M); + if (!FileOrErr) + return FileOrErr.takeError(); + return *FileOrErr; +} + +Expected<SmallVector<std::unique_ptr<MemoryBuffer>>> +bundleOpenMP(ArrayRef<OffloadingImage> Images) { + SmallVector<std::unique_ptr<MemoryBuffer>> Buffers; + for (const OffloadingImage &Image : Images) + Buffers.emplace_back(OffloadBinary::write(Image)); + + return std::move(Buffers); +} + +Expected<SmallVector<std::unique_ptr<MemoryBuffer>>> +bundleCuda(ArrayRef<OffloadingImage> Images, const ArgList &Args) { + SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles; + for (const OffloadingImage &Image : Images) + InputFiles.emplace_back(std::make_pair(Image.Image->getBufferIdentifier(), + Image.StringData.lookup("arch"))); + + Triple TheTriple = Triple(Images.front().StringData.lookup("triple")); + auto FileOrErr = nvptx::fatbinary(InputFiles, Args); + if (!FileOrErr) + return FileOrErr.takeError(); + + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError = + llvm::MemoryBuffer::getFileOrSTDIN(*FileOrErr); + + SmallVector<std::unique_ptr<MemoryBuffer>> Buffers; + if (std::error_code EC = ImageOrError.getError()) + return createFileError(*FileOrErr, EC); + Buffers.emplace_back(std::move(*ImageOrError)); + + return std::move(Buffers); +} + +Expected<SmallVector<std::unique_ptr<MemoryBuffer>>> +bundleHIP(ArrayRef<OffloadingImage> Images, const ArgList &Args) { + SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles; + for (const OffloadingImage &Image : Images) + InputFiles.emplace_back(std::make_pair(Image.Image->getBufferIdentifier(), + Image.StringData.lookup("arch"))); + + Triple TheTriple = Triple(Images.front().StringData.lookup("triple")); + auto FileOrErr = amdgcn::fatbinary(InputFiles, Args); + if (!FileOrErr) + return FileOrErr.takeError(); + + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError = + llvm::MemoryBuffer::getFileOrSTDIN(*FileOrErr); + + SmallVector<std::unique_ptr<MemoryBuffer>> Buffers; + if (std::error_code EC = ImageOrError.getError()) + return createFileError(*FileOrErr, EC); + Buffers.emplace_back(std::move(*ImageOrError)); + + return std::move(Buffers); +} + +/// Transforms the input \p Images into the binary format the runtime expects +/// for the given \p Kind. +Expected<SmallVector<std::unique_ptr<MemoryBuffer>>> +bundleLinkedOutput(ArrayRef<OffloadingImage> Images, const ArgList &Args, + OffloadKind Kind) { + llvm::TimeTraceScope TimeScope("Bundle linked output"); + switch (Kind) { + case OFK_OpenMP: + return bundleOpenMP(Images); + case OFK_Cuda: + return bundleCuda(Images, Args); + case OFK_HIP: + return bundleHIP(Images, Args); + default: + return createStringError(inconvertibleErrorCode(), + getOffloadKindName(Kind) + + " bundling is not supported"); + } +} + +/// Returns a new ArgList containg arguments used for the device linking phase. +DerivedArgList getLinkerArgs(ArrayRef<OffloadFile> Input, + const InputArgList &Args) { + DerivedArgList DAL = DerivedArgList(DerivedArgList(Args)); + for (Arg *A : Args) + DAL.append(A); + + // Set the subarchitecture and target triple for this compilation. + const OptTable &Tbl = getOptTable(); + DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch_EQ), + Args.MakeArgString(Input.front().getBinary()->getArch())); + DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_triple_EQ), + Args.MakeArgString(Input.front().getBinary()->getTriple())); + + // If every input file is bitcode we have whole program visibility as we do + // only support static linking with bitcode. + auto ContainsBitcode = [](const OffloadFile &F) { + return identify_magic(F.getBinary()->getImage()) == file_magic::bitcode; + }; + if (llvm::all_of(Input, ContainsBitcode)) + DAL.AddFlagArg(nullptr, Tbl.getOption(OPT_whole_program)); + + // Forward '-Xoffload-linker' options to the appropriate backend. + for (StringRef Arg : Args.getAllArgValues(OPT_device_linker_args_EQ)) { + auto [Triple, Value] = Arg.split('='); + if (Value.empty()) + DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ), + Args.MakeArgString(Triple)); + else if (Triple == DAL.getLastArgValue(OPT_triple_EQ)) + DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ), + Args.MakeArgString(Value)); + } + + return DAL; +} + +/// Transforms all the extracted offloading input files into an image that can +/// be registered by the runtime. +Expected<SmallVector<StringRef>> +linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles, + const InputArgList &Args, char **Argv, int Argc) { + llvm::TimeTraceScope TimeScope("Handle all device input"); + + DenseMap<OffloadFile::TargetID, SmallVector<OffloadFile>> InputMap; + for (auto &File : LinkerInputFiles) + InputMap[File].emplace_back(std::move(File)); + LinkerInputFiles.clear(); + + SmallVector<SmallVector<OffloadFile>> InputsForTarget; + for (auto &[ID, Input] : InputMap) + InputsForTarget.emplace_back(std::move(Input)); + InputMap.clear(); + + std::mutex ImageMtx; + DenseMap<OffloadKind, SmallVector<OffloadingImage>> Images; + auto Err = parallelForEachError(InputsForTarget, [&](auto &Input) -> Error { + llvm::TimeTraceScope TimeScope("Link device input"); + + // Each thread needs its own copy of the base arguments to maintain + // per-device argument storage of synthetic strings. + const OptTable &Tbl = getOptTable(); + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + auto BaseArgs = + Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [](StringRef Err) { + reportError(createStringError(inconvertibleErrorCode(), Err)); + }); + auto LinkerArgs = getLinkerArgs(Input, BaseArgs); + + DenseSet<OffloadKind> ActiveOffloadKinds; + for (const auto &File : Input) + if (File.getBinary()->getOffloadKind() != OFK_None) + ActiveOffloadKinds.insert(File.getBinary()->getOffloadKind()); + + // First link and remove all the input files containing bitcode. + SmallVector<StringRef> InputFiles; + if (Error Err = linkBitcodeFiles(Input, InputFiles, LinkerArgs)) + return Err; + + // Write any remaining device inputs to an output file for the linker. + for (const OffloadFile &File : Input) { + auto FileNameOrErr = writeOffloadFile(File); + if (!FileNameOrErr) + return FileNameOrErr.takeError(); + InputFiles.emplace_back(*FileNameOrErr); + } + + // Link the remaining device files using the device linker. + auto OutputOrErr = !Args.hasArg(OPT_embed_bitcode) + ? linkDevice(InputFiles, LinkerArgs) + : InputFiles.front(); + if (!OutputOrErr) + return OutputOrErr.takeError(); + + // Store the offloading image for each linked output file. + for (OffloadKind Kind : ActiveOffloadKinds) { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileOrErr = + llvm::MemoryBuffer::getFileOrSTDIN(*OutputOrErr); + if (std::error_code EC = FileOrErr.getError()) { + if (DryRun) + FileOrErr = MemoryBuffer::getMemBuffer(""); + else + return createFileError(*OutputOrErr, EC); + } + + std::scoped_lock<decltype(ImageMtx)> Guard(ImageMtx); + OffloadingImage TheImage{}; + TheImage.TheImageKind = + Args.hasArg(OPT_embed_bitcode) ? IMG_Bitcode : IMG_Object; + TheImage.TheOffloadKind = Kind; + TheImage.StringData = { + {"triple", + Args.MakeArgString(LinkerArgs.getLastArgValue(OPT_triple_EQ))}, + {"arch", + Args.MakeArgString(LinkerArgs.getLastArgValue(OPT_arch_EQ))}}; + TheImage.Image = std::move(*FileOrErr); + + Images[Kind].emplace_back(std::move(TheImage)); + } + return Error::success(); + }); + if (Err) + return std::move(Err); + + // Create a binary image of each offloading image and embed it into a new + // object file. + SmallVector<StringRef> WrappedOutput; + for (auto &[Kind, Input] : Images) { + // We sort the entries before bundling so they appear in a deterministic + // order in the final binary. + llvm::sort(Input, [](OffloadingImage &A, OffloadingImage &B) { + return A.StringData["triple"] > B.StringData["triple"] || + A.StringData["arch"] > B.StringData["arch"] || + A.TheOffloadKind < B.TheOffloadKind; + }); + auto BundledImagesOrErr = bundleLinkedOutput(Input, Args, Kind); + if (!BundledImagesOrErr) + return BundledImagesOrErr.takeError(); + auto OutputOrErr = wrapDeviceImages(*BundledImagesOrErr, Args, Kind); + if (!OutputOrErr) + return OutputOrErr.takeError(); + WrappedOutput.push_back(*OutputOrErr); + } + + return WrappedOutput; +} + +std::optional<std::string> findFile(StringRef Dir, StringRef Root, + const Twine &Name) { + SmallString<128> Path; + if (Dir.startswith("=")) + sys::path::append(Path, Root, Dir.substr(1), Name); + else + sys::path::append(Path, Dir, Name); + + if (sys::fs::exists(Path)) + return static_cast<std::string>(Path); + return std::nullopt; +} + +std::optional<std::string> +findFromSearchPaths(StringRef Name, StringRef Root, + ArrayRef<StringRef> SearchPaths) { + for (StringRef Dir : SearchPaths) + if (std::optional<std::string> File = findFile(Dir, Root, Name)) + return File; + return std::nullopt; +} + +std::optional<std::string> +searchLibraryBaseName(StringRef Name, StringRef Root, + ArrayRef<StringRef> SearchPaths) { + for (StringRef Dir : SearchPaths) { + if (std::optional<std::string> File = + findFile(Dir, Root, "lib" + Name + ".so")) + return File; + if (std::optional<std::string> File = + findFile(Dir, Root, "lib" + Name + ".a")) + return File; + } + return std::nullopt; +} + +/// Search for static libraries in the linker's library path given input like +/// `-lfoo` or `-l:libfoo.a`. +std::optional<std::string> searchLibrary(StringRef Input, StringRef Root, + ArrayRef<StringRef> SearchPaths) { + if (Input.startswith(":")) + return findFromSearchPaths(Input.drop_front(), Root, SearchPaths); + return searchLibraryBaseName(Input, Root, SearchPaths); +} + +/// Common redeclaration of needed symbol flags. +enum Symbol : uint32_t { + Sym_None = 0, + Sym_Undefined = 1U << 1, + Sym_Weak = 1U << 2, +}; + +/// Scan the symbols from a BitcodeFile \p Buffer and record if we need to +/// extract any symbols from it. +Expected<bool> getSymbolsFromBitcode(MemoryBufferRef Buffer, StringSaver &Saver, + DenseMap<StringRef, Symbol> &Syms) { + Expected<IRSymtabFile> IRSymtabOrErr = readIRSymtab(Buffer); + if (!IRSymtabOrErr) + return IRSymtabOrErr.takeError(); + + bool ShouldExtract = false; + for (unsigned I = 0; I != IRSymtabOrErr->Mods.size(); ++I) { + for (const auto &Sym : IRSymtabOrErr->TheReader.module_symbols(I)) { + if (Sym.isFormatSpecific() || !Sym.isGlobal()) + continue; + + bool NewSymbol = Syms.count(Sym.getName()) == 0; + auto &OldSym = Syms[Saver.save(Sym.getName())]; + + // We will extract if it defines a currenlty undefined non-weak symbol. + bool ResolvesStrongReference = + ((OldSym & Sym_Undefined && !(OldSym & Sym_Weak)) && + !Sym.isUndefined()); + // We will extract if it defines a new global symbol visible to the host. + bool NewGlobalSymbol = + ((NewSymbol || (OldSym & Sym_Undefined)) && !Sym.isUndefined() && + !Sym.canBeOmittedFromSymbolTable() && + (Sym.getVisibility() != GlobalValue::HiddenVisibility)); + ShouldExtract |= ResolvesStrongReference | NewGlobalSymbol; + + // Update this symbol in the "table" with the new information. + if (OldSym & Sym_Undefined && !Sym.isUndefined()) + OldSym = static_cast<Symbol>(OldSym & ~Sym_Undefined); + if (Sym.isUndefined() && NewSymbol) + OldSym = static_cast<Symbol>(OldSym | Sym_Undefined); + if (Sym.isWeak()) + OldSym = static_cast<Symbol>(OldSym | Sym_Weak); + } + } + + return ShouldExtract; +} + +/// Scan the symbols from an ObjectFile \p Obj and record if we need to extract +/// any symbols from it. +Expected<bool> getSymbolsFromObject(const ObjectFile &Obj, StringSaver &Saver, + DenseMap<StringRef, Symbol> &Syms) { + bool ShouldExtract = false; + for (SymbolRef Sym : Obj.symbols()) { + auto FlagsOrErr = Sym.getFlags(); + if (!FlagsOrErr) + return FlagsOrErr.takeError(); + + if (!(*FlagsOrErr & SymbolRef::SF_Global) || + (*FlagsOrErr & SymbolRef::SF_FormatSpecific)) + continue; + + auto NameOrErr = Sym.getName(); + if (!NameOrErr) + return NameOrErr.takeError(); + + bool NewSymbol = Syms.count(*NameOrErr) == 0; + auto &OldSym = Syms[Saver.save(*NameOrErr)]; + + // We will extract if it defines a currenlty undefined non-weak symbol. + bool ResolvesStrongReference = (OldSym & Sym_Undefined) && + !(OldSym & Sym_Weak) && + !(*FlagsOrErr & SymbolRef::SF_Undefined); + + // We will extract if it defines a new global symbol visible to the host. + bool NewGlobalSymbol = ((NewSymbol || (OldSym & Sym_Undefined)) && + !(*FlagsOrErr & SymbolRef::SF_Undefined) && + !(*FlagsOrErr & SymbolRef::SF_Hidden)); + ShouldExtract |= ResolvesStrongReference | NewGlobalSymbol; + + // Update this symbol in the "table" with the new information. + if (OldSym & Sym_Undefined && !(*FlagsOrErr & SymbolRef::SF_Undefined)) + OldSym = static_cast<Symbol>(OldSym & ~Sym_Undefined); + if (*FlagsOrErr & SymbolRef::SF_Undefined && NewSymbol) + OldSym = static_cast<Symbol>(OldSym | Sym_Undefined); + if (*FlagsOrErr & SymbolRef::SF_Weak) + OldSym = static_cast<Symbol>(OldSym | Sym_Weak); + } + return ShouldExtract; +} + +/// Attempt to 'resolve' symbols found in input files. We use this to +/// determine if an archive member needs to be extracted. An archive member +/// will be extracted if any of the following is true. +/// 1) It defines an undefined symbol in a regular object filie. +/// 2) It defines a global symbol without hidden visibility that has not +/// yet been defined. +Expected<bool> getSymbols(StringRef Image, StringSaver &Saver, + DenseMap<StringRef, Symbol> &Syms) { + MemoryBufferRef Buffer = MemoryBufferRef(Image, ""); + switch (identify_magic(Image)) { + case file_magic::bitcode: + return getSymbolsFromBitcode(Buffer, Saver, Syms); + case file_magic::elf_relocatable: { + Expected<std::unique_ptr<ObjectFile>> ObjFile = + ObjectFile::createObjectFile(Buffer); + if (!ObjFile) + return ObjFile.takeError(); + return getSymbolsFromObject(**ObjFile, Saver, Syms); + } + default: + return false; + } +} + +/// Search the input files and libraries for embedded device offloading code +/// and add it to the list of files to be linked. Files coming from static +/// libraries are only added to the input if they are used by an existing +/// input file. +Expected<SmallVector<OffloadFile>> getDeviceInput(const ArgList &Args) { + llvm::TimeTraceScope TimeScope("ExtractDeviceCode"); + + StringRef Root = Args.getLastArgValue(OPT_sysroot_EQ); + SmallVector<StringRef> LibraryPaths; + for (const opt::Arg *Arg : Args.filtered(OPT_library_path)) + LibraryPaths.push_back(Arg->getValue()); + + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + + // Try to extract device code from the linker input files. + SmallVector<OffloadFile> InputFiles; + DenseMap<OffloadFile::TargetID, DenseMap<StringRef, Symbol>> Syms; + for (const opt::Arg *Arg : Args.filtered(OPT_INPUT, OPT_library)) { + std::optional<std::string> Filename = + Arg->getOption().matches(OPT_library) + ? searchLibrary(Arg->getValue(), Root, LibraryPaths) + : std::string(Arg->getValue()); + + if (!Filename && Arg->getOption().matches(OPT_library)) + reportError(createStringError(inconvertibleErrorCode(), + "unable to find library -l%s", + Arg->getValue())); + + if (!Filename || !sys::fs::exists(*Filename) || + sys::fs::is_directory(*Filename)) + continue; + + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = + MemoryBuffer::getFileOrSTDIN(*Filename); + if (std::error_code EC = BufferOrErr.getError()) + return createFileError(*Filename, EC); + + MemoryBufferRef Buffer = **BufferOrErr; + if (identify_magic(Buffer.getBuffer()) == file_magic::elf_shared_object) + continue; + + SmallVector<OffloadFile> Binaries; + if (Error Err = extractOffloadBinaries(Buffer, Binaries)) + return std::move(Err); + + // We only extract archive members that are needed. + bool IsArchive = identify_magic(Buffer.getBuffer()) == file_magic::archive; + bool Extracted = true; + while (Extracted) { + Extracted = false; + for (OffloadFile &Binary : Binaries) { + if (!Binary.getBinary()) + continue; + + // If we don't have an object file for this architecture do not + // extract. + if (IsArchive && !Syms.count(Binary)) + continue; + + Expected<bool> ExtractOrErr = + getSymbols(Binary.getBinary()->getImage(), Saver, Syms[Binary]); + if (!ExtractOrErr) + return ExtractOrErr.takeError(); + + Extracted = IsArchive && *ExtractOrErr; + + if (!IsArchive || Extracted) + InputFiles.emplace_back(std::move(Binary)); + + // If we extracted any files we need to check all the symbols again. + if (Extracted) + break; + } + } + } + + for (StringRef Library : Args.getAllArgValues(OPT_bitcode_library_EQ)) { + auto FileOrErr = getInputBitcodeLibrary(Library); + if (!FileOrErr) + return FileOrErr.takeError(); + InputFiles.push_back(std::move(*FileOrErr)); + } + + return std::move(InputFiles); +} + +} // namespace + +int main(int Argc, char **Argv) { + InitLLVM X(Argc, Argv); + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); + InitializeAllAsmPrinters(); + + LinkerExecutable = Argv[0]; + sys::PrintStackTraceOnErrorSignal(Argv[0]); + + const OptTable &Tbl = getOptTable(); + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + auto Args = Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [&](StringRef Err) { + reportError(createStringError(inconvertibleErrorCode(), Err)); + }); + + if (Args.hasArg(OPT_help) || Args.hasArg(OPT_help_hidden)) { + Tbl.printHelp( + outs(), + "clang-linker-wrapper [options] -- <options to passed to the linker>", + "\nA wrapper utility over the host linker. It scans the input files\n" + "for sections that require additional processing prior to linking.\n" + "The will then transparently pass all arguments and input to the\n" + "specified host linker to create the final binary.\n", + Args.hasArg(OPT_help_hidden), Args.hasArg(OPT_help_hidden)); + return EXIT_SUCCESS; + } + if (Args.hasArg(OPT_v)) { + printVersion(outs()); + return EXIT_SUCCESS; + } + + // This forwards '-mllvm' arguments to LLVM if present. + SmallVector<const char *> NewArgv = {Argv[0]}; + for (const opt::Arg *Arg : Args.filtered(OPT_mllvm)) + NewArgv.push_back(Arg->getValue()); + for (const opt::Arg *Arg : Args.filtered(OPT_offload_opt_eq_minus)) + NewArgv.push_back(Args.MakeArgString(StringRef("-") + Arg->getValue())); + cl::ParseCommandLineOptions(NewArgv.size(), &NewArgv[0]); + + Verbose = Args.hasArg(OPT_verbose); + DryRun = Args.hasArg(OPT_dry_run); + SaveTemps = Args.hasArg(OPT_save_temps); + ExecutableName = Args.getLastArgValue(OPT_o, "a.out"); + CudaBinaryPath = Args.getLastArgValue(OPT_cuda_path_EQ).str(); + + parallel::strategy = hardware_concurrency(1); + if (auto *Arg = Args.getLastArg(OPT_wrapper_jobs)) { + unsigned Threads = 0; + if (!llvm::to_integer(Arg->getValue(), Threads) || Threads == 0) + reportError(createStringError( + inconvertibleErrorCode(), "%s: expected a positive integer, got '%s'", + Arg->getSpelling().data(), Arg->getValue())); + parallel::strategy = hardware_concurrency(Threads); + } + + if (Args.hasArg(OPT_wrapper_time_trace_eq)) { + unsigned Granularity; + Args.getLastArgValue(OPT_wrapper_time_trace_granularity, "500") + .getAsInteger(10, Granularity); + timeTraceProfilerInitialize(Granularity, Argv[0]); + } + + { + llvm::TimeTraceScope TimeScope("Execute linker wrapper"); + + // Extract the device input files stored in the host fat binary. + auto DeviceInputFiles = getDeviceInput(Args); + if (!DeviceInputFiles) + reportError(DeviceInputFiles.takeError()); + + // Link and wrap the device images extracted from the linker input. + auto FilesOrErr = + linkAndWrapDeviceFiles(*DeviceInputFiles, Args, Argv, Argc); + if (!FilesOrErr) + reportError(FilesOrErr.takeError()); + + // Run the host linking job with the rendered arguments. + if (Error Err = runLinker(*FilesOrErr, Args)) + reportError(std::move(Err)); + } + + if (const opt::Arg *Arg = Args.getLastArg(OPT_wrapper_time_trace_eq)) { + if (Error Err = timeTraceProfilerWrite(Arg->getValue(), ExecutableName)) + reportError(std::move(Err)); + timeTraceProfilerCleanup(); + } + + // Remove the temporary files created. + if (!SaveTemps) + for (const auto &TempFile : TempFiles) + if (std::error_code EC = sys::fs::remove(TempFile)) + reportError(createFileError(TempFile, EC)); + + return EXIT_SUCCESS; +} diff --git a/gnu/llvm/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/gnu/llvm/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td new file mode 100644 index 00000000000..c12aac77c3b --- /dev/null +++ b/gnu/llvm/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td @@ -0,0 +1,124 @@ +include "llvm/Option/OptParser.td" + +def WrapperOnlyOption : OptionFlag; +def DeviceOnlyOption : OptionFlag; + +def help : Flag<["--"], "help">, + HelpText<"Display available options (--help-hidden for more)">; + +def help_hidden : Flag<["--"], "help-hidden">, + HelpText<"Display all available options">; + +// Flags for the linker wrapper. +def linker_path_EQ : Joined<["--"], "linker-path=">, + Flags<[WrapperOnlyOption]>, MetaVarName<"<path>">, + HelpText<"The linker executable to invoke">; +def cuda_path_EQ : Joined<["--"], "cuda-path=">, + Flags<[WrapperOnlyOption]>, MetaVarName<"<dir>">, + HelpText<"Set the system CUDA path">; +def host_triple_EQ : Joined<["--"], "host-triple=">, + Flags<[WrapperOnlyOption]>, MetaVarName<"<triple>">, + HelpText<"Triple to use for the host compilation">; +def opt_level : Joined<["--"], "opt-level=">, + Flags<[WrapperOnlyOption]>, MetaVarName<"<O0, O1, O2, or O3>">, + HelpText<"Optimization level for LTO">; +def bitcode_library_EQ : Joined<["--"], "bitcode-library=">, + Flags<[WrapperOnlyOption]>, MetaVarName<"<kind>-<triple>-<arch>=<path>">, + HelpText<"Extra bitcode library to link">; +def device_linker_args_EQ : Joined<["--"], "device-linker=">, + Flags<[WrapperOnlyOption]>, MetaVarName<"<value> or <triple>=<value>">, + HelpText<"Arguments to pass to the device linker invocation">; +def dry_run : Flag<["--"], "dry-run">, + Flags<[WrapperOnlyOption]>, + HelpText<"Print program arguments without running">; +def verbose : Flag<["--"], "wrapper-verbose">, + Flags<[WrapperOnlyOption]>, HelpText<"Verbose output from tools">; +def embed_bitcode : Flag<["--"], "embed-bitcode">, + Flags<[WrapperOnlyOption]>, HelpText<"Embed linked bitcode in the module">; +def debug : Flag<["--"], "device-debug">, Flags<[WrapperOnlyOption]>, + HelpText<"Use debugging">; +def ptxas_arg : Joined<["--"], "ptxas-arg=">, + Flags<[WrapperOnlyOption]>, + HelpText<"Argument to pass to the 'ptxas' invocation">; +def pass_remarks_EQ : Joined<["--"], "pass-remarks=">, + Flags<[WrapperOnlyOption]>, HelpText<"Pass remarks for LTO">; +def pass_remarks_missed_EQ : Joined<["--"], "pass-remarks-missed=">, + Flags<[WrapperOnlyOption]>, HelpText<"Pass remarks for LTO">; +def pass_remarks_analysis_EQ : Joined<["--"], "pass-remarks-analysis=">, + Flags<[WrapperOnlyOption]>, HelpText<"Pass remarks for LTO">; +def print_wrapped_module : Flag<["--"], "print-wrapped-module">, + Flags<[WrapperOnlyOption]>, + HelpText<"Print the wrapped module's IR for testing">; +def save_temps : Flag<["--"], "save-temps">, + Flags<[WrapperOnlyOption]>, HelpText<"Save intermediate results">; + +def wrapper_time_trace_eq : Joined<["--"], "wrapper-time-trace=">, + Flags<[WrapperOnlyOption]>, MetaVarName<"<file>">, + HelpText<"Enable time-trace and write the output to <file>">; +def wrapper_time_trace_granularity : Joined<["--"], "wrapper-time-trace-granularity=">, + Flags<[WrapperOnlyOption]>, MetaVarName<"<number>">, + HelpText<"Set the granularity of time-trace updates">; + +def wrapper_jobs : Joined<["--"], "wrapper-jobs=">, + Flags<[WrapperOnlyOption]>, MetaVarName<"<number>">, + HelpText<"Sets the number of parallel jobs to use for device linking">; + +// Flags passed to the device linker. +def arch_EQ : Joined<["--"], "arch=">, + Flags<[DeviceOnlyOption, HelpHidden]>, MetaVarName<"<arch>">, + HelpText<"The device subarchitecture">; +def triple_EQ : Joined<["--"], "triple=">, + Flags<[DeviceOnlyOption, HelpHidden]>, MetaVarName<"<triple>">, + HelpText<"The device target triple">; +def whole_program : Flag<["--"], "whole-program">, + Flags<[DeviceOnlyOption, HelpHidden]>, + HelpText<"LTO has visibility of all input files">; +def linker_arg_EQ : Joined<["--"], "linker-arg=">, + Flags<[DeviceOnlyOption, HelpHidden]>, + HelpText<"An extra argument to be passed to the linker">; + +// Separator between the linker wrapper and host linker flags. +def separator : Flag<["--"], "">, Flags<[WrapperOnlyOption]>, + HelpText<"The separator for the wrapped linker arguments">; + +// Arguments for the LLVM backend. +def mllvm : Separate<["-"], "mllvm">, Flags<[WrapperOnlyOption]>, + MetaVarName<"<arg>">, HelpText<"Arguments passed to the LLVM invocation">; +def offload_opt_eq_minus : Joined<["--", "-"], "offload-opt=-">, Flags<[HelpHidden, WrapperOnlyOption]>, + HelpText<"Options passed to LLVM">; + +// Standard linker flags also used by the linker wrapper. +def sysroot_EQ : Joined<["--"], "sysroot">, HelpText<"Set the system root">; + +def o : JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">, + HelpText<"Path to file to write output">; +def output_EQ : Joined<["--"], "output=">, Alias<o>, Flags<[HelpHidden]>, + HelpText<"Alias for -o">; +def output : Separate<["--"], "output">, Alias<o>, Flags<[HelpHidden]>, + HelpText<"Alias for -o">; + +def library_path : JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">, + HelpText<"Add <dir> to the library search path">; +def library_path_S : Separate<["--", "-"], "library-path">, Flags<[HelpHidden]>, + Alias<library_path>; +def library_path_EQ : Joined<["--", "-"], "library-path=">, Flags<[HelpHidden]>, + Alias<library_path>; + +def library : JoinedOrSeparate<["-"], "l">, MetaVarName<"<libname>">, + HelpText<"Search for library <libname>">; +def library_S : Separate<["--", "-"], "library">, Flags<[HelpHidden]>, + Alias<library_path>; +def library_EQ : Joined<["--", "-"], "library=">, Flags<[HelpHidden]>, + Alias<library_path>; + +def as_needed : Flag<["--", "-"], "as-needed">; +def no_as_needed : Flag<["--", "-"], "no-as-needed">; + +def rpath : Separate<["--", "-"], "rpath">; +def rpath_EQ : Joined<["--", "-"], "rpath=">, Flags<[HelpHidden]>, Alias<rpath>; + +def dynamic_linker : Separate<["--", "-"], "dynamic-linker">; +def dynamic_linker_EQ : Joined<["--", "-"], "dynamic-linker=">, Alias<dynamic_linker>; + +def v : Flag<["--", "-"], "v">, HelpText<"Display the version number and exit">; +def version : Flag<["--", "-"], "version">, Flags<[HelpHidden]>, Alias<v>; diff --git a/gnu/llvm/clang/tools/clang-linker-wrapper/OffloadWrapper.cpp b/gnu/llvm/clang/tools/clang-linker-wrapper/OffloadWrapper.cpp new file mode 100644 index 00000000000..dbfd896362c --- /dev/null +++ b/gnu/llvm/clang/tools/clang-linker-wrapper/OffloadWrapper.cpp @@ -0,0 +1,622 @@ +//===- OffloadWrapper.cpp ---------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "OffloadWrapper.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Object/OffloadBinary.h" +#include "llvm/Support/Error.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +using namespace llvm; + +namespace { +/// Magic number that begins the section containing the CUDA fatbinary. +constexpr unsigned CudaFatMagic = 0x466243b1; +constexpr unsigned HIPFatMagic = 0x48495046; + +/// Copied from clang/CGCudaRuntime.h. +enum OffloadEntryKindFlag : uint32_t { + /// Mark the entry as a global entry. This indicates the presense of a + /// kernel if the size size field is zero and a variable otherwise. + OffloadGlobalEntry = 0x0, + /// Mark the entry as a managed global variable. + OffloadGlobalManagedEntry = 0x1, + /// Mark the entry as a surface variable. + OffloadGlobalSurfaceEntry = 0x2, + /// Mark the entry as a texture variable. + OffloadGlobalTextureEntry = 0x3, +}; + +IntegerType *getSizeTTy(Module &M) { + LLVMContext &C = M.getContext(); + switch (M.getDataLayout().getPointerTypeSize(Type::getInt8PtrTy(C))) { + case 4u: + return Type::getInt32Ty(C); + case 8u: + return Type::getInt64Ty(C); + } + llvm_unreachable("unsupported pointer type size"); +} + +// struct __tgt_offload_entry { +// void *addr; +// char *name; +// size_t size; +// int32_t flags; +// int32_t reserved; +// }; +StructType *getEntryTy(Module &M) { + LLVMContext &C = M.getContext(); + StructType *EntryTy = StructType::getTypeByName(C, "__tgt_offload_entry"); + if (!EntryTy) + EntryTy = StructType::create("__tgt_offload_entry", Type::getInt8PtrTy(C), + Type::getInt8PtrTy(C), getSizeTTy(M), + Type::getInt32Ty(C), Type::getInt32Ty(C)); + return EntryTy; +} + +PointerType *getEntryPtrTy(Module &M) { + return PointerType::getUnqual(getEntryTy(M)); +} + +// struct __tgt_device_image { +// void *ImageStart; +// void *ImageEnd; +// __tgt_offload_entry *EntriesBegin; +// __tgt_offload_entry *EntriesEnd; +// }; +StructType *getDeviceImageTy(Module &M) { + LLVMContext &C = M.getContext(); + StructType *ImageTy = StructType::getTypeByName(C, "__tgt_device_image"); + if (!ImageTy) + ImageTy = StructType::create("__tgt_device_image", Type::getInt8PtrTy(C), + Type::getInt8PtrTy(C), getEntryPtrTy(M), + getEntryPtrTy(M)); + return ImageTy; +} + +PointerType *getDeviceImagePtrTy(Module &M) { + return PointerType::getUnqual(getDeviceImageTy(M)); +} + +// struct __tgt_bin_desc { +// int32_t NumDeviceImages; +// __tgt_device_image *DeviceImages; +// __tgt_offload_entry *HostEntriesBegin; +// __tgt_offload_entry *HostEntriesEnd; +// }; +StructType *getBinDescTy(Module &M) { + LLVMContext &C = M.getContext(); + StructType *DescTy = StructType::getTypeByName(C, "__tgt_bin_desc"); + if (!DescTy) + DescTy = StructType::create("__tgt_bin_desc", Type::getInt32Ty(C), + getDeviceImagePtrTy(M), getEntryPtrTy(M), + getEntryPtrTy(M)); + return DescTy; +} + +PointerType *getBinDescPtrTy(Module &M) { + return PointerType::getUnqual(getBinDescTy(M)); +} + +/// Creates binary descriptor for the given device images. Binary descriptor +/// is an object that is passed to the offloading runtime at program startup +/// and it describes all device images available in the executable or shared +/// library. It is defined as follows +/// +/// __attribute__((visibility("hidden"))) +/// extern __tgt_offload_entry *__start_omp_offloading_entries; +/// __attribute__((visibility("hidden"))) +/// extern __tgt_offload_entry *__stop_omp_offloading_entries; +/// +/// static const char Image0[] = { <Bufs.front() contents> }; +/// ... +/// static const char ImageN[] = { <Bufs.back() contents> }; +/// +/// static const __tgt_device_image Images[] = { +/// { +/// Image0, /*ImageStart*/ +/// Image0 + sizeof(Image0), /*ImageEnd*/ +/// __start_omp_offloading_entries, /*EntriesBegin*/ +/// __stop_omp_offloading_entries /*EntriesEnd*/ +/// }, +/// ... +/// { +/// ImageN, /*ImageStart*/ +/// ImageN + sizeof(ImageN), /*ImageEnd*/ +/// __start_omp_offloading_entries, /*EntriesBegin*/ +/// __stop_omp_offloading_entries /*EntriesEnd*/ +/// } +/// }; +/// +/// static const __tgt_bin_desc BinDesc = { +/// sizeof(Images) / sizeof(Images[0]), /*NumDeviceImages*/ +/// Images, /*DeviceImages*/ +/// __start_omp_offloading_entries, /*HostEntriesBegin*/ +/// __stop_omp_offloading_entries /*HostEntriesEnd*/ +/// }; +/// +/// Global variable that represents BinDesc is returned. +GlobalVariable *createBinDesc(Module &M, ArrayRef<ArrayRef<char>> Bufs) { + LLVMContext &C = M.getContext(); + // Create external begin/end symbols for the offload entries table. + auto *EntriesB = new GlobalVariable( + M, getEntryTy(M), /*isConstant*/ true, GlobalValue::ExternalLinkage, + /*Initializer*/ nullptr, "__start_omp_offloading_entries"); + EntriesB->setVisibility(GlobalValue::HiddenVisibility); + auto *EntriesE = new GlobalVariable( + M, getEntryTy(M), /*isConstant*/ true, GlobalValue::ExternalLinkage, + /*Initializer*/ nullptr, "__stop_omp_offloading_entries"); + EntriesE->setVisibility(GlobalValue::HiddenVisibility); + + // We assume that external begin/end symbols that we have created above will + // be defined by the linker. But linker will do that only if linker inputs + // have section with "omp_offloading_entries" name which is not guaranteed. + // So, we just create dummy zero sized object in the offload entries section + // to force linker to define those symbols. + auto *DummyInit = + ConstantAggregateZero::get(ArrayType::get(getEntryTy(M), 0u)); + auto *DummyEntry = new GlobalVariable( + M, DummyInit->getType(), true, GlobalVariable::ExternalLinkage, DummyInit, + "__dummy.omp_offloading.entry"); + DummyEntry->setSection("omp_offloading_entries"); + DummyEntry->setVisibility(GlobalValue::HiddenVisibility); + + auto *Zero = ConstantInt::get(getSizeTTy(M), 0u); + Constant *ZeroZero[] = {Zero, Zero}; + + // Create initializer for the images array. + SmallVector<Constant *, 4u> ImagesInits; + ImagesInits.reserve(Bufs.size()); + for (ArrayRef<char> Buf : Bufs) { + auto *Data = ConstantDataArray::get(C, Buf); + auto *Image = new GlobalVariable(M, Data->getType(), /*isConstant*/ true, + GlobalVariable::InternalLinkage, Data, + ".omp_offloading.device_image"); + Image->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + Image->setSection(".llvm.offloading"); + Image->setAlignment(Align(object::OffloadBinary::getAlignment())); + + auto *Size = ConstantInt::get(getSizeTTy(M), Buf.size()); + Constant *ZeroSize[] = {Zero, Size}; + + auto *ImageB = + ConstantExpr::getGetElementPtr(Image->getValueType(), Image, ZeroZero); + auto *ImageE = + ConstantExpr::getGetElementPtr(Image->getValueType(), Image, ZeroSize); + + ImagesInits.push_back(ConstantStruct::get(getDeviceImageTy(M), ImageB, + ImageE, EntriesB, EntriesE)); + } + + // Then create images array. + auto *ImagesData = ConstantArray::get( + ArrayType::get(getDeviceImageTy(M), ImagesInits.size()), ImagesInits); + + auto *Images = + new GlobalVariable(M, ImagesData->getType(), /*isConstant*/ true, + GlobalValue::InternalLinkage, ImagesData, + ".omp_offloading.device_images"); + Images->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + + auto *ImagesB = + ConstantExpr::getGetElementPtr(Images->getValueType(), Images, ZeroZero); + + // And finally create the binary descriptor object. + auto *DescInit = ConstantStruct::get( + getBinDescTy(M), + ConstantInt::get(Type::getInt32Ty(C), ImagesInits.size()), ImagesB, + EntriesB, EntriesE); + + return new GlobalVariable(M, DescInit->getType(), /*isConstant*/ true, + GlobalValue::InternalLinkage, DescInit, + ".omp_offloading.descriptor"); +} + +void createRegisterFunction(Module &M, GlobalVariable *BinDesc) { + LLVMContext &C = M.getContext(); + auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false); + auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage, + ".omp_offloading.descriptor_reg", &M); + Func->setSection(".text.startup"); + + // Get __tgt_register_lib function declaration. + auto *RegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(M), + /*isVarArg*/ false); + FunctionCallee RegFuncC = + M.getOrInsertFunction("__tgt_register_lib", RegFuncTy); + + // Construct function body + IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func)); + Builder.CreateCall(RegFuncC, BinDesc); + Builder.CreateRetVoid(); + + // Add this function to constructors. + // Set priority to 1 so that __tgt_register_lib is executed AFTER + // __tgt_register_requires (we want to know what requirements have been + // asked for before we load a libomptarget plugin so that by the time the + // plugin is loaded it can report how many devices there are which can + // satisfy these requirements). + appendToGlobalCtors(M, Func, /*Priority*/ 1); +} + +void createUnregisterFunction(Module &M, GlobalVariable *BinDesc) { + LLVMContext &C = M.getContext(); + auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false); + auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage, + ".omp_offloading.descriptor_unreg", &M); + Func->setSection(".text.startup"); + + // Get __tgt_unregister_lib function declaration. + auto *UnRegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(M), + /*isVarArg*/ false); + FunctionCallee UnRegFuncC = + M.getOrInsertFunction("__tgt_unregister_lib", UnRegFuncTy); + + // Construct function body + IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func)); + Builder.CreateCall(UnRegFuncC, BinDesc); + Builder.CreateRetVoid(); + + // Add this function to global destructors. + // Match priority of __tgt_register_lib + appendToGlobalDtors(M, Func, /*Priority*/ 1); +} + +// struct fatbin_wrapper { +// int32_t magic; +// int32_t version; +// void *image; +// void *reserved; +//}; +StructType *getFatbinWrapperTy(Module &M) { + LLVMContext &C = M.getContext(); + StructType *FatbinTy = StructType::getTypeByName(C, "fatbin_wrapper"); + if (!FatbinTy) + FatbinTy = StructType::create("fatbin_wrapper", Type::getInt32Ty(C), + Type::getInt32Ty(C), Type::getInt8PtrTy(C), + Type::getInt8PtrTy(C)); + return FatbinTy; +} + +/// Embed the image \p Image into the module \p M so it can be found by the +/// runtime. +GlobalVariable *createFatbinDesc(Module &M, ArrayRef<char> Image, bool IsHIP) { + LLVMContext &C = M.getContext(); + llvm::Type *Int8PtrTy = Type::getInt8PtrTy(C); + llvm::Triple Triple = llvm::Triple(M.getTargetTriple()); + + // Create the global string containing the fatbinary. + StringRef FatbinConstantSection = + IsHIP ? ".hip_fatbin" + : (Triple.isMacOSX() ? "__NV_CUDA,__nv_fatbin" : ".nv_fatbin"); + auto *Data = ConstantDataArray::get(C, Image); + auto *Fatbin = new GlobalVariable(M, Data->getType(), /*isConstant*/ true, + GlobalVariable::InternalLinkage, Data, + ".fatbin_image"); + Fatbin->setSection(FatbinConstantSection); + + // Create the fatbinary wrapper + StringRef FatbinWrapperSection = IsHIP ? ".hipFatBinSegment" + : Triple.isMacOSX() ? "__NV_CUDA,__fatbin" + : ".nvFatBinSegment"; + Constant *FatbinWrapper[] = { + ConstantInt::get(Type::getInt32Ty(C), IsHIP ? HIPFatMagic : CudaFatMagic), + ConstantInt::get(Type::getInt32Ty(C), 1), + ConstantExpr::getPointerBitCastOrAddrSpaceCast(Fatbin, Int8PtrTy), + ConstantPointerNull::get(Type::getInt8PtrTy(C))}; + + Constant *FatbinInitializer = + ConstantStruct::get(getFatbinWrapperTy(M), FatbinWrapper); + + auto *FatbinDesc = + new GlobalVariable(M, getFatbinWrapperTy(M), + /*isConstant*/ true, GlobalValue::InternalLinkage, + FatbinInitializer, ".fatbin_wrapper"); + FatbinDesc->setSection(FatbinWrapperSection); + FatbinDesc->setAlignment(Align(8)); + + // We create a dummy entry to ensure the linker will define the begin / end + // symbols. The CUDA runtime should ignore the null address if we attempt to + // register it. + auto *DummyInit = + ConstantAggregateZero::get(ArrayType::get(getEntryTy(M), 0u)); + auto *DummyEntry = new GlobalVariable( + M, DummyInit->getType(), true, GlobalVariable::ExternalLinkage, DummyInit, + IsHIP ? "__dummy.hip_offloading.entry" : "__dummy.cuda_offloading.entry"); + DummyEntry->setVisibility(GlobalValue::HiddenVisibility); + DummyEntry->setSection(IsHIP ? "hip_offloading_entries" + : "cuda_offloading_entries"); + + return FatbinDesc; +} + +/// Create the register globals function. We will iterate all of the offloading +/// entries stored at the begin / end symbols and register them according to +/// their type. This creates the following function in IR: +/// +/// extern struct __tgt_offload_entry __start_cuda_offloading_entries; +/// extern struct __tgt_offload_entry __stop_cuda_offloading_entries; +/// +/// extern void __cudaRegisterFunction(void **, void *, void *, void *, int, +/// void *, void *, void *, void *, int *); +/// extern void __cudaRegisterVar(void **, void *, void *, void *, int32_t, +/// int64_t, int32_t, int32_t); +/// +/// void __cudaRegisterTest(void **fatbinHandle) { +/// for (struct __tgt_offload_entry *entry = &__start_cuda_offloading_entries; +/// entry != &__stop_cuda_offloading_entries; ++entry) { +/// if (!entry->size) +/// __cudaRegisterFunction(fatbinHandle, entry->addr, entry->name, +/// entry->name, -1, 0, 0, 0, 0, 0); +/// else +/// __cudaRegisterVar(fatbinHandle, entry->addr, entry->name, entry->name, +/// 0, entry->size, 0, 0); +/// } +/// } +Function *createRegisterGlobalsFunction(Module &M, bool IsHIP) { + LLVMContext &C = M.getContext(); + // Get the __cudaRegisterFunction function declaration. + auto *RegFuncTy = FunctionType::get( + Type::getInt32Ty(C), + {Type::getInt8PtrTy(C)->getPointerTo(), Type::getInt8PtrTy(C), + Type::getInt8PtrTy(C), Type::getInt8PtrTy(C), Type::getInt32Ty(C), + Type::getInt8PtrTy(C), Type::getInt8PtrTy(C), Type::getInt8PtrTy(C), + Type::getInt8PtrTy(C), Type::getInt32PtrTy(C)}, + /*isVarArg*/ false); + FunctionCallee RegFunc = M.getOrInsertFunction( + IsHIP ? "__hipRegisterFunction" : "__cudaRegisterFunction", RegFuncTy); + + // Get the __cudaRegisterVar function declaration. + auto *RegVarTy = FunctionType::get( + Type::getVoidTy(C), + {Type::getInt8PtrTy(C)->getPointerTo(), Type::getInt8PtrTy(C), + Type::getInt8PtrTy(C), Type::getInt8PtrTy(C), Type::getInt32Ty(C), + getSizeTTy(M), Type::getInt32Ty(C), Type::getInt32Ty(C)}, + /*isVarArg*/ false); + FunctionCallee RegVar = M.getOrInsertFunction( + IsHIP ? "__hipRegisterVar" : "__cudaRegisterVar", RegVarTy); + + // Create the references to the start / stop symbols defined by the linker. + auto *EntriesB = + new GlobalVariable(M, ArrayType::get(getEntryTy(M), 0), + /*isConstant*/ true, GlobalValue::ExternalLinkage, + /*Initializer*/ nullptr, + IsHIP ? "__start_hip_offloading_entries" + : "__start_cuda_offloading_entries"); + EntriesB->setVisibility(GlobalValue::HiddenVisibility); + auto *EntriesE = + new GlobalVariable(M, ArrayType::get(getEntryTy(M), 0), + /*isConstant*/ true, GlobalValue::ExternalLinkage, + /*Initializer*/ nullptr, + IsHIP ? "__stop_hip_offloading_entries" + : "__stop_cuda_offloading_entries"); + EntriesE->setVisibility(GlobalValue::HiddenVisibility); + + auto *RegGlobalsTy = FunctionType::get(Type::getVoidTy(C), + Type::getInt8PtrTy(C)->getPointerTo(), + /*isVarArg*/ false); + auto *RegGlobalsFn = + Function::Create(RegGlobalsTy, GlobalValue::InternalLinkage, + IsHIP ? ".hip.globals_reg" : ".cuda.globals_reg", &M); + RegGlobalsFn->setSection(".text.startup"); + + // Create the loop to register all the entries. + IRBuilder<> Builder(BasicBlock::Create(C, "entry", RegGlobalsFn)); + auto *EntryBB = BasicBlock::Create(C, "while.entry", RegGlobalsFn); + auto *IfThenBB = BasicBlock::Create(C, "if.then", RegGlobalsFn); + auto *IfElseBB = BasicBlock::Create(C, "if.else", RegGlobalsFn); + auto *SwGlobalBB = BasicBlock::Create(C, "sw.global", RegGlobalsFn); + auto *SwManagedBB = BasicBlock::Create(C, "sw.managed", RegGlobalsFn); + auto *SwSurfaceBB = BasicBlock::Create(C, "sw.surface", RegGlobalsFn); + auto *SwTextureBB = BasicBlock::Create(C, "sw.texture", RegGlobalsFn); + auto *IfEndBB = BasicBlock::Create(C, "if.end", RegGlobalsFn); + auto *ExitBB = BasicBlock::Create(C, "while.end", RegGlobalsFn); + + auto *EntryCmp = Builder.CreateICmpNE(EntriesB, EntriesE); + Builder.CreateCondBr(EntryCmp, EntryBB, ExitBB); + Builder.SetInsertPoint(EntryBB); + auto *Entry = Builder.CreatePHI(getEntryPtrTy(M), 2, "entry"); + auto *AddrPtr = + Builder.CreateInBoundsGEP(getEntryTy(M), Entry, + {ConstantInt::get(getSizeTTy(M), 0), + ConstantInt::get(Type::getInt32Ty(C), 0)}); + auto *Addr = Builder.CreateLoad(Type::getInt8PtrTy(C), AddrPtr, "addr"); + auto *NamePtr = + Builder.CreateInBoundsGEP(getEntryTy(M), Entry, + {ConstantInt::get(getSizeTTy(M), 0), + ConstantInt::get(Type::getInt32Ty(C), 1)}); + auto *Name = Builder.CreateLoad(Type::getInt8PtrTy(C), NamePtr, "name"); + auto *SizePtr = + Builder.CreateInBoundsGEP(getEntryTy(M), Entry, + {ConstantInt::get(getSizeTTy(M), 0), + ConstantInt::get(Type::getInt32Ty(C), 2)}); + auto *Size = Builder.CreateLoad(getSizeTTy(M), SizePtr, "size"); + auto *FlagsPtr = + Builder.CreateInBoundsGEP(getEntryTy(M), Entry, + {ConstantInt::get(getSizeTTy(M), 0), + ConstantInt::get(Type::getInt32Ty(C), 3)}); + auto *Flags = Builder.CreateLoad(Type::getInt32Ty(C), FlagsPtr, "flag"); + auto *FnCond = + Builder.CreateICmpEQ(Size, ConstantInt::getNullValue(getSizeTTy(M))); + Builder.CreateCondBr(FnCond, IfThenBB, IfElseBB); + + // Create kernel registration code. + Builder.SetInsertPoint(IfThenBB); + Builder.CreateCall(RegFunc, + {RegGlobalsFn->arg_begin(), Addr, Name, Name, + ConstantInt::get(Type::getInt32Ty(C), -1), + ConstantPointerNull::get(Type::getInt8PtrTy(C)), + ConstantPointerNull::get(Type::getInt8PtrTy(C)), + ConstantPointerNull::get(Type::getInt8PtrTy(C)), + ConstantPointerNull::get(Type::getInt8PtrTy(C)), + ConstantPointerNull::get(Type::getInt32PtrTy(C))}); + Builder.CreateBr(IfEndBB); + Builder.SetInsertPoint(IfElseBB); + + auto *Switch = Builder.CreateSwitch(Flags, IfEndBB); + // Create global variable registration code. + Builder.SetInsertPoint(SwGlobalBB); + Builder.CreateCall(RegVar, {RegGlobalsFn->arg_begin(), Addr, Name, Name, + ConstantInt::get(Type::getInt32Ty(C), 0), Size, + ConstantInt::get(Type::getInt32Ty(C), 0), + ConstantInt::get(Type::getInt32Ty(C), 0)}); + Builder.CreateBr(IfEndBB); + Switch->addCase(Builder.getInt32(OffloadGlobalEntry), SwGlobalBB); + + // Create managed variable registration code. + Builder.SetInsertPoint(SwManagedBB); + Builder.CreateBr(IfEndBB); + Switch->addCase(Builder.getInt32(OffloadGlobalManagedEntry), SwManagedBB); + + // Create surface variable registration code. + Builder.SetInsertPoint(SwSurfaceBB); + Builder.CreateBr(IfEndBB); + Switch->addCase(Builder.getInt32(OffloadGlobalSurfaceEntry), SwSurfaceBB); + + // Create texture variable registration code. + Builder.SetInsertPoint(SwTextureBB); + Builder.CreateBr(IfEndBB); + Switch->addCase(Builder.getInt32(OffloadGlobalTextureEntry), SwTextureBB); + + Builder.SetInsertPoint(IfEndBB); + auto *NewEntry = Builder.CreateInBoundsGEP( + getEntryTy(M), Entry, ConstantInt::get(getSizeTTy(M), 1)); + auto *Cmp = Builder.CreateICmpEQ( + NewEntry, + ConstantExpr::getInBoundsGetElementPtr( + ArrayType::get(getEntryTy(M), 0), EntriesE, + ArrayRef<Constant *>({ConstantInt::get(getSizeTTy(M), 0), + ConstantInt::get(getSizeTTy(M), 0)}))); + Entry->addIncoming( + ConstantExpr::getInBoundsGetElementPtr( + ArrayType::get(getEntryTy(M), 0), EntriesB, + ArrayRef<Constant *>({ConstantInt::get(getSizeTTy(M), 0), + ConstantInt::get(getSizeTTy(M), 0)})), + &RegGlobalsFn->getEntryBlock()); + Entry->addIncoming(NewEntry, IfEndBB); + Builder.CreateCondBr(Cmp, ExitBB, EntryBB); + Builder.SetInsertPoint(ExitBB); + Builder.CreateRetVoid(); + + return RegGlobalsFn; +} + +// Create the constructor and destructor to register the fatbinary with the CUDA +// runtime. +void createRegisterFatbinFunction(Module &M, GlobalVariable *FatbinDesc, + bool IsHIP) { + LLVMContext &C = M.getContext(); + auto *CtorFuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false); + auto *CtorFunc = + Function::Create(CtorFuncTy, GlobalValue::InternalLinkage, + IsHIP ? ".hip.fatbin_reg" : ".cuda.fatbin_reg", &M); + CtorFunc->setSection(".text.startup"); + + auto *DtorFuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false); + auto *DtorFunc = + Function::Create(DtorFuncTy, GlobalValue::InternalLinkage, + IsHIP ? ".hip.fatbin_unreg" : ".cuda.fatbin_unreg", &M); + DtorFunc->setSection(".text.startup"); + + // Get the __cudaRegisterFatBinary function declaration. + auto *RegFatTy = FunctionType::get(Type::getInt8PtrTy(C)->getPointerTo(), + Type::getInt8PtrTy(C), + /*isVarArg*/ false); + FunctionCallee RegFatbin = M.getOrInsertFunction( + IsHIP ? "__hipRegisterFatBinary" : "__cudaRegisterFatBinary", RegFatTy); + // Get the __cudaRegisterFatBinaryEnd function declaration. + auto *RegFatEndTy = FunctionType::get(Type::getVoidTy(C), + Type::getInt8PtrTy(C)->getPointerTo(), + /*isVarArg*/ false); + FunctionCallee RegFatbinEnd = + M.getOrInsertFunction("__cudaRegisterFatBinaryEnd", RegFatEndTy); + // Get the __cudaUnregisterFatBinary function declaration. + auto *UnregFatTy = FunctionType::get(Type::getVoidTy(C), + Type::getInt8PtrTy(C)->getPointerTo(), + /*isVarArg*/ false); + FunctionCallee UnregFatbin = M.getOrInsertFunction( + IsHIP ? "__hipUnregisterFatBinary" : "__cudaUnregisterFatBinary", + UnregFatTy); + + auto *AtExitTy = + FunctionType::get(Type::getInt32Ty(C), DtorFuncTy->getPointerTo(), + /*isVarArg*/ false); + FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy); + + auto *BinaryHandleGlobal = new llvm::GlobalVariable( + M, Type::getInt8PtrTy(C)->getPointerTo(), false, + llvm::GlobalValue::InternalLinkage, + llvm::ConstantPointerNull::get(Type::getInt8PtrTy(C)->getPointerTo()), + IsHIP ? ".hip.binary_handle" : ".cuda.binary_handle"); + + // Create the constructor to register this image with the runtime. + IRBuilder<> CtorBuilder(BasicBlock::Create(C, "entry", CtorFunc)); + CallInst *Handle = CtorBuilder.CreateCall( + RegFatbin, ConstantExpr::getPointerBitCastOrAddrSpaceCast( + FatbinDesc, Type::getInt8PtrTy(C))); + CtorBuilder.CreateAlignedStore( + Handle, BinaryHandleGlobal, + Align(M.getDataLayout().getPointerTypeSize(Type::getInt8PtrTy(C)))); + CtorBuilder.CreateCall(createRegisterGlobalsFunction(M, IsHIP), Handle); + if (!IsHIP) + CtorBuilder.CreateCall(RegFatbinEnd, Handle); + CtorBuilder.CreateCall(AtExit, DtorFunc); + CtorBuilder.CreateRetVoid(); + + // Create the destructor to unregister the image with the runtime. We cannot + // use a standard global destructor after CUDA 9.2 so this must be called by + // `atexit()` intead. + IRBuilder<> DtorBuilder(BasicBlock::Create(C, "entry", DtorFunc)); + LoadInst *BinaryHandle = DtorBuilder.CreateAlignedLoad( + Type::getInt8PtrTy(C)->getPointerTo(), BinaryHandleGlobal, + Align(M.getDataLayout().getPointerTypeSize(Type::getInt8PtrTy(C)))); + DtorBuilder.CreateCall(UnregFatbin, BinaryHandle); + DtorBuilder.CreateRetVoid(); + + // Add this function to constructors. + appendToGlobalCtors(M, CtorFunc, /*Priority*/ 1); +} + +} // namespace + +Error wrapOpenMPBinaries(Module &M, ArrayRef<ArrayRef<char>> Images) { + GlobalVariable *Desc = createBinDesc(M, Images); + if (!Desc) + return createStringError(inconvertibleErrorCode(), + "No binary descriptors created."); + createRegisterFunction(M, Desc); + createUnregisterFunction(M, Desc); + return Error::success(); +} + +Error wrapCudaBinary(Module &M, ArrayRef<char> Image) { + GlobalVariable *Desc = createFatbinDesc(M, Image, /* IsHIP */ false); + if (!Desc) + return createStringError(inconvertibleErrorCode(), + "No fatinbary section created."); + + createRegisterFatbinFunction(M, Desc, /* IsHIP */ false); + return Error::success(); +} + +Error wrapHIPBinary(Module &M, ArrayRef<char> Image) { + GlobalVariable *Desc = createFatbinDesc(M, Image, /* IsHIP */ true); + if (!Desc) + return createStringError(inconvertibleErrorCode(), + "No fatinbary section created."); + + createRegisterFatbinFunction(M, Desc, /* IsHIP */ true); + return Error::success(); +} diff --git a/gnu/llvm/clang/tools/clang-linker-wrapper/OffloadWrapper.h b/gnu/llvm/clang/tools/clang-linker-wrapper/OffloadWrapper.h new file mode 100644 index 00000000000..679333975b2 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-linker-wrapper/OffloadWrapper.h @@ -0,0 +1,28 @@ +//===- OffloadWrapper.h --r-------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CLANG_LINKER_WRAPPER_OFFLOAD_WRAPPER_H +#define LLVM_CLANG_TOOLS_CLANG_LINKER_WRAPPER_OFFLOAD_WRAPPER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Module.h" + +/// Wraps the input device images into the module \p M as global symbols and +/// registers the images with the OpenMP Offloading runtime libomptarget. +llvm::Error wrapOpenMPBinaries(llvm::Module &M, + llvm::ArrayRef<llvm::ArrayRef<char>> Images); + +/// Wraps the input fatbinary image into the module \p M as global symbols and +/// registers the images with the CUDA runtime. +llvm::Error wrapCudaBinary(llvm::Module &M, llvm::ArrayRef<char> Images); + +/// Wraps the input bundled image into the module \p M as global symbols and +/// registers the images with the HIP runtime. +llvm::Error wrapHIPBinary(llvm::Module &M, llvm::ArrayRef<char> Images); + +#endif diff --git a/gnu/llvm/clang/tools/clang-offload-bundler/CMakeLists.txt b/gnu/llvm/clang/tools/clang-offload-bundler/CMakeLists.txt index 2738bf02e72..7bc22f1479d 100644 --- a/gnu/llvm/clang/tools/clang-offload-bundler/CMakeLists.txt +++ b/gnu/llvm/clang/tools/clang-offload-bundler/CMakeLists.txt @@ -1,16 +1,21 @@ -set(LLVM_LINK_COMPONENTS Object Support) +set(LLVM_LINK_COMPONENTS + Object + Support + TargetParser + ) add_clang_tool(clang-offload-bundler ClangOffloadBundler.cpp - + DEPENDS intrinsics_gen ) set(CLANG_OFFLOAD_BUNDLER_LIB_DEPS clangBasic + clangDriver ) - + add_dependencies(clang clang-offload-bundler) clang_target_link_libraries(clang-offload-bundler diff --git a/gnu/llvm/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp b/gnu/llvm/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp index 43f7091c97f..04ad545f19c 100644 --- a/gnu/llvm/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp +++ b/gnu/llvm/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp @@ -7,20 +7,20 @@ //===----------------------------------------------------------------------===// /// /// \file -/// This file implements a clang-offload-bundler that bundles different -/// files that relate with the same source code but different targets into a -/// single one. Also the implements the opposite functionality, i.e. unbundle -/// files previous created by this tool. +/// This file implements a stand-alone clang-offload-bundler tool using the +/// OffloadBundler API. /// //===----------------------------------------------------------------------===// +#include "clang/Basic/Cuda.h" +#include "clang/Basic/TargetID.h" #include "clang/Basic/Version.h" +#include "clang/Driver/OffloadBundler.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" @@ -46,6 +46,7 @@ #include <cstddef> #include <cstdint> #include <forward_list> +#include <map> #include <memory> #include <set> #include <string> @@ -54,1242 +55,89 @@ using namespace llvm; using namespace llvm::object; +using namespace clang; -static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); +static void PrintVersion(raw_ostream &OS) { + OS << clang::getClangToolFullVersion("clang-offload-bundler") << '\n'; +} -// Mark all our options with this category, everything else (except for -version -// and -help) will be hidden. -static cl::OptionCategory - ClangOffloadBundlerCategory("clang-offload-bundler options"); +int main(int argc, const char **argv) { + + cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); -static cl::list<std::string> - InputFileNames("inputs", cl::CommaSeparated, cl::OneOrMore, - cl::desc("[<input file>,...]"), + // Mark all our options with this category, everything else (except for + // -version and -help) will be hidden. + cl::OptionCategory + ClangOffloadBundlerCategory("clang-offload-bundler options"); + cl::list<std::string> + InputFileNames("input", + cl::desc("Input file." + " Can be specified multiple times " + "for multiple input files."), cl::cat(ClangOffloadBundlerCategory)); -static cl::list<std::string> - OutputFileNames("outputs", cl::CommaSeparated, - cl::desc("[<output file>,...]"), + cl::list<std::string> + InputFileNamesDeprecatedOpt("inputs", cl::CommaSeparated, + cl::desc("[<input file>,...] (deprecated)"), + cl::cat(ClangOffloadBundlerCategory)); + cl::list<std::string> + OutputFileNames("output", + cl::desc("Output file." + " Can be specified multiple times " + "for multiple output files."), cl::cat(ClangOffloadBundlerCategory)); -static cl::list<std::string> + cl::list<std::string> + OutputFileNamesDeprecatedOpt("outputs", cl::CommaSeparated, + cl::desc("[<output file>,...] (deprecated)"), + cl::cat(ClangOffloadBundlerCategory)); + cl::list<std::string> TargetNames("targets", cl::CommaSeparated, cl::desc("[<offload kind>-<target triple>,...]"), cl::cat(ClangOffloadBundlerCategory)); -static cl::opt<std::string> - FilesType("type", cl::Required, - cl::desc("Type of the files to be bundled/unbundled.\n" - "Current supported types are:\n" - " i - cpp-output\n" - " ii - c++-cpp-output\n" - " cui - cuda/hip-output\n" - " d - dependency\n" - " ll - llvm\n" - " bc - llvm-bc\n" - " s - assembler\n" - " o - object\n" - " a - archive of objects\n" - " gch - precompiled-header\n" - " ast - clang AST file"), - cl::cat(ClangOffloadBundlerCategory)); -static cl::opt<bool> + cl::opt<std::string> FilesType( + "type", cl::Required, + cl::desc("Type of the files to be bundled/unbundled.\n" + "Current supported types are:\n" + " i - cpp-output\n" + " ii - c++-cpp-output\n" + " cui - cuda-cpp-output\n" + " hipi - hip-cpp-output\n" + " d - dependency\n" + " ll - llvm\n" + " bc - llvm-bc\n" + " s - assembler\n" + " o - object\n" + " a - archive of objects\n" + " gch - precompiled-header\n" + " ast - clang AST file"), + cl::cat(ClangOffloadBundlerCategory)); + cl::opt<bool> Unbundle("unbundle", cl::desc("Unbundle bundled file into several output files.\n"), cl::init(false), cl::cat(ClangOffloadBundlerCategory)); - -static cl::opt<bool> + cl::opt<bool> ListBundleIDs("list", cl::desc("List bundle IDs in the bundled file.\n"), cl::init(false), cl::cat(ClangOffloadBundlerCategory)); - -static cl::opt<bool> PrintExternalCommands( + cl::opt<bool> PrintExternalCommands( "###", cl::desc("Print any external commands that are to be executed " "instead of actually executing them - for testing purposes.\n"), cl::init(false), cl::cat(ClangOffloadBundlerCategory)); - -static cl::opt<bool> + cl::opt<bool> AllowMissingBundles("allow-missing-bundles", cl::desc("Create empty files if bundles are missing " "when unbundling.\n"), cl::init(false), cl::cat(ClangOffloadBundlerCategory)); - -static cl::opt<unsigned> + cl::opt<unsigned> BundleAlignment("bundle-align", cl::desc("Alignment of bundle for binary files"), cl::init(1), cl::cat(ClangOffloadBundlerCategory)); + cl::opt<bool> HipOpenmpCompatible( + "hip-openmp-compatible", + cl::desc("Treat hip and hipv4 offload kinds as " + "compatible with openmp kind, and vice versa.\n"), + cl::init(false), cl::cat(ClangOffloadBundlerCategory)); -/// Magic string that marks the existence of offloading data. -#define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__" - -/// The index of the host input in the list of inputs. -static unsigned HostInputIndex = ~0u; - -/// Whether not having host target is allowed. -static bool AllowNoHost = false; - -/// Path to the current binary. -static std::string BundlerExecutable; - -/// Obtain the offload kind, real machine triple, and an optional GPUArch -/// out of the target information specified by the user. -/// Bundle Entry ID (or, Offload Target String) has following components: -/// * Offload Kind - Host, OpenMP, or HIP -/// * Triple - Standard LLVM Triple -/// * GPUArch (Optional) - Processor name, like gfx906 or sm_30 -/// In presence of Proc, the Triple should contain separator "-" for all -/// standard four components, even if they are empty. -struct OffloadTargetInfo { - StringRef OffloadKind; - llvm::Triple Triple; - StringRef GPUArch; - - OffloadTargetInfo(const StringRef Target) { - SmallVector<StringRef, 6> Components; - Target.split(Components, '-', 5); - Components.resize(6); - this->OffloadKind = Components[0]; - this->Triple = llvm::Triple(Components[1], Components[2], Components[3], - Components[4]); - this->GPUArch = Components[5]; - } - - bool hasHostKind() const { return this->OffloadKind == "host"; } - - bool isOffloadKindValid() const { - return OffloadKind == "host" || OffloadKind == "openmp" || - OffloadKind == "hip" || OffloadKind == "hipv4"; - } - - bool isTripleValid() const { - return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch; - } - - bool operator==(const OffloadTargetInfo &Target) const { - return OffloadKind == Target.OffloadKind && - Triple.isCompatibleWith(Target.Triple) && GPUArch == Target.GPUArch; - } - - std::string str() { - return Twine(OffloadKind + "-" + Triple.str() + "-" + GPUArch).str(); - } -}; - -/// Generic file handler interface. -class FileHandler { -public: - struct BundleInfo { - StringRef BundleID; - }; - - FileHandler() {} - - virtual ~FileHandler() {} - - /// Update the file handler with information from the header of the bundled - /// file. - virtual Error ReadHeader(MemoryBuffer &Input) = 0; - - /// Read the marker of the next bundled to be read in the file. The bundle - /// name is returned if there is one in the file, or `None` if there are no - /// more bundles to be read. - virtual Expected<Optional<StringRef>> - ReadBundleStart(MemoryBuffer &Input) = 0; - - /// Read the marker that closes the current bundle. - virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0; - - /// Read the current bundle and write the result into the stream \a OS. - virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0; - - /// Write the header of the bundled file to \a OS based on the information - /// gathered from \a Inputs. - virtual Error WriteHeader(raw_fd_ostream &OS, - ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0; - - /// Write the marker that initiates a bundle for the triple \a TargetTriple to - /// \a OS. - virtual Error WriteBundleStart(raw_fd_ostream &OS, - StringRef TargetTriple) = 0; - - /// Write the marker that closes a bundle for the triple \a TargetTriple to \a - /// OS. - virtual Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) = 0; - - /// Write the bundle from \a Input into \a OS. - virtual Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0; - - /// List bundle IDs in \a Input. - virtual Error listBundleIDs(MemoryBuffer &Input) { - if (Error Err = ReadHeader(Input)) - return Err; - - return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { - llvm::outs() << Info.BundleID << '\n'; - Error Err = listBundleIDsCallback(Input, Info); - if (Err) - return Err; - return Error::success(); - }); - } - - /// For each bundle in \a Input, do \a Func. - Error forEachBundle(MemoryBuffer &Input, - std::function<Error(const BundleInfo &)> Func) { - while (true) { - Expected<Optional<StringRef>> CurTripleOrErr = ReadBundleStart(Input); - if (!CurTripleOrErr) - return CurTripleOrErr.takeError(); - - // No more bundles. - if (!*CurTripleOrErr) - break; - - StringRef CurTriple = **CurTripleOrErr; - assert(!CurTriple.empty()); - - BundleInfo Info{CurTriple}; - if (Error Err = Func(Info)) - return Err; - } - return Error::success(); - } - -protected: - virtual Error listBundleIDsCallback(MemoryBuffer &Input, - const BundleInfo &Info) { - return Error::success(); - } -}; - -/// Handler for binary files. The bundled file will have the following format -/// (all integers are stored in little-endian format): -/// -/// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string) -/// -/// NumberOfOffloadBundles (8-byte integer) -/// -/// OffsetOfBundle1 (8-byte integer) -/// SizeOfBundle1 (8-byte integer) -/// NumberOfBytesInTripleOfBundle1 (8-byte integer) -/// TripleOfBundle1 (byte length defined before) -/// -/// ... -/// -/// OffsetOfBundleN (8-byte integer) -/// SizeOfBundleN (8-byte integer) -/// NumberOfBytesInTripleOfBundleN (8-byte integer) -/// TripleOfBundleN (byte length defined before) -/// -/// Bundle1 -/// ... -/// BundleN - -/// Read 8-byte integers from a buffer in little-endian format. -static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) { - uint64_t Res = 0; - const char *Data = Buffer.data(); - - for (unsigned i = 0; i < 8; ++i) { - Res <<= 8; - uint64_t Char = (uint64_t)Data[pos + 7 - i]; - Res |= 0xffu & Char; - } - return Res; -} - -/// Write 8-byte integers to a buffer in little-endian format. -static void Write8byteIntegerToBuffer(raw_fd_ostream &OS, uint64_t Val) { - for (unsigned i = 0; i < 8; ++i) { - char Char = (char)(Val & 0xffu); - OS.write(&Char, 1); - Val >>= 8; - } -} - -class BinaryFileHandler final : public FileHandler { - /// Information about the bundles extracted from the header. - struct BinaryBundleInfo final : public BundleInfo { - /// Size of the bundle. - uint64_t Size = 0u; - /// Offset at which the bundle starts in the bundled file. - uint64_t Offset = 0u; - - BinaryBundleInfo() {} - BinaryBundleInfo(uint64_t Size, uint64_t Offset) - : Size(Size), Offset(Offset) {} - }; - - /// Map between a triple and the corresponding bundle information. - StringMap<BinaryBundleInfo> BundlesInfo; - - /// Iterator for the bundle information that is being read. - StringMap<BinaryBundleInfo>::iterator CurBundleInfo; - StringMap<BinaryBundleInfo>::iterator NextBundleInfo; - - /// Current bundle target to be written. - std::string CurWriteBundleTarget; - -public: - BinaryFileHandler() : FileHandler() {} - - ~BinaryFileHandler() final {} - - Error ReadHeader(MemoryBuffer &Input) final { - StringRef FC = Input.getBuffer(); - - // Initialize the current bundle with the end of the container. - CurBundleInfo = BundlesInfo.end(); - - // Check if buffer is smaller than magic string. - size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; - if (ReadChars > FC.size()) - return Error::success(); - - // Check if no magic was found. - StringRef Magic(FC.data(), sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); - if (!Magic.equals(OFFLOAD_BUNDLER_MAGIC_STR)) - return Error::success(); - - // Read number of bundles. - if (ReadChars + 8 > FC.size()) - return Error::success(); - - uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars); - ReadChars += 8; - - // Read bundle offsets, sizes and triples. - for (uint64_t i = 0; i < NumberOfBundles; ++i) { - - // Read offset. - if (ReadChars + 8 > FC.size()) - return Error::success(); - - uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars); - ReadChars += 8; - - // Read size. - if (ReadChars + 8 > FC.size()) - return Error::success(); - - uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars); - ReadChars += 8; - - // Read triple size. - if (ReadChars + 8 > FC.size()) - return Error::success(); - - uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars); - ReadChars += 8; - - // Read triple. - if (ReadChars + TripleSize > FC.size()) - return Error::success(); - - StringRef Triple(&FC.data()[ReadChars], TripleSize); - ReadChars += TripleSize; - - // Check if the offset and size make sense. - if (!Offset || Offset + Size > FC.size()) - return Error::success(); - - assert(BundlesInfo.find(Triple) == BundlesInfo.end() && - "Triple is duplicated??"); - BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset); - } - // Set the iterator to where we will start to read. - CurBundleInfo = BundlesInfo.end(); - NextBundleInfo = BundlesInfo.begin(); - return Error::success(); - } - - Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final { - if (NextBundleInfo == BundlesInfo.end()) - return None; - CurBundleInfo = NextBundleInfo++; - return CurBundleInfo->first(); - } - - Error ReadBundleEnd(MemoryBuffer &Input) final { - assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); - return Error::success(); - } - - Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { - assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); - StringRef FC = Input.getBuffer(); - OS.write(FC.data() + CurBundleInfo->second.Offset, - CurBundleInfo->second.Size); - return Error::success(); - } - - Error WriteHeader(raw_fd_ostream &OS, - ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { - // Compute size of the header. - uint64_t HeaderSize = 0; - - HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; - HeaderSize += 8; // Number of Bundles - - for (auto &T : TargetNames) { - HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple. - HeaderSize += T.size(); // The triple. - } - - // Write to the buffer the header. - OS << OFFLOAD_BUNDLER_MAGIC_STR; - - Write8byteIntegerToBuffer(OS, TargetNames.size()); - - unsigned Idx = 0; - for (auto &T : TargetNames) { - MemoryBuffer &MB = *Inputs[Idx++]; - HeaderSize = alignTo(HeaderSize, BundleAlignment); - // Bundle offset. - Write8byteIntegerToBuffer(OS, HeaderSize); - // Size of the bundle (adds to the next bundle's offset) - Write8byteIntegerToBuffer(OS, MB.getBufferSize()); - BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize); - HeaderSize += MB.getBufferSize(); - // Size of the triple - Write8byteIntegerToBuffer(OS, T.size()); - // Triple - OS << T; - } - return Error::success(); - } - - Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { - CurWriteBundleTarget = TargetTriple.str(); - return Error::success(); - } - - Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { - return Error::success(); - } - - Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { - auto BI = BundlesInfo[CurWriteBundleTarget]; - OS.seek(BI.Offset); - OS.write(Input.getBufferStart(), Input.getBufferSize()); - return Error::success(); - } -}; - -namespace { - -// This class implements a list of temporary files that are removed upon -// object destruction. -class TempFileHandlerRAII { -public: - ~TempFileHandlerRAII() { - for (const auto &File : Files) - sys::fs::remove(File); - } - - // Creates temporary file with given contents. - Expected<StringRef> Create(Optional<ArrayRef<char>> Contents) { - SmallString<128u> File; - if (std::error_code EC = - sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File)) - return createFileError(File, EC); - Files.push_front(File); - - if (Contents) { - std::error_code EC; - raw_fd_ostream OS(File, EC); - if (EC) - return createFileError(File, EC); - OS.write(Contents->data(), Contents->size()); - } - return Files.front().str(); - } - -private: - std::forward_list<SmallString<128u>> Files; -}; - -} // end anonymous namespace - -/// Handler for object files. The bundles are organized by sections with a -/// designated name. -/// -/// To unbundle, we just copy the contents of the designated section. -class ObjectFileHandler final : public FileHandler { - - /// The object file we are currently dealing with. - std::unique_ptr<ObjectFile> Obj; - - /// Return the input file contents. - StringRef getInputFileContents() const { return Obj->getData(); } - - /// Return bundle name (<kind>-<triple>) if the provided section is an offload - /// section. - static Expected<Optional<StringRef>> IsOffloadSection(SectionRef CurSection) { - Expected<StringRef> NameOrErr = CurSection.getName(); - if (!NameOrErr) - return NameOrErr.takeError(); - - // If it does not start with the reserved suffix, just skip this section. - if (!NameOrErr->startswith(OFFLOAD_BUNDLER_MAGIC_STR)) - return None; - - // Return the triple that is right after the reserved prefix. - return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); - } - - /// Total number of inputs. - unsigned NumberOfInputs = 0; - - /// Total number of processed inputs, i.e, inputs that were already - /// read from the buffers. - unsigned NumberOfProcessedInputs = 0; - - /// Iterator of the current and next section. - section_iterator CurrentSection; - section_iterator NextSection; - -public: - ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn) - : FileHandler(), Obj(std::move(ObjIn)), - CurrentSection(Obj->section_begin()), - NextSection(Obj->section_begin()) {} - - ~ObjectFileHandler() final {} - - Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } - - Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final { - while (NextSection != Obj->section_end()) { - CurrentSection = NextSection; - ++NextSection; - - // Check if the current section name starts with the reserved prefix. If - // so, return the triple. - Expected<Optional<StringRef>> TripleOrErr = - IsOffloadSection(*CurrentSection); - if (!TripleOrErr) - return TripleOrErr.takeError(); - if (*TripleOrErr) - return **TripleOrErr; - } - return None; - } - - Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); } - - Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { - Expected<StringRef> ContentOrErr = CurrentSection->getContents(); - if (!ContentOrErr) - return ContentOrErr.takeError(); - StringRef Content = *ContentOrErr; - - // Copy fat object contents to the output when extracting host bundle. - if (Content.size() == 1u && Content.front() == 0) - Content = StringRef(Input.getBufferStart(), Input.getBufferSize()); - - OS.write(Content.data(), Content.size()); - return Error::success(); - } - - Error WriteHeader(raw_fd_ostream &OS, - ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { - assert(HostInputIndex != ~0u && "Host input index not defined."); - - // Record number of inputs. - NumberOfInputs = Inputs.size(); - return Error::success(); - } - - Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { - ++NumberOfProcessedInputs; - return Error::success(); - } - - Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { - assert(NumberOfProcessedInputs <= NumberOfInputs && - "Processing more inputs that actually exist!"); - assert(HostInputIndex != ~0u && "Host input index not defined."); - - // If this is not the last output, we don't have to do anything. - if (NumberOfProcessedInputs != NumberOfInputs) - return Error::success(); - - // We will use llvm-objcopy to add target objects sections to the output - // fat object. These sections should have 'exclude' flag set which tells - // link editor to remove them from linker inputs when linking executable or - // shared library. - - // Find llvm-objcopy in order to create the bundle binary. - ErrorOr<std::string> Objcopy = sys::findProgramByName( - "llvm-objcopy", sys::path::parent_path(BundlerExecutable)); - if (!Objcopy) - Objcopy = sys::findProgramByName("llvm-objcopy"); - if (!Objcopy) - return createStringError(Objcopy.getError(), - "unable to find 'llvm-objcopy' in path"); - - // We write to the output file directly. So, we close it and use the name - // to pass down to llvm-objcopy. - OS.close(); - - // Temporary files that need to be removed. - TempFileHandlerRAII TempFiles; - - // Compose llvm-objcopy command line for add target objects' sections with - // appropriate flags. - BumpPtrAllocator Alloc; - StringSaver SS{Alloc}; - SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"}; - for (unsigned I = 0; I < NumberOfInputs; ++I) { - StringRef InputFile = InputFileNames[I]; - if (I == HostInputIndex) { - // Special handling for the host bundle. We do not need to add a - // standard bundle for the host object since we are going to use fat - // object as a host object. Therefore use dummy contents (one zero byte) - // when creating section for the host bundle. - Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0)); - if (!TempFileOrErr) - return TempFileOrErr.takeError(); - InputFile = *TempFileOrErr; - } - - ObjcopyArgs.push_back(SS.save(Twine("--add-section=") + - OFFLOAD_BUNDLER_MAGIC_STR + TargetNames[I] + - "=" + InputFile)); - ObjcopyArgs.push_back(SS.save(Twine("--set-section-flags=") + - OFFLOAD_BUNDLER_MAGIC_STR + TargetNames[I] + - "=readonly,exclude")); - } - ObjcopyArgs.push_back("--"); - ObjcopyArgs.push_back(InputFileNames[HostInputIndex]); - ObjcopyArgs.push_back(OutputFileNames.front()); - - if (Error Err = executeObjcopy(*Objcopy, ObjcopyArgs)) - return Err; - - return Error::success(); - } - - Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { - return Error::success(); - } - -private: - static Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) { - // If the user asked for the commands to be printed out, we do that - // instead of executing it. - if (PrintExternalCommands) { - errs() << "\"" << Objcopy << "\""; - for (StringRef Arg : drop_begin(Args, 1)) - errs() << " \"" << Arg << "\""; - errs() << "\n"; - } else { - if (sys::ExecuteAndWait(Objcopy, Args)) - return createStringError(inconvertibleErrorCode(), - "'llvm-objcopy' tool failed"); - } - return Error::success(); - } -}; - -/// Handler for text files. The bundled file will have the following format. -/// -/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" -/// Bundle 1 -/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" -/// ... -/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" -/// Bundle N -/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" -class TextFileHandler final : public FileHandler { - /// String that begins a line comment. - StringRef Comment; - - /// String that initiates a bundle. - std::string BundleStartString; - - /// String that closes a bundle. - std::string BundleEndString; - - /// Number of chars read from input. - size_t ReadChars = 0u; - -protected: - Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } - - Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final { - StringRef FC = Input.getBuffer(); - - // Find start of the bundle. - ReadChars = FC.find(BundleStartString, ReadChars); - if (ReadChars == FC.npos) - return None; - - // Get position of the triple. - size_t TripleStart = ReadChars = ReadChars + BundleStartString.size(); - - // Get position that closes the triple. - size_t TripleEnd = ReadChars = FC.find("\n", ReadChars); - if (TripleEnd == FC.npos) - return None; - - // Next time we read after the new line. - ++ReadChars; - - return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart); - } - - Error ReadBundleEnd(MemoryBuffer &Input) final { - StringRef FC = Input.getBuffer(); - - // Read up to the next new line. - assert(FC[ReadChars] == '\n' && "The bundle should end with a new line."); - - size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1); - if (TripleEnd != FC.npos) - // Next time we read after the new line. - ++ReadChars; - - return Error::success(); - } - - Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { - StringRef FC = Input.getBuffer(); - size_t BundleStart = ReadChars; - - // Find end of the bundle. - size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars); - - StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart); - OS << Bundle; - - return Error::success(); - } - - Error WriteHeader(raw_fd_ostream &OS, - ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { - return Error::success(); - } - - Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { - OS << BundleStartString << TargetTriple << "\n"; - return Error::success(); - } - - Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { - OS << BundleEndString << TargetTriple << "\n"; - return Error::success(); - } - - Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { - OS << Input.getBuffer(); - return Error::success(); - } - -public: - TextFileHandler(StringRef Comment) - : FileHandler(), Comment(Comment), ReadChars(0) { - BundleStartString = - "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ "; - BundleEndString = - "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ "; - } - - Error listBundleIDsCallback(MemoryBuffer &Input, - const BundleInfo &Info) final { - // TODO: To list bundle IDs in a bundled text file we need to go through - // all bundles. The format of bundled text file may need to include a - // header if the performance of listing bundle IDs of bundled text file is - // important. - ReadChars = Input.getBuffer().find(BundleEndString, ReadChars); - if (Error Err = ReadBundleEnd(Input)) - return Err; - return Error::success(); - } -}; - -/// Return an appropriate object file handler. We use the specific object -/// handler if we know how to deal with that format, otherwise we use a default -/// binary file handler. -static std::unique_ptr<FileHandler> -CreateObjectFileHandler(MemoryBuffer &FirstInput) { - // Check if the input file format is one that we know how to deal with. - Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput); - - // We only support regular object files. If failed to open the input as a - // known binary or this is not an object file use the default binary handler. - if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr)) - return std::make_unique<BinaryFileHandler>(); - - // Otherwise create an object file handler. The handler will be owned by the - // client of this function. - return std::make_unique<ObjectFileHandler>( - std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release()))); -} - -/// Return an appropriate handler given the input files and options. -static Expected<std::unique_ptr<FileHandler>> -CreateFileHandler(MemoryBuffer &FirstInput) { - if (FilesType == "i") - return std::make_unique<TextFileHandler>(/*Comment=*/"//"); - if (FilesType == "ii") - return std::make_unique<TextFileHandler>(/*Comment=*/"//"); - if (FilesType == "cui") - return std::make_unique<TextFileHandler>(/*Comment=*/"//"); - // TODO: `.d` should be eventually removed once `-M` and its variants are - // handled properly in offload compilation. - if (FilesType == "d") - return std::make_unique<TextFileHandler>(/*Comment=*/"#"); - if (FilesType == "ll") - return std::make_unique<TextFileHandler>(/*Comment=*/";"); - if (FilesType == "bc") - return std::make_unique<BinaryFileHandler>(); - if (FilesType == "s") - return std::make_unique<TextFileHandler>(/*Comment=*/"#"); - if (FilesType == "o") - return CreateObjectFileHandler(FirstInput); - if (FilesType == "a") - return CreateObjectFileHandler(FirstInput); - if (FilesType == "gch") - return std::make_unique<BinaryFileHandler>(); - if (FilesType == "ast") - return std::make_unique<BinaryFileHandler>(); - - return createStringError(errc::invalid_argument, - "'" + FilesType + "': invalid file type specified"); -} - -/// Bundle the files. Return true if an error was found. -static Error BundleFiles() { - std::error_code EC; - - // Create output file. - raw_fd_ostream OutputFile(OutputFileNames.front(), EC, sys::fs::OF_None); - if (EC) - return createFileError(OutputFileNames.front(), EC); - - // Open input files. - SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers; - InputBuffers.reserve(InputFileNames.size()); - for (auto &I : InputFileNames) { - ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = - MemoryBuffer::getFileOrSTDIN(I); - if (std::error_code EC = CodeOrErr.getError()) - return createFileError(I, EC); - InputBuffers.emplace_back(std::move(*CodeOrErr)); - } - - // Get the file handler. We use the host buffer as reference. - assert((HostInputIndex != ~0u || AllowNoHost) && - "Host input index undefined??"); - Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = - CreateFileHandler(*InputBuffers[AllowNoHost ? 0 : HostInputIndex]); - if (!FileHandlerOrErr) - return FileHandlerOrErr.takeError(); - - std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; - assert(FH); - - // Write header. - if (Error Err = FH->WriteHeader(OutputFile, InputBuffers)) - return Err; - - // Write all bundles along with the start/end markers. If an error was found - // writing the end of the bundle component, abort the bundle writing. - auto Input = InputBuffers.begin(); - for (auto &Triple : TargetNames) { - if (Error Err = FH->WriteBundleStart(OutputFile, Triple)) - return Err; - if (Error Err = FH->WriteBundle(OutputFile, **Input)) - return Err; - if (Error Err = FH->WriteBundleEnd(OutputFile, Triple)) - return Err; - ++Input; - } - return Error::success(); -} - -// List bundle IDs. Return true if an error was found. -static Error ListBundleIDsInFile(StringRef InputFileName) { - // Open Input file. - ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = - MemoryBuffer::getFileOrSTDIN(InputFileName); - if (std::error_code EC = CodeOrErr.getError()) - return createFileError(InputFileName, EC); - - MemoryBuffer &Input = **CodeOrErr; - - // Select the right files handler. - Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = - CreateFileHandler(Input); - if (!FileHandlerOrErr) - return FileHandlerOrErr.takeError(); - - std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; - assert(FH); - return FH->listBundleIDs(Input); -} - -// Unbundle the files. Return true if an error was found. -static Error UnbundleFiles() { - // Open Input file. - ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = - MemoryBuffer::getFileOrSTDIN(InputFileNames.front()); - if (std::error_code EC = CodeOrErr.getError()) - return createFileError(InputFileNames.front(), EC); - - MemoryBuffer &Input = **CodeOrErr; - - // Select the right files handler. - Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = - CreateFileHandler(Input); - if (!FileHandlerOrErr) - return FileHandlerOrErr.takeError(); - - std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; - assert(FH); - - // Read the header of the bundled file. - if (Error Err = FH->ReadHeader(Input)) - return Err; - - // Create a work list that consist of the map triple/output file. - StringMap<StringRef> Worklist; - auto Output = OutputFileNames.begin(); - for (auto &Triple : TargetNames) { - Worklist[Triple] = *Output; - ++Output; - } - - // Read all the bundles that are in the work list. If we find no bundles we - // assume the file is meant for the host target. - bool FoundHostBundle = false; - while (!Worklist.empty()) { - Expected<Optional<StringRef>> CurTripleOrErr = FH->ReadBundleStart(Input); - if (!CurTripleOrErr) - return CurTripleOrErr.takeError(); - - // We don't have more bundles. - if (!*CurTripleOrErr) - break; - - StringRef CurTriple = **CurTripleOrErr; - assert(!CurTriple.empty()); - - auto Output = Worklist.find(CurTriple); - // The file may have more bundles for other targets, that we don't care - // about. Therefore, move on to the next triple - if (Output == Worklist.end()) - continue; - - // Check if the output file can be opened and copy the bundle to it. - std::error_code EC; - raw_fd_ostream OutputFile(Output->second, EC, sys::fs::OF_None); - if (EC) - return createFileError(Output->second, EC); - if (Error Err = FH->ReadBundle(OutputFile, Input)) - return Err; - if (Error Err = FH->ReadBundleEnd(Input)) - return Err; - Worklist.erase(Output); - - // Record if we found the host bundle. - auto OffloadInfo = OffloadTargetInfo(CurTriple); - if (OffloadInfo.hasHostKind()) - FoundHostBundle = true; - } - - if (!AllowMissingBundles && !Worklist.empty()) { - std::string ErrMsg = "Can't find bundles for"; - std::set<StringRef> Sorted; - for (auto &E : Worklist) - Sorted.insert(E.first()); - unsigned I = 0; - unsigned Last = Sorted.size() - 1; - for (auto &E : Sorted) { - if (I != 0 && Last > 1) - ErrMsg += ","; - ErrMsg += " "; - if (I == Last && I != 0) - ErrMsg += "and "; - ErrMsg += E.str(); - ++I; - } - return createStringError(inconvertibleErrorCode(), ErrMsg); - } - - // If no bundles were found, assume the input file is the host bundle and - // create empty files for the remaining targets. - if (Worklist.size() == TargetNames.size()) { - for (auto &E : Worklist) { - std::error_code EC; - raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); - if (EC) - return createFileError(E.second, EC); - - // If this entry has a host kind, copy the input file to the output file. - auto OffloadInfo = OffloadTargetInfo(E.getKey()); - if (OffloadInfo.hasHostKind()) - OutputFile.write(Input.getBufferStart(), Input.getBufferSize()); - } - return Error::success(); - } - - // If we found elements, we emit an error if none of those were for the host - // in case host bundle name was provided in command line. - if (!FoundHostBundle && HostInputIndex != ~0u) - return createStringError(inconvertibleErrorCode(), - "Can't find bundle for the host target"); - - // If we still have any elements in the worklist, create empty files for them. - for (auto &E : Worklist) { - std::error_code EC; - raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); - if (EC) - return createFileError(E.second, EC); - } - - return Error::success(); -} - -static Archive::Kind getDefaultArchiveKindForHost() { - return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN - : Archive::K_GNU; -} - -/// @brief Checks if a code object \p CodeObjectInfo is compatible with a given -/// target \p TargetInfo. -/// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id -bool isCodeObjectCompatible(OffloadTargetInfo &CodeObjectInfo, - OffloadTargetInfo &TargetInfo) { - - // Compatible in case of exact match. - if (CodeObjectInfo == TargetInfo) { - DEBUG_WITH_TYPE( - "CodeObjectCompatibility", - dbgs() << "Compatible: Exact match: " << CodeObjectInfo.str() << "\n"); - return true; - } - - // Incompatible if Kinds or Triples mismatch. - if (CodeObjectInfo.OffloadKind != TargetInfo.OffloadKind || - !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { - DEBUG_WITH_TYPE( - "CodeObjectCompatibility", - dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " - << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() - << "]\n"); - return false; - } - - // Incompatible if GPUArch mismatch. - if (CodeObjectInfo.GPUArch != TargetInfo.GPUArch) { - DEBUG_WITH_TYPE("CodeObjectCompatibility", - dbgs() << "Incompatible: GPU Arch mismatch \t[CodeObject: " - << CodeObjectInfo.str() - << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); - return false; - } - - DEBUG_WITH_TYPE( - "CodeObjectCompatibility", - dbgs() << "Compatible: Code Objects are compatible \t[CodeObject: " - << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() - << "]\n"); - return true; -} - -/// @brief Computes a list of targets among all given targets which are -/// compatible with this code object -/// @param [in] Code Object \p CodeObject -/// @param [out] List of all compatible targets \p CompatibleTargets among all -/// given targets -/// @return false, if no compatible target is found. -static bool -getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, - SmallVectorImpl<StringRef> &CompatibleTargets) { - if (!CompatibleTargets.empty()) { - DEBUG_WITH_TYPE("CodeObjectCompatibility", - dbgs() << "CompatibleTargets list should be empty\n"); - return false; - } - for (auto &Target : TargetNames) { - auto TargetInfo = OffloadTargetInfo(Target); - if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo)) - CompatibleTargets.push_back(Target); - } - return !CompatibleTargets.empty(); -} - -/// UnbundleArchive takes an archive file (".a") as input containing bundled -/// code object files, and a list of offload targets (not host), and extracts -/// the code objects into a new archive file for each offload target. Each -/// resulting archive file contains all code object files corresponding to that -/// particular offload target. The created archive file does not -/// contain an index of the symbols and code object files are named as -/// <<Parent Bundle Name>-<CodeObject's GPUArch>>, with ':' replaced with '_'. -static Error UnbundleArchive() { - std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; - - /// Map of target names with list of object files that will form the device - /// specific archive for that target - StringMap<std::vector<NewArchiveMember>> OutputArchivesMap; - - // Map of target names and output archive filenames - StringMap<StringRef> TargetOutputFileNameMap; - - auto Output = OutputFileNames.begin(); - for (auto &Target : TargetNames) { - TargetOutputFileNameMap[Target] = *Output; - ++Output; - } - - StringRef IFName = InputFileNames.front(); - ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = - MemoryBuffer::getFileOrSTDIN(IFName, true, false); - if (std::error_code EC = BufOrErr.getError()) - return createFileError(InputFileNames.front(), EC); - - ArchiveBuffers.push_back(std::move(*BufOrErr)); - Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = - Archive::create(ArchiveBuffers.back()->getMemBufferRef()); - if (!LibOrErr) - return LibOrErr.takeError(); - - auto Archive = std::move(*LibOrErr); - - Error ArchiveErr = Error::success(); - auto ChildEnd = Archive->child_end(); - - /// Iterate over all bundled code object files in the input archive. - for (auto ArchiveIter = Archive->child_begin(ArchiveErr); - ArchiveIter != ChildEnd; ++ArchiveIter) { - if (ArchiveErr) - return ArchiveErr; - auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); - if (!ArchiveChildNameOrErr) - return ArchiveChildNameOrErr.takeError(); - - StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr); - - auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); - if (!CodeObjectBufferRefOrErr) - return CodeObjectBufferRefOrErr.takeError(); - - auto CodeObjectBuffer = - MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); - - Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = - CreateFileHandler(*CodeObjectBuffer); - if (!FileHandlerOrErr) - return FileHandlerOrErr.takeError(); - - std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr; - assert(FileHandler && - "FileHandle creation failed for file in the archive!"); - - if (Error ReadErr = FileHandler.get()->ReadHeader(*CodeObjectBuffer)) - return ReadErr; - - Expected<Optional<StringRef>> CurBundleIDOrErr = - FileHandler->ReadBundleStart(*CodeObjectBuffer); - if (!CurBundleIDOrErr) - return CurBundleIDOrErr.takeError(); - - Optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr; - // No device code in this child, skip. - if (!OptionalCurBundleID.hasValue()) - continue; - StringRef CodeObject = *OptionalCurBundleID; - - // Process all bundle entries (CodeObjects) found in this child of input - // archive. - while (!CodeObject.empty()) { - SmallVector<StringRef> CompatibleTargets; - auto CodeObjectInfo = OffloadTargetInfo(CodeObject); - if (CodeObjectInfo.hasHostKind()) { - // Do nothing, we don't extract host code yet. - } else if (getCompatibleOffloadTargets(CodeObjectInfo, - CompatibleTargets)) { - std::string BundleData; - raw_string_ostream DataStream(BundleData); - if (Error Err = - FileHandler.get()->ReadBundle(DataStream, *CodeObjectBuffer)) - return Err; - - for (auto &CompatibleTarget : CompatibleTargets) { - SmallString<128> BundledObjectFileName; - BundledObjectFileName.assign(BundledObjectFile); - auto OutputBundleName = - Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" + - CodeObject) - .str(); - // Replace ':' in optional target feature list with '_' to ensure - // cross-platform validity. - std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':', - '_'); - - std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy( - DataStream.str(), OutputBundleName); - ArchiveBuffers.push_back(std::move(MemBuf)); - llvm::MemoryBufferRef MemBufRef = - MemoryBufferRef(*(ArchiveBuffers.back())); - - // For inserting <CompatibleTarget, list<CodeObject>> entry in - // OutputArchivesMap. - if (OutputArchivesMap.find(CompatibleTarget) == - OutputArchivesMap.end()) { - - std::vector<NewArchiveMember> ArchiveMembers; - ArchiveMembers.push_back(NewArchiveMember(MemBufRef)); - OutputArchivesMap.insert_or_assign(CompatibleTarget, - std::move(ArchiveMembers)); - } else { - OutputArchivesMap[CompatibleTarget].push_back( - NewArchiveMember(MemBufRef)); - } - } - } - - if (Error Err = FileHandler.get()->ReadBundleEnd(*CodeObjectBuffer)) - return Err; - - Expected<Optional<StringRef>> NextTripleOrErr = - FileHandler->ReadBundleStart(*CodeObjectBuffer); - if (!NextTripleOrErr) - return NextTripleOrErr.takeError(); - - CodeObject = ((*NextTripleOrErr).hasValue()) ? **NextTripleOrErr : ""; - } // End of processing of all bundle entries of this child of input archive. - } // End of while over children of input archive. - - assert(!ArchiveErr && "Error occured while reading archive!"); - - /// Write out an archive for each target - for (auto &Target : TargetNames) { - StringRef FileName = TargetOutputFileNameMap[Target]; - StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers = - OutputArchivesMap.find(Target); - if (CurArchiveMembers != OutputArchivesMap.end()) { - if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(), - true, getDefaultArchiveKindForHost(), - true, false, nullptr)) - return WriteErr; - } else if (!AllowMissingBundles) { - std::string ErrMsg = - Twine("no compatible code object found for the target '" + Target + - "' in heterogenous archive library: " + IFName) - .str(); - return createStringError(inconvertibleErrorCode(), ErrMsg); - } - } - - return Error::success(); -} - -static void PrintVersion(raw_ostream &OS) { - OS << clang::getClangToolFullVersion("clang-offload-bundler") << '\n'; -} - -int main(int argc, const char **argv) { + // Process commandline options and report errors sys::PrintStackTraceOnErrorSignal(argv[0]); cl::HideUnrelatedOptions(ClangOffloadBundlerCategory); @@ -1306,24 +154,92 @@ int main(int argc, const char **argv) { return 0; } + /// Class to store bundler options in standard (non-cl::opt) data structures + // Avoid using cl::opt variables after these assignments when possible + OffloadBundlerConfig BundlerConfig; + BundlerConfig.AllowMissingBundles = AllowMissingBundles; + BundlerConfig.PrintExternalCommands = PrintExternalCommands; + BundlerConfig.HipOpenmpCompatible = HipOpenmpCompatible; + BundlerConfig.BundleAlignment = BundleAlignment; + BundlerConfig.FilesType = FilesType; + BundlerConfig.ObjcopyPath = ""; + + BundlerConfig.TargetNames = TargetNames; + BundlerConfig.InputFileNames = InputFileNames; + BundlerConfig.OutputFileNames = OutputFileNames; + + /// The index of the host input in the list of inputs. + BundlerConfig.HostInputIndex = ~0u; + + /// Whether not having host target is allowed. + BundlerConfig.AllowNoHost = false; + auto reportError = [argv](Error E) { logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0])); exit(1); }; auto doWork = [&](std::function<llvm::Error()> Work) { - // Save the current executable directory as it will be useful to find other - // tools. - BundlerExecutable = argv[0]; - if (!llvm::sys::fs::exists(BundlerExecutable)) - BundlerExecutable = - sys::fs::getMainExecutable(argv[0], &BundlerExecutable); - if (llvm::Error Err = Work()) { reportError(std::move(Err)); } }; + auto warningOS = [argv]() -> raw_ostream & { + return WithColor::warning(errs(), StringRef(argv[0])); + }; + + /// Path to the current binary. + std::string BundlerExecutable = argv[0]; + + if (!llvm::sys::fs::exists(BundlerExecutable)) + BundlerExecutable = + sys::fs::getMainExecutable(argv[0], &BundlerExecutable); + + // Find llvm-objcopy in order to create the bundle binary. + ErrorOr<std::string> Objcopy = sys::findProgramByName( + "llvm-objcopy", + sys::path::parent_path(BundlerExecutable)); + if (!Objcopy) + Objcopy = sys::findProgramByName("llvm-objcopy"); + if (!Objcopy) + reportError(createStringError(Objcopy.getError(), + "unable to find 'llvm-objcopy' in path")); + else + BundlerConfig.ObjcopyPath = *Objcopy; + + if (InputFileNames.getNumOccurrences() != 0 && + InputFileNamesDeprecatedOpt.getNumOccurrences() != 0) { + reportError(createStringError( + errc::invalid_argument, + "-inputs and -input cannot be used together, use only -input instead")); + } + + if (InputFileNamesDeprecatedOpt.size()) { + warningOS() << "-inputs is deprecated, use -input instead\n"; + // temporary hack to support -inputs + std::vector<std::string> &s = InputFileNames; + s.insert(s.end(), InputFileNamesDeprecatedOpt.begin(), + InputFileNamesDeprecatedOpt.end()); + } + BundlerConfig.InputFileNames = InputFileNames; + + if (OutputFileNames.getNumOccurrences() != 0 && + OutputFileNamesDeprecatedOpt.getNumOccurrences() != 0) { + reportError(createStringError(errc::invalid_argument, + "-outputs and -output cannot be used " + "together, use only -output instead")); + } + + if (OutputFileNamesDeprecatedOpt.size()) { + warningOS() << "-outputs is deprecated, use -output instead\n"; + // temporary hack to support -outputs + std::vector<std::string> &s = OutputFileNames; + s.insert(s.end(), OutputFileNamesDeprecatedOpt.begin(), + OutputFileNamesDeprecatedOpt.end()); + } + BundlerConfig.OutputFileNames = OutputFileNames; + if (ListBundleIDs) { if (Unbundle) { reportError( @@ -1343,20 +259,23 @@ int main(int argc, const char **argv) { "-targets option is invalid for -list")); } - doWork([]() { return ListBundleIDsInFile(InputFileNames.front()); }); + doWork([&]() { return OffloadBundler::ListBundleIDsInFile( + InputFileNames.front(), + BundlerConfig); }); return 0; } - if (OutputFileNames.getNumOccurrences() == 0) { - reportError(createStringError( - errc::invalid_argument, - "for the --outputs option: must be specified at least once!")); + if (OutputFileNames.size() == 0) { + reportError( + createStringError(errc::invalid_argument, "no output file specified!")); } + if (TargetNames.getNumOccurrences() == 0) { reportError(createStringError( errc::invalid_argument, "for the --targets option: must be specified at least once!")); } + if (Unbundle) { if (InputFileNames.size() != 1) { reportError(createStringError( @@ -1369,7 +288,7 @@ int main(int argc, const char **argv) { "match in unbundling mode")); } } else { - if (FilesType == "a") { + if (BundlerConfig.FilesType == "a") { reportError(createStringError(errc::invalid_argument, "Archive files are only supported " "for unbundling")); @@ -1392,6 +311,8 @@ int main(int argc, const char **argv) { unsigned HostTargetNum = 0u; bool HIPOnly = true; llvm::DenseSet<StringRef> ParsedTargets; + // Map {offload-kind}-{triple} to target IDs. + std::map<std::string, std::set<StringRef>> TargetIDs; for (StringRef Target : TargetNames) { if (ParsedTargets.contains(Target)) { reportError(createStringError(errc::invalid_argument, @@ -1399,7 +320,7 @@ int main(int argc, const char **argv) { } ParsedTargets.insert(Target); - auto OffloadInfo = OffloadTargetInfo(Target); + auto OffloadInfo = OffloadTargetInfo(Target, BundlerConfig); bool KindIsValid = OffloadInfo.isOffloadKindValid(); bool TripleIsValid = OffloadInfo.isTripleValid(); @@ -1414,10 +335,12 @@ int main(int argc, const char **argv) { reportError(createStringError(errc::invalid_argument, Msg.str())); } + TargetIDs[OffloadInfo.OffloadKind.str() + "-" + OffloadInfo.Triple.str()] + .insert(OffloadInfo.TargetID); if (KindIsValid && OffloadInfo.hasHostKind()) { ++HostTargetNum; // Save the index of the input that refers to the host. - HostInputIndex = Index; + BundlerConfig.HostInputIndex = Index; } if (OffloadInfo.OffloadKind != "hip" && OffloadInfo.OffloadKind != "hipv4") @@ -1425,29 +348,42 @@ int main(int argc, const char **argv) { ++Index; } + for (const auto &TargetID : TargetIDs) { + if (auto ConflictingTID = + clang::getConflictTargetIDCombination(TargetID.second)) { + SmallVector<char, 128u> Buf; + raw_svector_ostream Msg(Buf); + Msg << "Cannot bundle inputs with conflicting targets: '" + << TargetID.first + "-" + ConflictingTID->first << "' and '" + << TargetID.first + "-" + ConflictingTID->second << "'"; + reportError(createStringError(errc::invalid_argument, Msg.str())); + } + } // HIP uses clang-offload-bundler to bundle device-only compilation results // for multiple GPU archs, therefore allow no host target if all entries // are for HIP. - AllowNoHost = HIPOnly; + BundlerConfig.AllowNoHost = HIPOnly; // Host triple is not really needed for unbundling operation, so do not // treat missing host triple as error if we do unbundling. if ((Unbundle && HostTargetNum > 1) || - (!Unbundle && HostTargetNum != 1 && !AllowNoHost)) { + (!Unbundle && HostTargetNum != 1 && !BundlerConfig.AllowNoHost)) { reportError(createStringError(errc::invalid_argument, "expecting exactly one host target but got " + Twine(HostTargetNum))); } - doWork([]() { + OffloadBundler Bundler(BundlerConfig); + + doWork([&]() { if (Unbundle) { - if (FilesType == "a") - return UnbundleArchive(); + if (BundlerConfig.FilesType == "a") + return Bundler.UnbundleArchive(); else - return UnbundleFiles(); + return Bundler.UnbundleFiles(); } else - return BundleFiles(); + return Bundler.BundleFiles(); }); return 0; } diff --git a/gnu/llvm/clang/tools/clang-offload-packager/CMakeLists.txt b/gnu/llvm/clang/tools/clang-offload-packager/CMakeLists.txt new file mode 100644 index 00000000000..accc9486f46 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-offload-packager/CMakeLists.txt @@ -0,0 +1,19 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + BinaryFormat + Object + Support) + +add_clang_tool(clang-offload-packager + ClangOffloadPackager.cpp + + DEPENDS + ${tablegen_deps} + ) + +add_dependencies(clang clang-offload-packager) + +clang_target_link_libraries(clang-offload-packager + PRIVATE + clangBasic + ) diff --git a/gnu/llvm/clang/tools/clang-offload-packager/ClangOffloadPackager.cpp b/gnu/llvm/clang/tools/clang-offload-packager/ClangOffloadPackager.cpp new file mode 100644 index 00000000000..47ef155ef27 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-offload-packager/ClangOffloadPackager.cpp @@ -0,0 +1,221 @@ +//===-- clang-offload-packager/ClangOffloadPackager.cpp - file bundler ---===// +// +// 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 tool takes several device object files and bundles them into a single +// binary image using a custom binary format. This is intended to be used to +// embed many device files into an application to create a fat binary. +// +//===---------------------------------------------------------------------===// + +#include "clang/Basic/Version.h" + +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Object/OffloadBinary.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; +using namespace llvm::object; + +static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); + +static cl::OptionCategory + ClangOffloadPackagerCategory("clang-offload-packager options"); + +static cl::opt<std::string> OutputFile("o", cl::desc("Write output to <file>."), + cl::value_desc("file"), + cl::cat(ClangOffloadPackagerCategory)); + +static cl::opt<std::string> InputFile(cl::Positional, + cl::desc("Extract from <file>."), + cl::value_desc("file"), + cl::cat(ClangOffloadPackagerCategory)); + +static cl::list<std::string> + DeviceImages("image", + cl::desc("List of key and value arguments. Required keywords " + "are 'file' and 'triple'."), + cl::value_desc("<key>=<value>,..."), + cl::cat(ClangOffloadPackagerCategory)); + +static void PrintVersion(raw_ostream &OS) { + OS << clang::getClangToolFullVersion("clang-offload-packager") << '\n'; +} + +// Get a map containing all the arguments for the image. Repeated arguments will +// be placed in a comma separated list. +static DenseMap<StringRef, StringRef> getImageArguments(StringRef Image, + StringSaver &Saver) { + DenseMap<StringRef, StringRef> Args; + for (StringRef Arg : llvm::split(Image, ",")) { + auto [Key, Value] = Arg.split("="); + if (Args.count(Key)) + Args[Key] = Saver.save(Args[Key] + "," + Value); + else + Args[Key] = Value; + } + + return Args; +} + +static Error bundleImages() { + SmallVector<char, 1024> BinaryData; + raw_svector_ostream OS(BinaryData); + for (StringRef Image : DeviceImages) { + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + DenseMap<StringRef, StringRef> Args = getImageArguments(Image, Saver); + + if (!Args.count("triple") || !Args.count("file")) + return createStringError( + inconvertibleErrorCode(), + "'file' and 'triple' are required image arguments"); + + OffloadBinary::OffloadingImage ImageBinary{}; + std::unique_ptr<llvm::MemoryBuffer> DeviceImage; + for (const auto &[Key, Value] : Args) { + if (Key == "file") { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ObjectOrErr = + llvm::MemoryBuffer::getFileOrSTDIN(Value); + if (std::error_code EC = ObjectOrErr.getError()) + return errorCodeToError(EC); + + // Clang uses the '.o' suffix for LTO bitcode. + if (identify_magic((*ObjectOrErr)->getBuffer()) == file_magic::bitcode) + ImageBinary.TheImageKind = object::IMG_Bitcode; + else + ImageBinary.TheImageKind = + getImageKind(sys::path::extension(Value).drop_front()); + ImageBinary.Image = std::move(*ObjectOrErr); + } else if (Key == "kind") { + ImageBinary.TheOffloadKind = getOffloadKind(Value); + } else { + ImageBinary.StringData[Key] = Value; + } + } + std::unique_ptr<MemoryBuffer> Buffer = OffloadBinary::write(ImageBinary); + if (Buffer->getBufferSize() % OffloadBinary::getAlignment() != 0) + return createStringError(inconvertibleErrorCode(), + "Offload binary has invalid size alignment"); + OS << Buffer->getBuffer(); + } + + Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr = + FileOutputBuffer::create(OutputFile, BinaryData.size()); + if (!OutputOrErr) + return OutputOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr); + std::copy(BinaryData.begin(), BinaryData.end(), Output->getBufferStart()); + if (Error E = Output->commit()) + return E; + return Error::success(); +} + +static Error unbundleImages() { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = + MemoryBuffer::getFileOrSTDIN(InputFile); + if (std::error_code EC = BufferOrErr.getError()) + return createFileError(InputFile, EC); + std::unique_ptr<MemoryBuffer> Buffer = std::move(*BufferOrErr); + + // This data can be misaligned if extracted from an archive. + if (!isAddrAligned(Align(OffloadBinary::getAlignment()), + Buffer->getBufferStart())) + Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(), + Buffer->getBufferIdentifier()); + + SmallVector<OffloadFile> Binaries; + if (Error Err = extractOffloadBinaries(*Buffer, Binaries)) + return Err; + + // Try to extract each device image specified by the user from the input file. + for (StringRef Image : DeviceImages) { + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + auto Args = getImageArguments(Image, Saver); + + for (uint64_t I = 0, E = Binaries.size(); I != E; ++I) { + const auto *Binary = Binaries[I].getBinary(); + // We handle the 'file' and 'kind' identifiers differently. + bool Match = llvm::all_of(Args, [&](auto &Arg) { + const auto [Key, Value] = Arg; + if (Key == "file") + return true; + if (Key == "kind") + return Binary->getOffloadKind() == getOffloadKind(Value); + return Binary->getString(Key) == Value; + }); + if (!Match) + continue; + + // If the user did not provide a filename derive one from the input and + // image. + StringRef Filename = + !Args.count("file") + ? Saver.save(sys::path::stem(InputFile) + "-" + + Binary->getTriple() + "-" + Binary->getArch() + "." + + std::to_string(I) + "." + + getImageKindName(Binary->getImageKind())) + : Args["file"]; + + Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr = + FileOutputBuffer::create(Filename, Binary->getImage().size()); + if (!OutputOrErr) + return OutputOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr); + llvm::copy(Binary->getImage(), Output->getBufferStart()); + if (Error E = Output->commit()) + return E; + } + } + + return Error::success(); +} + +int main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(argv[0]); + cl::HideUnrelatedOptions(ClangOffloadPackagerCategory); + cl::SetVersionPrinter(PrintVersion); + cl::ParseCommandLineOptions( + argc, argv, + "A utility for bundling several object files into a single binary.\n" + "The output binary can then be embedded into the host section table\n" + "to create a fatbinary containing offloading code.\n"); + + if (Help) { + cl::PrintHelpMessage(); + return EXIT_SUCCESS; + } + + auto reportError = [argv](Error E) { + logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0])); + return EXIT_FAILURE; + }; + + if (!InputFile.empty() && !OutputFile.empty()) + return reportError( + createStringError(inconvertibleErrorCode(), + "Packaging to an output file and extracting from an " + "input file are mutually exclusive.")); + + if (!OutputFile.empty()) { + if (Error Err = bundleImages()) + return reportError(std::move(Err)); + } else if (!InputFile.empty()) { + if (Error Err = unbundleImages()) + return reportError(std::move(Err)); + } + + return EXIT_SUCCESS; +} diff --git a/gnu/llvm/clang/tools/clang-refactor/ClangRefactor.cpp b/gnu/llvm/clang/tools/clang-refactor/ClangRefactor.cpp index eacd00a601d..d362eecf06d 100644 --- a/gnu/llvm/clang/tools/clang-refactor/ClangRefactor.cpp +++ b/gnu/llvm/clang/tools/clang-refactor/ClangRefactor.cpp @@ -26,6 +26,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" +#include <optional> #include <string> using namespace clang; @@ -39,11 +40,11 @@ static cl::OptionCategory CommonRefactorOptions("Refactoring options"); static cl::opt<bool> Verbose("v", cl::desc("Use verbose output"), cl::cat(cl::getGeneralCategory()), - cl::sub(*cl::AllSubCommands)); + cl::sub(cl::SubCommand::getAll())); static cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s"), cl::cat(cl::getGeneralCategory()), - cl::sub(*cl::AllSubCommands)); + cl::sub(cl::SubCommand::getAll())); } // end namespace opts @@ -147,14 +148,14 @@ std::unique_ptr<SourceSelectionArgument> SourceSelectionArgument::fromString(StringRef Value) { if (Value.startswith("test:")) { StringRef Filename = Value.drop_front(strlen("test:")); - Optional<TestSelectionRangesInFile> ParsedTestSelection = + std::optional<TestSelectionRangesInFile> ParsedTestSelection = findTestSelectionRanges(Filename); if (!ParsedTestSelection) return nullptr; // A parsing error was already reported. return std::make_unique<TestSourceSelectionArgument>( std::move(*ParsedTestSelection)); } - Optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value); + std::optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value); if (Range) return std::make_unique<SourceRangeSelectionArgument>(std::move(*Range)); llvm::errs() << "error: '-selection' option must be specified using " @@ -194,13 +195,13 @@ public: : Options(Options) {} void visit(const RefactoringOption &Opt, - Optional<std::string> &Value) override { + std::optional<std::string> &Value) override { const cl::opt<std::string> &CLOpt = Options.getStringOption(Opt); if (!CLOpt.getValue().empty()) { Value = CLOpt.getValue(); return; } - Value = None; + Value = std::nullopt; if (Opt.isRequired()) MissingRequiredOptions.push_back(&Opt); } @@ -224,7 +225,8 @@ public: RefactoringActionCommandLineOptions &Options) : Category(Category), Subcommand(Subcommand), Options(Options) {} - void visit(const RefactoringOption &Opt, Optional<std::string> &) override { + void visit(const RefactoringOption &Opt, + std::optional<std::string> &) override { if (Visited.insert(&Opt).second) Options.addStringOption(Opt, create<std::string>(Opt)); } @@ -316,7 +318,7 @@ public: ClangRefactorConsumer(AtomicChanges &Changes) : SourceChanges(&Changes) {} void handleError(llvm::Error Err) override { - Optional<PartialDiagnosticAt> Diag = DiagnosticError::take(Err); + std::optional<PartialDiagnosticAt> Diag = DiagnosticError::take(Err); if (!Diag) { llvm::errs() << llvm::toString(std::move(Err)) << "\n"; return; diff --git a/gnu/llvm/clang/tools/clang-refactor/TestSupport.cpp b/gnu/llvm/clang/tools/clang-refactor/TestSupport.cpp index eb880889749..400313eeab5 100644 --- a/gnu/llvm/clang/tools/clang-refactor/TestSupport.cpp +++ b/gnu/llvm/clang/tools/clang-refactor/TestSupport.cpp @@ -24,6 +24,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace llvm; @@ -177,8 +178,8 @@ bool TestRefactoringResultConsumer::handleAllResults() { bool Failed = false; for (auto &Group : llvm::enumerate(Results)) { // All ranges in the group must produce the same result. - Optional<tooling::AtomicChanges> CanonicalResult; - Optional<std::string> CanonicalErrorMessage; + std::optional<tooling::AtomicChanges> CanonicalResult; + std::optional<std::string> CanonicalErrorMessage; for (auto &I : llvm::enumerate(Group.value())) { Expected<tooling::AtomicChanges> &Result = I.value(); std::string ErrorMessage; @@ -291,14 +292,14 @@ static unsigned addEndLineOffsetAndEndColumn(StringRef Source, unsigned Offset, Source, LineStart == StringRef::npos ? 0 : LineStart + 1, Column - 1); } -Optional<TestSelectionRangesInFile> +std::optional<TestSelectionRangesInFile> findTestSelectionRanges(StringRef Filename) { ErrorOr<std::unique_ptr<MemoryBuffer>> ErrOrFile = MemoryBuffer::getFile(Filename); if (!ErrOrFile) { llvm::errs() << "error: -selection=test:" << Filename << " : could not open the given file"; - return None; + return std::nullopt; } StringRef Source = ErrOrFile.get()->getBuffer(); @@ -340,7 +341,7 @@ findTestSelectionRanges(StringRef Filename) { // Allow CHECK: comments to contain range= commands. if (!RangeRegex.match(Comment, &Matches) || Comment.contains("CHECK")) { if (DetectMistypedCommand()) - return None; + return std::nullopt; continue; } unsigned Offset = Tok.getEndLoc().getRawEncoding(); @@ -359,7 +360,7 @@ findTestSelectionRanges(StringRef Filename) { SmallVector<StringRef, 4> EndLocMatches; if (!EndLocRegex.match(Matches[3], &EndLocMatches)) { if (DetectMistypedCommand()) - return None; + return std::nullopt; continue; } unsigned EndLineOffset = 0, EndColumn = 0; @@ -380,7 +381,7 @@ findTestSelectionRanges(StringRef Filename) { if (GroupedRanges.empty()) { llvm::errs() << "error: -selection=test:" << Filename << ": no 'range' commands"; - return None; + return std::nullopt; } TestSelectionRangesInFile TestRanges = {Filename.str(), {}}; diff --git a/gnu/llvm/clang/tools/clang-refactor/TestSupport.h b/gnu/llvm/clang/tools/clang-refactor/TestSupport.h index 1282c3a90f3..4eac9022baa 100644 --- a/gnu/llvm/clang/tools/clang-refactor/TestSupport.h +++ b/gnu/llvm/clang/tools/clang-refactor/TestSupport.h @@ -18,10 +18,10 @@ #include "ToolRefactoringResultConsumer.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Error.h" #include <map> +#include <optional> #include <string> namespace clang { @@ -93,9 +93,10 @@ struct TestSelectionRangesInFile { /// because clang-refactor should return zero on exit when the group results are /// consistent. /// -/// \returns None on failure (errors are emitted to stderr), or a set of +/// \returns std::nullopt on failure (errors are emitted to stderr), or a set of /// grouped source ranges in the given file otherwise. -Optional<TestSelectionRangesInFile> findTestSelectionRanges(StringRef Filename); +std::optional<TestSelectionRangesInFile> +findTestSelectionRanges(StringRef Filename); } // end namespace refactor } // end namespace clang diff --git a/gnu/llvm/clang/tools/clang-rename/CMakeLists.txt b/gnu/llvm/clang/tools/clang-rename/CMakeLists.txt index cda8e29ec5b..f4c4e520520 100644 --- a/gnu/llvm/clang/tools/clang-rename/CMakeLists.txt +++ b/gnu/llvm/clang/tools/clang-rename/CMakeLists.txt @@ -18,9 +18,9 @@ clang_target_link_libraries(clang-rename clangToolingRefactoring ) -install(PROGRAMS clang-rename.py - DESTINATION share/clang +install(FILES clang-rename.py + DESTINATION "${CMAKE_INSTALL_DATADIR}/clang" COMPONENT clang-rename) -install(PROGRAMS clang-rename.el - DESTINATION share/clang +install(FILES clang-rename.el + DESTINATION "${CMAKE_INSTALL_DATADIR}/clang" COMPONENT clang-rename) diff --git a/gnu/llvm/clang/tools/clang-rename/ClangRename.cpp b/gnu/llvm/clang/tools/clang-rename/ClangRename.cpp index 141ba379ed0..e7ceac7dbf3 100644 --- a/gnu/llvm/clang/tools/clang-rename/ClangRename.cpp +++ b/gnu/llvm/clang/tools/clang-rename/ClangRename.cpp @@ -68,17 +68,17 @@ static cl::OptionCategory ClangRenameOptions("clang-rename common options"); static cl::list<unsigned> SymbolOffsets( "offset", cl::desc("Locates the symbol by offset as opposed to <line>:<column>."), - cl::ZeroOrMore, cl::cat(ClangRenameOptions)); + cl::cat(ClangRenameOptions)); static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."), cl::cat(ClangRenameOptions)); static cl::list<std::string> QualifiedNames("qualified-name", cl::desc("The fully qualified name of the symbol."), - cl::ZeroOrMore, cl::cat(ClangRenameOptions)); + cl::cat(ClangRenameOptions)); static cl::list<std::string> NewNames("new-name", cl::desc("The new name to change the symbol to."), - cl::ZeroOrMore, cl::cat(ClangRenameOptions)); + cl::cat(ClangRenameOptions)); static cl::opt<bool> PrintName( "pn", cl::desc("Print the found symbol's name prior to renaming to stderr."), diff --git a/gnu/llvm/clang/tools/clang-repl/CMakeLists.txt b/gnu/llvm/clang/tools/clang-repl/CMakeLists.txt index 060c62aa419..b51a18c10cd 100644 --- a/gnu/llvm/clang/tools/clang-repl/CMakeLists.txt +++ b/gnu/llvm/clang/tools/clang-repl/CMakeLists.txt @@ -11,8 +11,14 @@ add_clang_tool(clang-repl ClangRepl.cpp ) -clang_target_link_libraries(clang-repl PUBLIC +clang_target_link_libraries(clang-repl PRIVATE clangBasic + clangFrontend clangInterpreter clangTooling ) + +# Support plugins. +if(CLANG_PLUGIN_SUPPORT) + export_executable_symbols_for_plugins(clang-repl) +endif() diff --git a/gnu/llvm/clang/tools/clang-repl/ClangRepl.cpp b/gnu/llvm/clang/tools/clang-repl/ClangRepl.cpp index ba6bb11abc8..401a31d3406 100644 --- a/gnu/llvm/clang/tools/clang-repl/ClangRepl.cpp +++ b/gnu/llvm/clang/tools/clang-repl/ClangRepl.cpp @@ -21,18 +21,18 @@ #include "llvm/Support/ManagedStatic.h" // llvm_shutdown #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" // llvm::Initialize* +#include <optional> static llvm::cl::list<std::string> - ClangArgs("Xcc", llvm::cl::ZeroOrMore, + ClangArgs("Xcc", 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, +static void LLVMErrorHandler(void *UserData, const char *Message, bool GenCrashDiag) { auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData); @@ -49,11 +49,30 @@ static void LLVMErrorHandler(void *UserData, const std::string &Message, exit(GenCrashDiag ? 70 : 1); } +// If we are running with -verify a reported has to be returned as unsuccess. +// This is relevant especially for the test suite. +static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) { + unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors(); + if (CI->getDiagnosticOpts().VerifyDiagnostics) { + // If there was an error that came from the verifier we must return 1 as + // an exit code for the process. This will make the test fail as expected. + clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient(); + Client->EndSourceFile(); + Errs = Client->getNumErrors(); + + // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced. + Client->BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor()); + } + return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS; +} + llvm::ExitOnError ExitOnErr; int main(int argc, const char **argv) { ExitOnErr.setBanner("clang-repl: "); llvm::cl::ParseCommandLineOptions(argc, argv); + llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + std::vector<const char *> ClangArgv(ClangArgs.size()); std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), [](const std::string &s) -> const char * { return s.data(); }); @@ -80,20 +99,35 @@ int main(int argc, const char **argv) { llvm::install_fatal_error_handler(LLVMErrorHandler, static_cast<void *>(&CI->getDiagnostics())); + // Load any requested plugins. + CI->LoadRequestedPlugins(); + 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: "); } + bool HasError = false; + if (OptInputs.empty()) { llvm::LineEditor LE("clang-repl"); // FIXME: Add LE.setListCompleter - while (llvm::Optional<std::string> Line = LE.readLine()) { - if (*Line == "quit") + while (std::optional<std::string> Line = LE.readLine()) { + if (*Line == R"(%quit)") break; - if (auto Err = Interp->ParseAndExecute(*Line)) + if (*Line == R"(%undo)") { + if (auto Err = Interp->Undo()) { + llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); + HasError = true; + } + continue; + } + + if (auto Err = Interp->ParseAndExecute(*Line)) { llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); + HasError = true; + } } } @@ -102,7 +136,5 @@ int main(int argc, const char **argv) { // later errors use the default handling behavior instead. llvm::remove_fatal_error_handler(); - llvm::llvm_shutdown(); - - return 0; + return checkDiagErrors(Interp->getCompilerInstance(), HasError); } diff --git a/gnu/llvm/clang/tools/clang-scan-deps/CMakeLists.txt b/gnu/llvm/clang/tools/clang-scan-deps/CMakeLists.txt index 6aa914f3b25..4db565314c0 100644 --- a/gnu/llvm/clang/tools/clang-scan-deps/CMakeLists.txt +++ b/gnu/llvm/clang/tools/clang-scan-deps/CMakeLists.txt @@ -1,6 +1,8 @@ set(LLVM_LINK_COMPONENTS Core + Option Support + TargetParser ) add_clang_tool(clang-scan-deps diff --git a/gnu/llvm/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/gnu/llvm/clang/tools/clang-scan-deps/ClangScanDeps.cpp index 74784ebd3b9..9c7d454c9ef 100644 --- a/gnu/llvm/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/gnu/llvm/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" @@ -16,6 +18,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Program.h" @@ -23,6 +26,7 @@ #include "llvm/Support/ThreadPool.h" #include "llvm/Support/Threading.h" #include <mutex> +#include <optional> #include <thread> using namespace clang; @@ -81,7 +85,7 @@ public: "" /*no-suffix*/, ErrorFile); llvm::FileRemover OutputRemover(OutputFile.c_str()); llvm::FileRemover ErrorRemover(ErrorFile.c_str()); - llvm::Optional<StringRef> Redirects[] = { + std::optional<StringRef> Redirects[] = { {""}, // Stdin OutputFile.str(), ErrorFile.str(), @@ -116,60 +120,48 @@ static llvm::cl::opt<ScanningMode> ScanMode( "mode", llvm::cl::desc("The preprocessing mode used to compute the dependencies"), llvm::cl::values( - clEnumValN(ScanningMode::MinimizedSourcePreprocessing, - "preprocess-minimized-sources", - "The set of dependencies is computed by preprocessing the " - "source files that were minimized to only include the " - "contents that might affect the dependencies"), + clEnumValN(ScanningMode::DependencyDirectivesScan, + "preprocess-dependency-directives", + "The set of dependencies is computed by preprocessing with " + "special lexing after scanning the source files to get the " + "directives that might affect the dependencies"), clEnumValN(ScanningMode::CanonicalPreprocessing, "preprocess", "The set of dependencies is computed by preprocessing the " - "unmodified source files")), - llvm::cl::init(ScanningMode::MinimizedSourcePreprocessing), + "source files")), + llvm::cl::init(ScanningMode::DependencyDirectivesScan), llvm::cl::cat(DependencyScannerCategory)); static llvm::cl::opt<ScanningOutputFormat> Format( "format", llvm::cl::desc("The output format for the dependencies"), - llvm::cl::values(clEnumValN(ScanningOutputFormat::Make, "make", - "Makefile compatible dep file"), - clEnumValN(ScanningOutputFormat::Full, "experimental-full", - "Full dependency graph suitable" - " for explicitly building modules. This format " - "is experimental and will change.")), + llvm::cl::values( + clEnumValN(ScanningOutputFormat::Make, "make", + "Makefile compatible dep file"), + clEnumValN(ScanningOutputFormat::P1689, "p1689", + "Generate standard c++ modules dependency P1689 format"), + clEnumValN(ScanningOutputFormat::Full, "experimental-full", + "Full dependency graph suitable" + " for explicitly building modules. This format " + "is experimental and will change.")), llvm::cl::init(ScanningOutputFormat::Make), llvm::cl::cat(DependencyScannerCategory)); -// This mode is mostly useful for development of explicitly built modules. -// Command lines will contain arguments specifying modulemap file paths and -// absolute paths to PCM files in the module cache directory. -// -// Build tools that want to put the PCM files in a different location should use -// the C++ APIs instead, of which there are two flavors: -// -// 1. APIs that generate arguments with paths to modulemap and PCM files via -// callbacks provided by the client: -// * ModuleDeps::getCanonicalCommandLine(LookupPCMPath, LookupModuleDeps) -// * FullDependencies::getAdditionalArgs(LookupPCMPath, LookupModuleDeps) -// -// 2. APIs that don't generate arguments with paths to modulemap or PCM files -// and instead expect the client to append them manually after the fact: -// * ModuleDeps::getCanonicalCommandLineWithoutModulePaths() -// * FullDependencies::getAdditionalArgsWithoutModulePaths() -// -static llvm::cl::opt<bool> GenerateModulesPathArgs( - "generate-modules-path-args", - llvm::cl::desc( - "With '-format experimental-full', include arguments specifying " - "modules-related paths in the generated command lines: " - "'-fmodule-file=', '-o', '-fmodule-map-file='."), - llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory)); - static llvm::cl::opt<std::string> ModuleFilesDir( "module-files-dir", - llvm::cl::desc("With '-generate-modules-path-args', paths to module files " - "in the generated command lines will begin with the " - "specified directory instead the module cache directory."), + llvm::cl::desc( + "The build directory for modules. Defaults to the value of " + "'-fmodules-cache-path=' from command lines for implicit modules."), llvm::cl::cat(DependencyScannerCategory)); +static llvm::cl::opt<bool> OptimizeArgs( + "optimize-args", + llvm::cl::desc("Whether to optimize command-line arguments of modules."), + llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory)); + +static llvm::cl::opt<bool> EagerLoadModules( + "eager-load-pcm", + llvm::cl::desc("Load PCM files eagerly (instead of lazily on import)."), + llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory)); + llvm::cl::opt<unsigned> NumThreads("j", llvm::cl::Optional, llvm::cl::desc("Number of worker threads to use (default: use " @@ -178,21 +170,47 @@ llvm::cl::opt<unsigned> llvm::cl::opt<std::string> CompilationDB("compilation-database", - llvm::cl::desc("Compilation database"), llvm::cl::Required, + llvm::cl::desc("Compilation database"), llvm::cl::Optional, llvm::cl::cat(DependencyScannerCategory)); -llvm::cl::opt<bool> ReuseFileManager( - "reuse-filemanager", - llvm::cl::desc("Reuse the file manager and its cache between invocations."), - llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory)); +llvm::cl::opt<std::string> P1689TargettedCommand( + llvm::cl::Positional, llvm::cl::ZeroOrMore, + llvm::cl::desc("The command line flags for the target of which " + "the dependencies are to be computed.")); -llvm::cl::opt<bool> SkipExcludedPPRanges( - "skip-excluded-pp-ranges", - llvm::cl::desc( - "Use the preprocessor optimization that skips excluded conditionals by " - "bumping the buffer pointer in the lexer instead of lexing the tokens " - "until reaching the end directive."), - llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory)); +llvm::cl::opt<std::string> ModuleName( + "module-name", llvm::cl::Optional, + llvm::cl::desc("the module of which the dependencies are to be computed"), + llvm::cl::cat(DependencyScannerCategory)); + +llvm::cl::list<std::string> ModuleDepTargets( + "dependency-target", + llvm::cl::desc("The names of dependency targets for the dependency file"), + llvm::cl::cat(DependencyScannerCategory)); + +llvm::cl::opt<bool> DeprecatedDriverCommand( + "deprecated-driver-command", llvm::cl::Optional, + llvm::cl::desc("use a single driver command to build the tu (deprecated)"), + llvm::cl::cat(DependencyScannerCategory)); + +enum ResourceDirRecipeKind { + RDRK_ModifyCompilerPath, + RDRK_InvokeCompiler, +}; + +static llvm::cl::opt<ResourceDirRecipeKind> ResourceDirRecipe( + "resource-dir-recipe", + llvm::cl::desc("How to produce missing '-resource-dir' argument"), + llvm::cl::values( + clEnumValN(RDRK_ModifyCompilerPath, "modify-compiler-path", + "Construct the resource directory from the compiler path in " + "the compilation database. This assumes it's part of the " + "same toolchain as this clang-scan-deps. (default)"), + clEnumValN(RDRK_InvokeCompiler, "invoke-compiler", + "Invoke the compiler with '-print-resource-dir' and use the " + "reported path as the resource directory. (deprecated)")), + llvm::cl::init(RDRK_ModifyCompilerPath), + llvm::cl::cat(DependencyScannerCategory)); llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional, llvm::cl::desc("Use verbose output."), @@ -201,24 +219,6 @@ llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional, } // end anonymous namespace -class SingleCommandCompilationDatabase : public tooling::CompilationDatabase { -public: - SingleCommandCompilationDatabase(tooling::CompileCommand Cmd) - : Command(std::move(Cmd)) {} - - std::vector<tooling::CompileCommand> - getCompileCommands(StringRef FilePath) const override { - return {Command}; - } - - std::vector<tooling::CompileCommand> getAllCompileCommands() const override { - return {Command}; - } - -private: - tooling::CompileCommand Command; -}; - /// Takes the result of a dependency scan and prints error / dependency files /// based on the result. /// @@ -267,7 +267,7 @@ class FullDeps { public: void mergeDeps(StringRef Input, FullDependenciesResult FDR, size_t InputIndex) { - const FullDependencies &FD = FDR.FullDeps; + FullDependencies &FD = FDR.FullDeps; InputDeps ID; ID.FileName = std::string(Input); @@ -285,15 +285,8 @@ public: Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)}); } - ID.AdditionalCommandLine = - GenerateModulesPathArgs - ? FD.getAdditionalArgs( - [&](ModuleID MID) { return lookupPCMPath(MID); }, - [&](ModuleID MID) -> const ModuleDeps & { - return lookupModuleDeps(MID); - }) - : FD.getAdditionalArgsWithoutModulePaths(); - + ID.DriverCommandLine = std::move(FD.DriverCommandLine); + ID.Commands = std::move(FD.Commands); Inputs.push_back(std::move(ID)); } @@ -323,28 +316,40 @@ public: {"file-deps", toJSONSorted(MD.FileDeps)}, {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)}, {"clang-modulemap-file", MD.ClangModuleMapFile}, - {"command-line", - GenerateModulesPathArgs - ? MD.getCanonicalCommandLine( - [&](ModuleID MID) { return lookupPCMPath(MID); }, - [&](ModuleID MID) -> const ModuleDeps & { - return lookupModuleDeps(MID); - }) - : MD.getCanonicalCommandLineWithoutModulePaths()}, + {"command-line", MD.BuildArguments}, }; OutModules.push_back(std::move(O)); } Array TUs; for (auto &&I : Inputs) { - Object O{ - {"input-file", I.FileName}, - {"clang-context-hash", I.ContextHash}, - {"file-deps", I.FileDeps}, - {"clang-module-deps", toJSONSorted(I.ModuleDeps)}, - {"command-line", I.AdditionalCommandLine}, - }; - TUs.push_back(std::move(O)); + 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{ @@ -356,31 +361,6 @@ public: } private: - StringRef lookupPCMPath(ModuleID MID) { - auto PCMPath = PCMPaths.insert({MID, ""}); - if (PCMPath.second) - PCMPath.first->second = constructPCMPath(lookupModuleDeps(MID)); - return PCMPath.first->second; - } - - /// Construct a path for the explicitly built PCM. - std::string constructPCMPath(const ModuleDeps &MD) const { - StringRef Filename = llvm::sys::path::filename(MD.ImplicitModulePCMPath); - - SmallString<256> ExplicitPCMPath( - !ModuleFilesDir.empty() - ? ModuleFilesDir - : MD.Invocation.getHeaderSearchOpts().ModuleCachePath); - llvm::sys::path::append(ExplicitPCMPath, MD.ID.ContextHash, Filename); - return std::string(ExplicitPCMPath); - } - - const ModuleDeps &lookupModuleDeps(ModuleID MID) { - auto I = Modules.find(IndexedModuleID{MID, 0}); - assert(I != Modules.end()); - return I->second; - }; - struct IndexedModuleID { ModuleID ID; mutable size_t InputIndex; @@ -404,13 +384,13 @@ private: std::string ContextHash; std::vector<std::string> FileDeps; std::vector<ModuleID> ModuleDeps; - std::vector<std::string> AdditionalCommandLine; + std::vector<std::string> DriverCommandLine; + std::vector<Command> Commands; }; std::mutex Lock; std::unordered_map<IndexedModuleID, ModuleDeps, IndexedModuleIDHasher> Modules; - std::unordered_map<ModuleID, std::string, ModuleIDHasher> PCMPaths; std::vector<InputDeps> Inputs; }; @@ -432,19 +412,231 @@ static bool handleFullDependencyToolResult( return false; } -int main(int argc, const char **argv) { +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); +} + +// getCompilationDataBase - If -compilation-database is set, load the +// compilation database from the specified file. Otherwise if the we're +// generating P1689 format, trying to generate the compilation database +// form specified command line after the positional parameter "--". +static std::unique_ptr<tooling::CompilationDatabase> +getCompilationDataBase(int argc, const char **argv, std::string &ErrorMessage) { llvm::InitLLVM X(argc, argv); llvm::cl::HideUnrelatedOptions(DependencyScannerCategory); if (!llvm::cl::ParseCommandLineOptions(argc, argv)) - return 1; + return nullptr; + + if (!CompilationDB.empty()) + return tooling::JSONCompilationDatabase::loadFromFile( + CompilationDB, ErrorMessage, + tooling::JSONCommandLineSyntax::AutoDetect); + + if (Format != ScanningOutputFormat::P1689) { + llvm::errs() << "the --compilation-database option: must be specified at " + "least once!"; + return nullptr; + } + + // Trying to get the input file, the output file and the command line options + // from the positional parameter "--". + const char **DoubleDash = std::find(argv, argv + argc, StringRef("--")); + if (DoubleDash == argv + argc) { + llvm::errs() << "The command line arguments is required after '--' in " + "P1689 per file mode."; + return nullptr; + } + std::vector<const char *> CommandLine(DoubleDash + 1, argv + argc); + + llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags = + CompilerInstance::createDiagnostics(new DiagnosticOptions); + driver::Driver TheDriver(CommandLine[0], llvm::sys::getDefaultTargetTriple(), + *Diags); + std::unique_ptr<driver::Compilation> C( + TheDriver.BuildCompilation(CommandLine)); + if (!C) + 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() << "Only one input file is allowed in P1689 per file mode."; + 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() << "The command line should provide exactly one output file " + "in P1689 per file mode.\n"; + } + 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 main(int argc, const char **argv) { std::string ErrorMessage; - std::unique_ptr<tooling::JSONCompilationDatabase> Compilations = - tooling::JSONCompilationDatabase::loadFromFile( - CompilationDB, ErrorMessage, - tooling::JSONCommandLineSyntax::AutoDetect); + std::unique_ptr<tooling::CompilationDatabase> Compilations = + getCompilationDataBase(argc, argv, ErrorMessage); if (!Compilations) { - llvm::errs() << "error: " << ErrorMessage << "\n"; + llvm::errs() << ErrorMessage << "\n"; return 1; } @@ -459,7 +651,7 @@ int main(int argc, const char **argv) { AdjustingCompilations->appendArgumentsAdjuster( [&ResourceDirCache](const tooling::CommandLineArguments &Args, StringRef FileName) { - std::string LastO = ""; + std::string LastO; bool HasResourceDir = false; bool ClangCLMode = false; auto FlagsEnd = llvm::find(Args, "--"); @@ -469,7 +661,7 @@ int main(int argc, const char **argv) { llvm::is_contained(Args, "--driver-mode=cl"); // Reverse scan, starting at the end or at the element before "--". - auto R = llvm::make_reverse_iterator(FlagsEnd); + auto R = std::make_reverse_iterator(FlagsEnd); for (auto I = R, E = Args.rend(); I != E; ++I) { StringRef Arg = *I; if (ClangCLMode) { @@ -503,7 +695,7 @@ int main(int argc, const char **argv) { AdjustedArgs.push_back("/clang:" + LastO); } - if (!HasResourceDir) { + if (!HasResourceDir && ResourceDirRecipe == RDRK_InvokeCompiler) { StringRef ResourceDir = ResourceDirCache.findResourceDir(Args, ClangCLMode); if (!ResourceDir.empty()) { @@ -519,20 +711,19 @@ int main(int argc, const char **argv) { // Print out the dependency results to STDOUT by default. SharedStream DependencyOS(llvm::outs()); - DependencyScanningService Service(ScanMode, Format, ReuseFileManager, - SkipExcludedPPRanges); + DependencyScanningService Service(ScanMode, Format, OptimizeArgs, + EagerLoadModules); llvm::ThreadPool Pool(llvm::hardware_concurrency(NumThreads)); std::vector<std::unique_ptr<DependencyScanningTool>> WorkerTools; for (unsigned I = 0; I < Pool.getThreadCount(); ++I) WorkerTools.push_back(std::make_unique<DependencyScanningTool>(Service)); - std::vector<SingleCommandCompilationDatabase> Inputs; - for (tooling::CompileCommand Cmd : - AdjustingCompilations->getAllCompileCommands()) - Inputs.emplace_back(Cmd); + std::vector<tooling::CompileCommand> Inputs = + AdjustingCompilations->getAllCompileCommands(); std::atomic<bool> HadErrors(false); FullDeps FD; + P1689Deps PD; std::mutex Lock; size_t Index = 0; @@ -541,11 +732,11 @@ int main(int argc, const char **argv) { << " files using " << Pool.getThreadCount() << " workers\n"; } for (unsigned I = 0; I < Pool.getThreadCount(); ++I) { - Pool.async([I, &Lock, &Index, &Inputs, &HadErrors, &FD, &WorkerTools, + Pool.async([I, &Lock, &Index, &Inputs, &HadErrors, &FD, &PD, &WorkerTools, &DependencyOS, &Errs]() { llvm::StringSet<> AlreadySeenModules; while (true) { - const SingleCommandCompilationDatabase *Input; + const tooling::CompileCommand *Input; std::string Filename; std::string CWD; size_t LocalIndex; @@ -556,19 +747,80 @@ int main(int argc, const char **argv) { return; LocalIndex = Index; Input = &Inputs[Index++]; - tooling::CompileCommand Cmd = Input->getAllCompileCommands()[0]; - Filename = std::move(Cmd.Filename); - CWD = std::move(Cmd.Directory); + Filename = std::move(Input->Filename); + 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 = WorkerTools[I]->getDependencyFile(*Input, CWD); + auto MaybeFile = WorkerTools[I]->getDependencyFile( + Input->CommandLine, CWD, MaybeModuleName); 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 = WorkerTools[I]->getP1689ModuleDependencyFile( + *Input, CWD, MakeformatOutput, MakeformatOutputPath); + HadErrors = + handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs); + + 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); + HadErrors = handleMakeDependencyToolResult(Filename, MaybeOutput, + MakeformatOS, Errs); + } + } else if (DeprecatedDriverCommand) { + auto MaybeFullDeps = + WorkerTools[I]->getFullDependenciesLegacyDriverCommand( + Input->CommandLine, CWD, AlreadySeenModules, LookupOutput, + MaybeModuleName); + if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD, + LocalIndex, DependencyOS, Errs)) + HadErrors = true; } else { auto MaybeFullDeps = WorkerTools[I]->getFullDependencies( - *Input, CWD, AlreadySeenModules); + Input->CommandLine, CWD, AlreadySeenModules, LookupOutput, + MaybeModuleName); if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD, LocalIndex, DependencyOS, Errs)) HadErrors = true; @@ -580,6 +832,8 @@ int main(int argc, const char **argv) { if (Format == ScanningOutputFormat::Full) FD.printFullOutput(llvm::outs()); + else if (Format == ScanningOutputFormat::P1689) + PD.printDependencies(llvm::outs()); return HadErrors; } diff --git a/gnu/llvm/clang/tools/diag-build/diag-build.sh b/gnu/llvm/clang/tools/diag-build/diag-build.sh index 018288dda95..b1504ffcc41 100755 --- a/gnu/llvm/clang/tools/diag-build/diag-build.sh +++ b/gnu/llvm/clang/tools/diag-build/diag-build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # diag-build: a tool showing enabled warnings in a project. # diff --git a/gnu/llvm/clang/tools/diagtool/DiagTool.cpp b/gnu/llvm/clang/tools/diagtool/DiagTool.cpp index 81d4e7e44cc..99abe5755f7 100644 --- a/gnu/llvm/clang/tools/diagtool/DiagTool.cpp +++ b/gnu/llvm/clang/tools/diagtool/DiagTool.cpp @@ -12,6 +12,7 @@ #include "DiagTool.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/STLExtras.h" #include <vector> using namespace diagtool; diff --git a/gnu/llvm/clang/tools/diagtool/DiagnosticNames.cpp b/gnu/llvm/clang/tools/diagtool/DiagnosticNames.cpp index c54f81481a2..852b8c226e8 100644 --- a/gnu/llvm/clang/tools/diagtool/DiagnosticNames.cpp +++ b/gnu/llvm/clang/tools/diagtool/DiagnosticNames.cpp @@ -20,16 +20,16 @@ static const DiagnosticRecord BuiltinDiagnosticsByName[] = { }; llvm::ArrayRef<DiagnosticRecord> diagtool::getBuiltinDiagnosticsByName() { - return llvm::makeArrayRef(BuiltinDiagnosticsByName); + return llvm::ArrayRef(BuiltinDiagnosticsByName); } // FIXME: Is it worth having two tables, especially when this one can get // out of sync easily? static const DiagnosticRecord BuiltinDiagnosticsByID[] = { -#define DIAG(ENUM,CLASS,DEFAULT_MAPPING,DESC,GROUP, \ - SFINAE,NOWERROR,SHOWINSYSHEADER,DEFER,CATEGORY) \ - { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) }, +#define DIAG(ENUM, CLASS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFER, CATEGORY) \ + {#ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t)}, #include "clang/Basic/DiagnosticCommonKinds.inc" #include "clang/Basic/DiagnosticCrossTUKinds.inc" #include "clang/Basic/DiagnosticDriverKinds.inc" @@ -66,9 +66,10 @@ const DiagnosticRecord &diagtool::getDiagnosticForID(short DiagID) { // Second the table of options, sorted by name for fast binary lookup. static const GroupRecord OptionTable[] = { -#define GET_DIAG_TABLE +#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs) \ + {FlagNameOffset, Members, SubGroups}, #include "clang/Basic/DiagnosticGroups.inc" -#undef GET_DIAG_TABLE +#undef DIAG_ENTRY }; llvm::StringRef GroupRecord::getName() const { @@ -102,5 +103,5 @@ GroupRecord::diagnostics() const { } llvm::ArrayRef<GroupRecord> diagtool::getDiagnosticGroups() { - return llvm::makeArrayRef(OptionTable); + return llvm::ArrayRef(OptionTable); } diff --git a/gnu/llvm/clang/tools/diagtool/FindDiagnosticID.cpp b/gnu/llvm/clang/tools/diagtool/FindDiagnosticID.cpp index 2a08814478f..6ced701f563 100644 --- a/gnu/llvm/clang/tools/diagtool/FindDiagnosticID.cpp +++ b/gnu/llvm/clang/tools/diagtool/FindDiagnosticID.cpp @@ -10,6 +10,7 @@ #include "DiagnosticNames.h" #include "clang/Basic/AllDiagnostics.h" #include "llvm/Support/CommandLine.h" +#include <optional> DEF_DIAGTOOL("find-diagnostic-id", "Print the id of the given diagnostic", FindDiagnosticID) @@ -26,14 +27,14 @@ static StringRef getNameFromID(StringRef Name) { return StringRef(); } -static Optional<DiagnosticRecord> +static std::optional<DiagnosticRecord> findDiagnostic(ArrayRef<DiagnosticRecord> Diagnostics, StringRef Name) { for (const auto &Diag : Diagnostics) { StringRef DiagName = Diag.getName(); if (DiagName == Name) return Diag; } - return None; + return std::nullopt; } int FindDiagnosticID::run(unsigned int argc, char **argv, @@ -47,7 +48,7 @@ int FindDiagnosticID::run(unsigned int argc, char **argv, std::vector<const char *> Args; Args.push_back("diagtool find-diagnostic-id"); - for (const char *A : llvm::makeArrayRef(argv, argc)) + for (const char *A : llvm::ArrayRef(argv, argc)) Args.push_back(A); llvm::cl::HideUnrelatedOptions(FindDiagnosticIDOptions); @@ -55,7 +56,7 @@ int FindDiagnosticID::run(unsigned int argc, char **argv, "Diagnostic ID mapping utility"); ArrayRef<DiagnosticRecord> AllDiagnostics = getBuiltinDiagnosticsByName(); - Optional<DiagnosticRecord> Diag = + std::optional<DiagnosticRecord> Diag = findDiagnostic(AllDiagnostics, DiagnosticName); if (!Diag) { // Name to id failed, so try id to name. diff --git a/gnu/llvm/clang/tools/diagtool/ShowEnabledWarnings.cpp b/gnu/llvm/clang/tools/diagtool/ShowEnabledWarnings.cpp index ae2d3e37e84..285efe6ae05 100644 --- a/gnu/llvm/clang/tools/diagtool/ShowEnabledWarnings.cpp +++ b/gnu/llvm/clang/tools/diagtool/ShowEnabledWarnings.cpp @@ -59,15 +59,16 @@ createDiagnostics(unsigned int argc, char **argv) { // Buffer diagnostics from argument parsing so that we can output them using a // well formed diagnostic object. TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; - IntrusiveRefCntPtr<DiagnosticsEngine> InterimDiags( - new DiagnosticsEngine(DiagIDs, new DiagnosticOptions(), DiagsBuffer)); // Try to build a CompilerInvocation. SmallVector<const char *, 4> Args; Args.push_back("diagtool"); Args.append(argv, argv + argc); + CreateInvocationOptions CIOpts; + CIOpts.Diags = + new DiagnosticsEngine(DiagIDs, new DiagnosticOptions(), DiagsBuffer); std::unique_ptr<CompilerInvocation> Invocation = - createInvocationFromCommandLine(Args, InterimDiags); + createInvocation(Args, CIOpts); if (!Invocation) return nullptr; diff --git a/gnu/llvm/clang/tools/diagtool/TreeView.cpp b/gnu/llvm/clang/tools/diagtool/TreeView.cpp index 843bd377e57..92d92aa9069 100644 --- a/gnu/llvm/clang/tools/diagtool/TreeView.cpp +++ b/gnu/llvm/clang/tools/diagtool/TreeView.cpp @@ -40,11 +40,7 @@ public: if (!Group.diagnostics().empty()) return false; - for (const GroupRecord &GR : Group.subgroups()) - if (!unimplemented(GR)) - return false; - - return true; + return llvm::all_of(Group.subgroups(), unimplemented); } static bool enabledByDefault(const GroupRecord &Group) { diff --git a/gnu/llvm/clang/tools/driver/CMakeLists.txt b/gnu/llvm/clang/tools/driver/CMakeLists.txt index 7c32aadb870..237ed453e28 100644 --- a/gnu/llvm/clang/tools/driver/CMakeLists.txt +++ b/gnu/llvm/clang/tools/driver/CMakeLists.txt @@ -13,12 +13,11 @@ set( LLVM_LINK_COMPONENTS Option ScalarOpts Support + TargetParser TransformUtils Vectorize ) -option(CLANG_PLUGIN_SUPPORT "Build clang with plugin support" ON) - # Support plugins. if(CLANG_PLUGIN_SUPPORT) set(support_plugins SUPPORT_PLUGINS) @@ -33,6 +32,7 @@ add_clang_tool(clang DEPENDS intrinsics_gen ${support_plugins} + GENERATE_DRIVER ) clang_target_link_libraries(clang @@ -62,7 +62,11 @@ if(NOT CLANG_LINKS_TO_CREATE) set(CLANG_LINKS_TO_CREATE clang++ clang-cl clang-cpp) endif() -foreach(link ${CLANG_LINKS_TO_CREATE}) +if (CLANG_ENABLE_HLSL) + set(HLSL_LINK clang-dxc) +endif() + +foreach(link ${CLANG_LINKS_TO_CREATE} ${HLSL_LINK}) add_clang_symlink(${link} clang) endforeach() @@ -82,7 +86,7 @@ if (APPLE) set(TOOL_INFO_PLIST_OUT "${CMAKE_CURRENT_BINARY_DIR}/${TOOL_INFO_PLIST}") target_link_libraries(clang PRIVATE - "-Wl,-sectcreate,__TEXT,__info_plist,${TOOL_INFO_PLIST_OUT}") + "-Wl,-sectcreate,__TEXT,__info_plist,\"${TOOL_INFO_PLIST_OUT}\"") configure_file("${TOOL_INFO_PLIST}.in" "${TOOL_INFO_PLIST_OUT}" @ONLY) set(TOOL_INFO_UTI) @@ -95,7 +99,7 @@ if(CLANG_ORDER_FILE AND (LLVM_LINKER_IS_LD64 OR LLVM_LINKER_IS_GOLD OR LLVM_LINKER_IS_LLD)) include(LLVMCheckLinkerFlag) - if (LLVM_LINKER_IS_LD64) + if (LLVM_LINKER_IS_LD64 OR (LLVM_LINKER_IS_LLD AND APPLE)) set(LINKER_ORDER_FILE_OPTION "-Wl,-order_file,${CLANG_ORDER_FILE}") elseif (LLVM_LINKER_IS_GOLD) set(LINKER_ORDER_FILE_OPTION "-Wl,--section-ordering-file,${CLANG_ORDER_FILE}") diff --git a/gnu/llvm/clang/tools/driver/cc1_main.cpp b/gnu/llvm/clang/tools/driver/cc1_main.cpp index 396d6ff529f..c79306b6f7d 100644 --- a/gnu/llvm/clang/tools/driver/cc1_main.cpp +++ b/gnu/llvm/clang/tools/driver/cc1_main.cpp @@ -28,6 +28,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Config/llvm-config.h" #include "llvm/LinkAllPasses.h" +#include "llvm/MC/TargetRegistry.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/OptTable.h" @@ -38,7 +39,6 @@ #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" @@ -57,7 +57,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); @@ -177,7 +177,8 @@ 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; } @@ -212,7 +213,9 @@ 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().TimeTrace || + !Clang->getFrontendOpts().TimeTracePath.empty()) { + Clang->getFrontendOpts().TimeTrace = 1; llvm::timeTraceProfilerInitialize( Clang->getFrontendOpts().TimeTraceGranularity, Argv0); } @@ -237,8 +240,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. { @@ -254,12 +259,18 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) { if (llvm::timeTraceProfilerEnabled()) { SmallString<128> Path(Clang->getFrontendOpts().OutputFile); llvm::sys::path::replace_extension(Path, "json"); + if (!Clang->getFrontendOpts().TimeTracePath.empty()) { + // replace the suffix to '.json' directly + SmallString<128> TracePath(Clang->getFrontendOpts().TimeTracePath); + if (llvm::sys::fs::is_directory(TracePath)) + llvm::sys::path::append(TracePath, llvm::sys::path::filename(Path)); + Path.assign(TracePath); + } if (auto profilerOutput = Clang->createOutputFile( Path.str(), /*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/gnu/llvm/clang/tools/driver/cc1as_main.cpp b/gnu/llvm/clang/tools/driver/cc1as_main.cpp index 086ce0ea778..f944113476f 100644 --- a/gnu/llvm/clang/tools/driver/cc1as_main.cpp +++ b/gnu/llvm/clang/tools/driver/cc1as_main.cpp @@ -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" @@ -49,11 +50,11 @@ #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 <memory> +#include <optional> #include <system_error> using namespace clang; using namespace clang::driver; @@ -134,9 +135,13 @@ struct AssemblerInvocation { unsigned NoExecStack : 1; unsigned FatalWarnings : 1; unsigned NoWarn : 1; + unsigned NoTypeCheck : 1; unsigned IncrementalLinkerCompatible : 1; unsigned EmbedBitcode : 1; + /// Whether to emit DWARF unwind info. + EmitDwarfUnwindType EmitDwarfUnwind; + /// The name of the relocation model to use. std::string RelocationModel; @@ -144,6 +149,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: @@ -160,10 +175,12 @@ public: NoExecStack = 0; FatalWarnings = 0; NoWarn = 0; + NoTypeCheck = 0; IncrementalLinkerCompatible = 0; Dwarf64 = 0; DwarfVersion = 0; EmbedBitcode = 0; + EmitDwarfUnwind = EmitDwarfUnwindType::Default; } static bool CreateFromArgs(AssemblerInvocation &Res, @@ -209,6 +226,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 +255,12 @@ 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); 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); @@ -295,6 +323,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 +341,16 @@ 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.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file); + return Success; } @@ -362,6 +401,9 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, assert(MRI && "Unable to create target register info!"); MCTargetOptions MCOptions; + MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind; + MCOptions.AsSecureLogFile = Opts.AsSecureLogFile; + std::unique_ptr<MCAsmInfo> MAI( TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions)); assert(MAI && "Unable to create target asm info!"); @@ -408,6 +450,10 @@ 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) @@ -447,6 +493,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, MCOptions.MCNoWarn = Opts.NoWarn; MCOptions.MCFatalWarnings = Opts.FatalWarnings; + MCOptions.MCNoTypeCheck = Opts.NoTypeCheck; MCOptions.ABIName = Opts.TargetABI; // FIXME: There is a bit of code duplication with addPassesToEmitFile. @@ -456,7 +503,7 @@ 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)); @@ -476,7 +523,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!"); @@ -490,7 +537,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI, Opts.RelaxAll, Opts.IncrementalLinkerCompatible, /*DWARFMustBeAtTheEnd*/ true)); - Str.get()->InitSections(Opts.NoExecStack); + Str.get()->initSections(Opts.NoExecStack, *STI); } // When -fembed-bitcode is passed to clang_as, a 1-byte marker @@ -498,7 +545,7 @@ 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); } @@ -550,7 +597,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); diff --git a/gnu/llvm/clang/tools/driver/cc1gen_reproducer_main.cpp b/gnu/llvm/clang/tools/driver/cc1gen_reproducer_main.cpp index 89b7227fdb1..9dbfc518add 100644 --- a/gnu/llvm/clang/tools/driver/cc1gen_reproducer_main.cpp +++ b/gnu/llvm/clang/tools/driver/cc1gen_reproducer_main.cpp @@ -23,6 +23,7 @@ #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; @@ -108,7 +109,7 @@ 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) { using namespace driver; @@ -134,7 +135,7 @@ generateReproducerForInvocationArguments(ArrayRef<const char *> Argv, } } - return None; + return std::nullopt; } std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes); @@ -180,7 +181,7 @@ 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 = + std::optional<driver::Driver::CompilationDiagnosticReport> Report = generateReproducerForInvocationArguments(DriverArgs, InvocationInfo); // Emit the information about the reproduce files to stdout. diff --git a/gnu/llvm/clang/tools/driver/driver.cpp b/gnu/llvm/clang/tools/driver/driver.cpp index 5a453429e79..d7474123365 100644 --- a/gnu/llvm/clang/tools/driver/driver.cpp +++ b/gnu/llvm/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" @@ -48,6 +49,7 @@ #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" #include <memory> +#include <optional> #include <set> #include <system_error> using namespace clang; @@ -120,7 +122,7 @@ static void ApplyOneQAOverride(raw_ostream &OS, 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) { + Edit.slice(2, Edit.size() - 1).contains('/')) { StringRef MatchPattern = Edit.substr(2).split('/').first; StringRef ReplPattern = Edit.substr(2).split('/').second; ReplPattern = ReplPattern.slice(0, ReplPattern.size()-1); @@ -243,29 +245,68 @@ 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); + + return true; } static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient, @@ -278,27 +319,6 @@ 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 @@ -329,18 +349,19 @@ 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], + return cc1gen_reproducer_main(ArrayRef(ArgV).slice(2), ArgV[0], GetExecutablePathVP); // Reject unknown tools. llvm::errs() << "error: unknown integrated tool '" << Tool << "'. " @@ -348,7 +369,7 @@ static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV) { return 1; } -int main(int Argc, const char **Argv) { +int clang_main(int Argc, char **Argv) { noteBottomOfStack(); llvm::InitLLVM X(Argc, Argv); llvm::setBugReportMsg("PLEASE submit a bug report to " BUG_REPORT_URL @@ -372,7 +393,7 @@ int main(int Argc, const char **Argv) { // 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. bool ClangCLMode = - IsClangCL(getDriverMode(Args[0], llvm::makeArrayRef(Args).slice(1))); + IsClangCL(getDriverMode(Args[0], llvm::ArrayRef(Args).slice(1))); enum { Default, POSIX, Windows } RSPQuoting = Default; for (const char *F : Args) { if (strcmp(F, "--rsp-quoting=posix") == 0) @@ -394,12 +415,17 @@ int main(int Argc, const char **Argv) { if (MarkEOLs && Args.size() > 1 && StringRef(Args[1]).startswith("-cc1")) MarkEOLs = false; - llvm::cl::ExpandResponseFiles(Saver, Tokenizer, Args, MarkEOLs); + llvm::cl::ExpansionContext ECtx(A, Tokenizer); + ECtx.setMarkEOLs(MarkEOLs); + if (llvm::Error Err = ECtx.expandResponseFiles(Args)) { + llvm::errs() << toString(std::move(Err)) << '\n'; + return 1; + } // 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; }); + auto FirstArg = llvm::find_if(llvm::drop_begin(Args), + [](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) { @@ -416,29 +442,29 @@ 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()); @@ -459,10 +485,15 @@ int main(int Argc, const char **Argv) { // 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); @@ -489,7 +520,8 @@ int main(int Argc, const char **Argv) { insertTargetAndModeArgs(TargetAndMode, Args, SavedStrings); - SetBackdoorDriverOutputsFromEnvVars(TheDriver); + if (!SetBackdoorDriverOutputsFromEnvVars(TheDriver)) + return 1; if (!UseNewCC1Process) { TheDriver.CC1Main = &ExecuteCC1Tool; @@ -498,32 +530,40 @@ int main(int Argc, const char **Argv) { } 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 +582,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) { diff --git a/gnu/llvm/clang/tools/include-mapping/cppreference_parser.py b/gnu/llvm/clang/tools/include-mapping/cppreference_parser.py new file mode 100644 index 00000000000..759269bffaa --- /dev/null +++ b/gnu/llvm/clang/tools/include-mapping/cppreference_parser.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +#===- cppreference_parser.py - ------------------------------*- python -*--===# +# +# 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 +# +#===------------------------------------------------------------------------===# + +from bs4 import BeautifulSoup, NavigableString + +import collections +import multiprocessing +import os +import re +import signal +import sys + + +class Symbol: + + def __init__(self, name, namespace, headers): + # unqualifed symbol name, e.g. "move" + self.name = name + # namespace of the symbol (with trailing "::"), e.g. "std::", "" (global scope) + # None for C symbols. + self.namespace = namespace + # a list of corresponding headers + self.headers = headers + + +def _HasClass(tag, *classes): + for c in tag.get('class', []): + if c in classes: + return True + return False + + +def _ParseSymbolPage(symbol_page_html, symbol_name): + """Parse symbol page and retrieve the include header defined in this page. + The symbol page provides header for the symbol, specifically in + "Defined in header <header>" section. An example: + + <tr class="t-dsc-header"> + <td colspan="2"> <div>Defined in header <code><ratio></code> </div> + </td></tr> + + Returns a list of headers. + """ + headers = set() + all_headers = set() + + soup = BeautifulSoup(symbol_page_html, "html.parser") + # Rows in table are like: + # Defined in header <foo> .t-dsc-header + # Defined in header <bar> .t-dsc-header + # decl1 .t-dcl + # Defined in header <baz> .t-dsc-header + # decl2 .t-dcl + for table in soup.select('table.t-dcl-begin, table.t-dsc-begin'): + current_headers = [] + was_decl = False + for row in table.select('tr'): + if _HasClass(row, 't-dcl', 't-dsc'): + was_decl = True + # Symbols are in the first cell. + found_symbols = row.find('td').stripped_strings + if not symbol_name in found_symbols: + continue + headers.update(current_headers) + elif _HasClass(row, 't-dsc-header'): + # If we saw a decl since the last header, this is a new block of headers + # for a new block of decls. + if was_decl: + current_headers = [] + was_decl = False + # There are also .t-dsc-header for "defined in namespace". + if not "Defined in header " in row.text: + continue + # The interesting header content (e.g. <cstdlib>) is wrapped in <code>. + for header_code in row.find_all("code"): + current_headers.append(header_code.text) + all_headers.add(header_code.text) + # If the symbol was never named, consider all named headers. + return headers or all_headers + + +def _ParseIndexPage(index_page_html): + """Parse index page. + The index page lists all std symbols and hrefs to their detailed pages + (which contain the defined header). An example: + + <a href="abs.html" title="abs"><tt>abs()</tt></a> (int) <br> + <a href="acos.html" title="acos"><tt>acos()</tt></a> <br> + + Returns a list of tuple (symbol_name, relative_path_to_symbol_page, variant). + """ + symbols = [] + soup = BeautifulSoup(index_page_html, "html.parser") + for symbol_href in soup.select("a[title]"): + # Ignore annotated symbols like "acos<>() (std::complex)". + # These tend to be overloads, and we the primary is more useful. + # This accidentally accepts begin/end despite the (iterator) caption: the + # (since C++11) note is first. They are good symbols, so the bug is unfixed. + caption = symbol_href.next_sibling + variant = None + if isinstance(caption, NavigableString) and "(" in caption: + variant = caption.text.strip(" ()") + symbol_tt = symbol_href.find("tt") + if symbol_tt: + symbols.append((symbol_tt.text.rstrip("<>()"), # strip any trailing <>() + symbol_href["href"], variant)) + return symbols + + +def _ReadSymbolPage(path, name): + with open(path) as f: + return _ParseSymbolPage(f.read(), name) + + +def _GetSymbols(pool, root_dir, index_page_name, namespace, variants_to_accept): + """Get all symbols listed in the index page. All symbols should be in the + given namespace. + + Returns a list of Symbols. + """ + + # Workflow steps: + # 1. Parse index page which lists all symbols to get symbol + # name (unqualified name) and its href link to the symbol page which + # contains the defined header. + # 2. Parse the symbol page to get the defined header. + index_page_path = os.path.join(root_dir, index_page_name) + with open(index_page_path, "r") as f: + # Read each symbol page in parallel. + results = [] # (symbol_name, promise of [header...]) + for symbol_name, symbol_page_path, variant in _ParseIndexPage(f.read()): + # Variant symbols (e.g. the std::locale version of isalpha) add ambiguity. + # FIXME: use these as a fallback rather than ignoring entirely. + variants_for_symbol = variants_to_accept.get( + (namespace or "") + symbol_name, ()) + if variant and variant not in variants_for_symbol: + continue + path = os.path.join(root_dir, symbol_page_path) + if os.path.isfile(path): + results.append((symbol_name, + pool.apply_async(_ReadSymbolPage, (path, symbol_name)))) + else: + sys.stderr.write("Discarding information for symbol: %s. Page %s does not exist.\n" + % (symbol_name, path)) + + # Build map from symbol name to a set of headers. + symbol_headers = collections.defaultdict(set) + for symbol_name, lazy_headers in results: + symbol_headers[symbol_name].update(lazy_headers.get()) + + symbols = [] + for name, headers in sorted(symbol_headers.items(), key=lambda t : t[0]): + symbols.append(Symbol(name, namespace, list(headers))) + return symbols + + +def GetSymbols(parse_pages): + """Get all symbols by parsing the given pages. + + Args: + parse_pages: a list of tuples (page_root_dir, index_page_name, namespace) + """ + # By default we prefer the non-variant versions, as they're more common. But + # there are some symbols, whose variant is more common. This list describes + # those symbols. + variants_to_accept = { + # std::remove<> has variant algorithm. + "std::remove": ("algorithm"), + } + symbols = [] + # Run many workers to process individual symbol pages under the symbol index. + # Don't allow workers to capture Ctrl-C. + pool = multiprocessing.Pool( + initializer=lambda: signal.signal(signal.SIGINT, signal.SIG_IGN)) + try: + for root_dir, page_name, namespace in parse_pages: + symbols.extend(_GetSymbols(pool, root_dir, page_name, namespace, + variants_to_accept)) + finally: + pool.terminate() + pool.join() + return symbols diff --git a/gnu/llvm/clang/tools/include-mapping/gen_std.py b/gnu/llvm/clang/tools/include-mapping/gen_std.py new file mode 100755 index 00000000000..db70771e0f5 --- /dev/null +++ b/gnu/llvm/clang/tools/include-mapping/gen_std.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +#===- gen_std.py - ------------------------------------------*- python -*--===# +# +# 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 +# +#===------------------------------------------------------------------------===# + +"""gen_std.py is a tool to generate a lookup table (from qualified names to +include headers) for C/C++ Standard Library symbols by parsing archived HTML +files from cppreference. + +The generated files are located in clang/include/Tooling/Inclusions. + +Caveats and FIXMEs: + - only symbols directly in "std" namespace are added, we should also add std's + subnamespace symbols (e.g. chrono). + - symbols with multiple variants or defined in multiple headers aren't added, + e.g. std::move, std::swap + +Usage: + 1. Install BeautifulSoup dependency, see instruction: + https://www.crummy.com/software/BeautifulSoup/bs4/doc/#installing-beautiful-soup + 2. Download cppreference offline HTML files (e.g. html_book_20181028.zip) at + https://en.cppreference.com/w/Cppreference:Archives + 3. Unzip the zip file from step 2 (e.g., to a "cppreference" directory). You should + get a "cppreference/reference" directory. + 4. Run the command: + // Generate C++ symbols + python3 gen_std.py -cppreference cppreference/reference -symbols=cpp > StdSymbolMap.inc + // Generate C++ removed symbols + python3 gen_std.py -cppreference cppreference/reference -symbols=cpp_removed > RemovedSymbolMap.inc + // Generate C symbols + python3 gen_std.py -cppreference cppreference/reference -symbols=c > CSymbolMap.inc +""" + + +import cppreference_parser +import argparse +import datetime +import os +import sys + +CODE_PREFIX = """\ +//===-- gen_std.py generated file -------------------------------*- C++ -*-===// +// +// Used to build a lookup table (qualified names => include headers) for %s +// Standard Library symbols. +// +// This file was generated automatically by +// clang/tools/include-mapping/gen_std.py, DO NOT EDIT! +// +// Generated from cppreference offline HTML book (modified on %s). +//===----------------------------------------------------------------------===// +""" + +def ParseArg(): + parser = argparse.ArgumentParser(description='Generate StdGen file') + parser.add_argument('-cppreference', metavar='PATH', + default='', + help='path to the cppreference offline HTML directory', + required=True + ) + parser.add_argument('-symbols', + default='cpp', + help='Generate c or cpp (removed) symbols. One of {cpp, c, cpp_removed}.', + required=True) + return parser.parse_args() + + +def main(): + args = ParseArg() + if args.symbols == 'cpp': + page_root = os.path.join(args.cppreference, "en", "cpp") + symbol_index_root = os.path.join(page_root, "symbol_index") + parse_pages = [ + (page_root, "symbol_index.html", "std::"), + # std sub-namespace symbols have separated pages. + # We don't index std literal operators (e.g. + # std::literals::chrono_literals::operator""d), these symbols can't be + # accessed by std::<symbol_name>. + # FIXME: index std::placeholders symbols, placeholders.html page is + # different (which contains one entry for _1, _2, ..., _N), we need special + # handling. + (symbol_index_root, "chrono.html", "std::chrono::"), + (symbol_index_root, "filesystem.html", "std::filesystem::"), + (symbol_index_root, "pmr.html", "std::pmr::"), + (symbol_index_root, "regex_constants.html", "std::regex_constants::"), + (symbol_index_root, "this_thread.html", "std::this_thread::"), + ] + elif args.symbols == 'cpp_removed': + page_root = os.path.join(args.cppreference, "en", "cpp") + symbol_index_root = os.path.join(page_root, "symbol_index") + parse_pages = [(symbol_index_root, "zombie_names.html", "std::")] + elif args.symbols == 'c': + page_root = os.path.join(args.cppreference, "en", "c") + symbol_index_root = page_root + parse_pages = [(page_root, "index.html", None)] + + if not os.path.exists(symbol_index_root): + exit("Path %s doesn't exist!" % symbol_index_root) + + symbols = cppreference_parser.GetSymbols(parse_pages) + + # We don't have version information from the unzipped offline HTML files. + # so we use the modified time of the symbol_index.html as the version. + index_page_path = os.path.join(page_root, "index.html") + cppreference_modified_date = datetime.datetime.fromtimestamp( + os.stat(index_page_path).st_mtime).strftime('%Y-%m-%d') + print(CODE_PREFIX % (args.symbols.upper(), cppreference_modified_date)) + for symbol in symbols: + if len(symbol.headers) == 1: + # SYMBOL(unqualified_name, namespace, header) + print("SYMBOL(%s, %s, %s)" % (symbol.name, symbol.namespace, + symbol.headers[0])) + elif len(symbol.headers) == 0: + sys.stderr.write("No header found for symbol %s\n" % symbol.name) + else: + # FIXME: support symbols with multiple headers (e.g. std::move). + sys.stderr.write("Ambiguous header for symbol %s: %s\n" % ( + symbol.name, ', '.join(symbol.headers))) + + +if __name__ == '__main__': + main() diff --git a/gnu/llvm/clang/tools/include-mapping/test.py b/gnu/llvm/clang/tools/include-mapping/test.py new file mode 100755 index 00000000000..507e462e752 --- /dev/null +++ b/gnu/llvm/clang/tools/include-mapping/test.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +#===- test.py - ---------------------------------------------*- python -*--===# +# +# 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 +# +#===------------------------------------------------------------------------===# + +from cppreference_parser import _ParseSymbolPage, _ParseIndexPage + +import unittest + +class TestStdGen(unittest.TestCase): + + def testParseIndexPage(self): + html = """ + <a href="abs.html" title="abs"><tt>abs()</tt></a> (int) <br> + <a href="complex/abs.html" title="abs"><tt>abs<>()</tt></a> (std::complex) <br> + <a href="acos.html" title="acos"><tt>acos()</tt></a> <br> + <a href="acosh.html" title="acosh"><tt>acosh()</tt></a> <span class="t-mark-rev">(since C++11)</span> <br> + <a href="as_bytes.html" title="as bytes"><tt>as_bytes<>()</tt></a> <span class="t-mark-rev t-since-cxx20">(since C++20)</span> <br> + """ + + actual = _ParseIndexPage(html) + expected = [ + ("abs", "abs.html", 'int'), + ("abs", "complex/abs.html", 'std::complex'), + ("acos", "acos.html", None), + ("acosh", "acosh.html", None), + ("as_bytes", "as_bytes.html", None), + ] + self.assertEqual(len(actual), len(expected)) + for i in range(0, len(actual)): + self.assertEqual(expected[i][0], actual[i][0]) + self.assertTrue(actual[i][1].endswith(expected[i][1])) + self.assertEqual(expected[i][2], actual[i][2]) + + + def testParseSymbolPage_SingleHeader(self): + # Defined in header <cmath> + html = """ + <table class="t-dcl-begin"><tbody> + <tr class="t-dsc-header"> + <td> <div>Defined in header <code><a href="cmath.html" title="cmath"><cmath></a></code> + </div></td> + <td></td> + <td></td> + </tr> + <tr class="t-dcl"> + <td>void foo()</td> + <td>this is matched</td> + </tr> +</tbody></table> +""" + self.assertEqual(_ParseSymbolPage(html, 'foo'), set(['<cmath>'])) + + + def testParseSymbolPage_MulHeaders(self): + # Defined in header <cstddef> + # Defined in header <cstdio> + # Defined in header <cstdlib> + html = """ +<table class="t-dcl-begin"><tbody> + <tr class="t-dsc-header"> + <td> <div>Defined in header <code><a href="cstddef.html" title="cstddef"><cstddef></a></code> + </div></td> + <td></td> + <td></td> + </tr> + <tr class="t-dcl"> + <td>void bar()</td> + <td>this mentions foo, but isn't matched</td> + </tr> + <tr class="t-dsc-header"> + <td> <div>Defined in header <code><a href="cstdio.html" title="cstdio"><cstdio></a></code> + </div></td> + <td></td> + <td></td> + </tr> + <tr class="t-dsc-header"> + <td> <div>Defined in header <code><a href=".cstdlib.html" title="ccstdlib"><cstdlib></a></code> + </div></td> + <td></td> + <td></td> + </tr> + <tr class="t-dcl"> + <td> + <span>void</span> + foo + <span>()</span> + </td> + <td>this is matched</td> + </tr> +</tbody></table> +""" + self.assertEqual(_ParseSymbolPage(html, "foo"), + set(['<cstdio>', '<cstdlib>'])) + + + def testParseSymbolPage_MulHeadersInSameDiv(self): + # Multile <code> blocks in a Div. + # Defined in header <algorithm> + # Defined in header <utility> + html = """ +<table class="t-dcl-begin"><tbody> +<tr class="t-dsc-header"> +<td><div> + Defined in header <code><a href="../header/algorithm.html" title="cpp/header/algorithm"><algorithm></a></code><br> + Defined in header <code><a href="../header/utility.html" title="cpp/header/utility"><utility></a></code> +</div></td> +<td></td> +</tr> +<tr class="t-dcl"> + <td> + <span>void</span> + foo + <span>()</span> + </td> + <td>this is matched</td> +</tr> +</tbody></table> +""" + self.assertEqual(_ParseSymbolPage(html, "foo"), + set(['<algorithm>', '<utility>'])) + + def testParseSymbolPage_MulSymbolsInSameTd(self): + # defined in header <cstdint> + # int8_t + # int16_t + html = """ +<table class="t-dcl-begin"><tbody> +<tr class="t-dsc-header"> +<td><div> + Defined in header <code><a href="cstdint.html" title="cstdint"><cstdint></a></code><br> +</div></td> +<td></td> +</tr> +<tr class="t-dcl"> + <td> + <span>int8_t</span> + <span>int16_t</span> + </td> + <td>this is matched</td> +</tr> +</tbody></table> +""" + self.assertEqual(_ParseSymbolPage(html, "int8_t"), + set(['<cstdint>'])) + self.assertEqual(_ParseSymbolPage(html, "int16_t"), + set(['<cstdint>'])) + + +if __name__ == '__main__': + unittest.main() diff --git a/gnu/llvm/clang/tools/libclang/BuildSystem.cpp b/gnu/llvm/clang/tools/libclang/BuildSystem.cpp index 0d69dcf1725..2f638ee8700 100644 --- a/gnu/llvm/clang/tools/libclang/BuildSystem.cpp +++ b/gnu/llvm/clang/tools/libclang/BuildSystem.cpp @@ -16,6 +16,7 @@ #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemAlloc.h" #include "llvm/Support/Path.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" diff --git a/gnu/llvm/clang/tools/libclang/CIndex.cpp b/gnu/llvm/clang/tools/libclang/CIndex.cpp index 7b93164ccaa..15652c49934 100644 --- a/gnu/llvm/clang/tools/libclang/CIndex.cpp +++ b/gnu/llvm/clang/tools/libclang/CIndex.cpp @@ -39,7 +39,6 @@ #include "clang/Lex/Lexer.h" #include "clang/Lex/PreprocessingRecord.h" #include "clang/Lex/Preprocessor.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Config/llvm-config.h" @@ -57,6 +56,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Support/thread.h" #include <mutex> +#include <optional> #if LLVM_ENABLE_THREADS != 0 && defined(__APPLE__) #define USE_DARWIN_THREADS @@ -535,10 +535,10 @@ bool CursorVisitor::VisitChildren(CXCursor Cursor) { for (ASTUnit::top_level_iterator TL = CXXUnit->top_level_begin(), TLEnd = CXXUnit->top_level_end(); TL != TLEnd; ++TL) { - const Optional<bool> V = handleDeclForVisitation(*TL); - if (!V.hasValue()) + const std::optional<bool> V = handleDeclForVisitation(*TL); + if (!V) continue; - return V.getValue(); + return *V; } } else if (VisitDeclContext( CXXUnit->getASTContext().getTranslationUnitDecl())) @@ -600,16 +600,16 @@ bool CursorVisitor::VisitBlockDecl(BlockDecl *B) { return false; } -Optional<bool> CursorVisitor::shouldVisitCursor(CXCursor Cursor) { +std::optional<bool> CursorVisitor::shouldVisitCursor(CXCursor Cursor) { if (RegionOfInterest.isValid()) { SourceRange Range = getFullCursorExtent(Cursor, AU->getSourceManager()); if (Range.isInvalid()) - return None; + return std::nullopt; switch (CompareRegionOfInterest(Range)) { case RangeBefore: // This declaration comes before the region of interest; skip it. - return None; + return std::nullopt; case RangeAfter: // This declaration comes after the region of interest; we're done. @@ -628,8 +628,8 @@ bool CursorVisitor::VisitDeclContext(DeclContext *DC) { // FIXME: Eventually remove. This part of a hack to support proper // iteration over all Decls contained lexically within an ObjC container. - SaveAndRestore<DeclContext::decl_iterator *> DI_saved(DI_current, &I); - SaveAndRestore<DeclContext::decl_iterator> DE_saved(DE_current, E); + SaveAndRestore DI_saved(DI_current, &I); + SaveAndRestore DE_saved(DE_current, E); for (; I != E; ++I) { Decl *D = *I; @@ -640,15 +640,15 @@ bool CursorVisitor::VisitDeclContext(DeclContext *DC) { if (auto *OMD = dyn_cast<ObjCMethodDecl>(D)) if (OMD->isSynthesizedAccessorStub()) continue; - const Optional<bool> V = handleDeclForVisitation(D); - if (!V.hasValue()) + const std::optional<bool> V = handleDeclForVisitation(D); + if (!V) continue; - return V.getValue(); + return *V; } return false; } -Optional<bool> CursorVisitor::handleDeclForVisitation(const Decl *D) { +std::optional<bool> CursorVisitor::handleDeclForVisitation(const Decl *D) { CXCursor Cursor = MakeCXCursor(D, TU, RegionOfInterest); // Ignore synthesized ivars here, otherwise if we have something like: @@ -658,7 +658,7 @@ Optional<bool> CursorVisitor::handleDeclForVisitation(const Decl *D) { // we passed the region-of-interest. if (auto *ivarD = dyn_cast<ObjCIvarDecl>(D)) { if (ivarD->getSynthesize()) - return None; + return std::nullopt; } // FIXME: ObjCClassRef/ObjCProtocolRef for forward class/protocol @@ -674,14 +674,14 @@ Optional<bool> CursorVisitor::handleDeclForVisitation(const Decl *D) { Cursor = MakeCursorObjCProtocolRef(PD, PD->getLocation(), TU); } - const Optional<bool> V = shouldVisitCursor(Cursor); - if (!V.hasValue()) - return None; - if (!V.getValue()) + const std::optional<bool> V = shouldVisitCursor(Cursor); + if (!V) + return std::nullopt; + if (!*V) return false; if (Visit(Cursor, true)) return true; - return None; + return std::nullopt; } bool CursorVisitor::VisitTranslationUnitDecl(TranslationUnitDecl *D) { @@ -761,10 +761,10 @@ bool CursorVisitor::VisitClassTemplatePartialSpecializationDecl( } bool CursorVisitor::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { - if (const auto *TC = D->getTypeConstraint()) - if (Visit(MakeCXCursor(TC->getImmediatelyDeclaredConstraint(), StmtParent, - TU, RegionOfInterest))) + if (const auto *TC = D->getTypeConstraint()) { + if (VisitTypeConstraint(*TC)) return true; + } // Visit the default argument. if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) @@ -863,6 +863,11 @@ bool CursorVisitor::VisitFunctionDecl(FunctionDecl *ND) { // FIXME: Attributes? } + if (auto *E = ND->getTrailingRequiresClause()) { + if (Visit(E)) + return true; + } + if (ND->doesThisDeclarationHaveABody() && !ND->isLateTemplateParsed()) { if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(ND)) { // Find the initializers that were written in the source. @@ -1068,10 +1073,10 @@ bool CursorVisitor::VisitObjCContainerDecl(ObjCContainerDecl *D) { E = DeclsInContainer.end(); I != E; ++I) { CXCursor Cursor = MakeCXCursor(*I, TU, RegionOfInterest); - const Optional<bool> &V = shouldVisitCursor(Cursor); - if (!V.hasValue()) + const std::optional<bool> &V = shouldVisitCursor(Cursor); + if (!V) continue; - if (!V.getValue()) + if (!*V) return false; if (Visit(Cursor, true)) return true; @@ -1310,6 +1315,75 @@ bool CursorVisitor::VisitDecompositionDecl(DecompositionDecl *D) { return VisitVarDecl(D); } +bool CursorVisitor::VisitConceptDecl(ConceptDecl *D) { + if (VisitTemplateParameters(D->getTemplateParameters())) + return true; + + if (auto *E = D->getConstraintExpr()) { + if (Visit(MakeCXCursor(E, D, TU, RegionOfInterest))) + return true; + } + return false; +} + +bool CursorVisitor::VisitTypeConstraint(const TypeConstraint &TC) { + if (TC.getNestedNameSpecifierLoc()) { + if (VisitNestedNameSpecifierLoc(TC.getNestedNameSpecifierLoc())) + return true; + } + if (TC.getNamedConcept()) { + if (Visit(MakeCursorTemplateRef(TC.getNamedConcept(), + TC.getConceptNameLoc(), TU))) + return true; + } + if (auto Args = TC.getTemplateArgsAsWritten()) { + for (const auto &Arg : Args->arguments()) { + if (VisitTemplateArgumentLoc(Arg)) + return true; + } + } + return false; +} + +bool CursorVisitor::VisitConceptRequirement(const concepts::Requirement &R) { + using namespace concepts; + switch (R.getKind()) { + case Requirement::RK_Type: { + const TypeRequirement &TR = cast<TypeRequirement>(R); + if (!TR.isSubstitutionFailure()) { + if (Visit(TR.getType()->getTypeLoc())) + return true; + } + break; + } + case Requirement::RK_Simple: + case Requirement::RK_Compound: { + const ExprRequirement &ER = cast<ExprRequirement>(R); + if (!ER.isExprSubstitutionFailure()) { + if (Visit(ER.getExpr())) + return true; + } + if (ER.getKind() == Requirement::RK_Compound) { + const auto &RTR = ER.getReturnTypeRequirement(); + if (RTR.isTypeConstraint()) { + if (const auto *Cons = RTR.getTypeConstraint()) + VisitTypeConstraint(*Cons); + } + } + break; + } + case Requirement::RK_Nested: { + const NestedRequirement &NR = cast<NestedRequirement>(R); + if (!NR.hasInvalidConstraint()) { + if (Visit(NR.getConstraintExpr())) + return true; + } + break; + } + } + return false; +} + bool CursorVisitor::VisitDeclarationNameInfo(DeclarationNameInfo Name) { switch (Name.getName().getNameKind()) { case clang::DeclarationName::Identifier: @@ -1436,12 +1510,19 @@ bool CursorVisitor::VisitTemplateParameters( return true; } + if (const auto *E = Params->getRequiresClause()) { + if (Visit(MakeCXCursor(E, nullptr, TU, RegionOfInterest))) + return true; + } + return false; } bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation Loc) { switch (Name.getKind()) { case TemplateName::Template: + case TemplateName::UsingTemplate: + case TemplateName::QualifiedTemplate: // FIXME: Visit nested-name-specifier. return Visit(MakeCursorTemplateRef(Name.getAsTemplateDecl(), Loc, TU)); case TemplateName::OverloadedTemplate: @@ -1459,11 +1540,6 @@ bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation Loc) { // FIXME: Visit nested-name-specifier. return false; - case TemplateName::QualifiedTemplate: - // FIXME: Visit nested-name-specifier. - return Visit(MakeCursorTemplateRef( - Name.getAsQualifiedTemplateName()->getDecl(), Loc, TU)); - case TemplateName::SubstTemplateTemplateParm: return Visit(MakeCursorTemplateRef( Name.getAsSubstTemplateTemplateParm()->getParameter(), Loc, TU)); @@ -1597,6 +1673,11 @@ bool CursorVisitor::VisitTagTypeLoc(TagTypeLoc TL) { } bool CursorVisitor::VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TL) { + if (const auto *TC = TL.getDecl()->getTypeConstraint()) { + if (VisitTypeConstraint(*TC)) + return true; + } + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); } @@ -1666,10 +1747,22 @@ bool CursorVisitor::VisitRValueReferenceTypeLoc(RValueReferenceTypeLoc TL) { return Visit(TL.getPointeeLoc()); } +bool CursorVisitor::VisitUsingTypeLoc(UsingTypeLoc TL) { + auto *underlyingDecl = TL.getUnderlyingType()->getAsTagDecl(); + if (underlyingDecl) { + return Visit(MakeCursorTypeRef(underlyingDecl, TL.getNameLoc(), TU)); + } + return false; +} + bool CursorVisitor::VisitAttributedTypeLoc(AttributedTypeLoc TL) { return Visit(TL.getModifiedLoc()); } +bool CursorVisitor::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { + return Visit(TL.getWrappedLoc()); +} + bool CursorVisitor::VisitFunctionTypeLoc(FunctionTypeLoc TL, bool SkipResultType) { if (!SkipResultType && Visit(TL.getReturnLoc())) @@ -1730,7 +1823,7 @@ bool CursorVisitor::VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) { } bool CursorVisitor::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) { - if (TypeSourceInfo *TSInfo = TL.getUnderlyingTInfo()) + if (TypeSourceInfo *TSInfo = TL.getUnmodifiedTInfo()) return Visit(TSInfo->getTypeLoc()); return false; @@ -1815,8 +1908,8 @@ DEFAULT_TYPELOC_IMPL(Enum, TagType) DEFAULT_TYPELOC_IMPL(SubstTemplateTypeParm, Type) DEFAULT_TYPELOC_IMPL(SubstTemplateTypeParmPack, Type) DEFAULT_TYPELOC_IMPL(Auto, Type) -DEFAULT_TYPELOC_IMPL(ExtInt, Type) -DEFAULT_TYPELOC_IMPL(DependentExtInt, Type) +DEFAULT_TYPELOC_IMPL(BitInt, Type) +DEFAULT_TYPELOC_IMPL(DependentBitInt, Type) bool CursorVisitor::VisitCXXRecordDecl(CXXRecordDecl *D) { // Visit the nested-name-specifier, if present. @@ -1866,6 +1959,9 @@ DEF_JOB(DeclRefExprParts, DeclRefExpr, DeclRefExprPartsKind) DEF_JOB(OverloadExprParts, OverloadExpr, OverloadExprPartsKind) DEF_JOB(SizeOfPackExprParts, SizeOfPackExpr, SizeOfPackExprPartsKind) DEF_JOB(LambdaExprParts, LambdaExpr, LambdaExprPartsKind) +DEF_JOB(ConceptSpecializationExprVisit, ConceptSpecializationExpr, + ConceptSpecializationExprVisitKind) +DEF_JOB(RequiresExprVisit, RequiresExpr, RequiresExprVisitKind) DEF_JOB(PostChildrenVisit, void, PostChildrenVisitKind) #undef DEF_JOB @@ -2041,11 +2137,16 @@ public: void VisitPseudoObjectExpr(const PseudoObjectExpr *E); void VisitOpaqueValueExpr(const OpaqueValueExpr *E); void VisitLambdaExpr(const LambdaExpr *E); + void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E); + void VisitRequiresExpr(const RequiresExpr *E); + void VisitCXXParenListInitExpr(const CXXParenListInitExpr *E); void VisitOMPExecutableDirective(const OMPExecutableDirective *D); void VisitOMPLoopBasedDirective(const OMPLoopBasedDirective *D); void VisitOMPLoopDirective(const OMPLoopDirective *D); void VisitOMPParallelDirective(const OMPParallelDirective *D); void VisitOMPSimdDirective(const OMPSimdDirective *D); + void + VisitOMPLoopTransformationDirective(const OMPLoopTransformationDirective *D); void VisitOMPTileDirective(const OMPTileDirective *D); void VisitOMPUnrollDirective(const OMPUnrollDirective *D); void VisitOMPForDirective(const OMPForDirective *D); @@ -2058,11 +2159,13 @@ public: void VisitOMPParallelForDirective(const OMPParallelForDirective *D); void VisitOMPParallelForSimdDirective(const OMPParallelForSimdDirective *D); void VisitOMPParallelMasterDirective(const OMPParallelMasterDirective *D); + void VisitOMPParallelMaskedDirective(const OMPParallelMaskedDirective *D); void VisitOMPParallelSectionsDirective(const OMPParallelSectionsDirective *D); void VisitOMPTaskDirective(const OMPTaskDirective *D); void VisitOMPTaskyieldDirective(const OMPTaskyieldDirective *D); void VisitOMPBarrierDirective(const OMPBarrierDirective *D); void VisitOMPTaskwaitDirective(const OMPTaskwaitDirective *D); + void VisitOMPErrorDirective(const OMPErrorDirective *D); void VisitOMPTaskgroupDirective(const OMPTaskgroupDirective *D); void VisitOMPCancellationPointDirective(const OMPCancellationPointDirective *D); @@ -2083,12 +2186,19 @@ public: void VisitOMPTaskLoopDirective(const OMPTaskLoopDirective *D); void VisitOMPTaskLoopSimdDirective(const OMPTaskLoopSimdDirective *D); void VisitOMPMasterTaskLoopDirective(const OMPMasterTaskLoopDirective *D); + void VisitOMPMaskedTaskLoopDirective(const OMPMaskedTaskLoopDirective *D); void VisitOMPMasterTaskLoopSimdDirective(const OMPMasterTaskLoopSimdDirective *D); + void VisitOMPMaskedTaskLoopSimdDirective( + const OMPMaskedTaskLoopSimdDirective *D); void VisitOMPParallelMasterTaskLoopDirective( const OMPParallelMasterTaskLoopDirective *D); + void VisitOMPParallelMaskedTaskLoopDirective( + const OMPParallelMaskedTaskLoopDirective *D); void VisitOMPParallelMasterTaskLoopSimdDirective( const OMPParallelMasterTaskLoopSimdDirective *D); + void VisitOMPParallelMaskedTaskLoopSimdDirective( + const OMPParallelMaskedTaskLoopSimdDirective *D); void VisitOMPDistributeDirective(const OMPDistributeDirective *D); void VisitOMPDistributeParallelForDirective( const OMPDistributeParallelForDirective *D); @@ -2271,6 +2381,8 @@ void OMPClauseEnqueue::VisitOMPUpdateClause(const OMPUpdateClause *) {} void OMPClauseEnqueue::VisitOMPCaptureClause(const OMPCaptureClause *) {} +void OMPClauseEnqueue::VisitOMPCompareClause(const OMPCompareClause *) {} + void OMPClauseEnqueue::VisitOMPSeqCstClause(const OMPSeqCstClause *) {} void OMPClauseEnqueue::VisitOMPAcqRelClause(const OMPAcqRelClause *) {} @@ -2313,6 +2425,10 @@ void OMPClauseEnqueue::VisitOMPFilterClause(const OMPFilterClause *C) { Visitor->AddStmt(C->getThreadID()); } +void OMPClauseEnqueue::VisitOMPAlignClause(const OMPAlignClause *C) { + Visitor->AddStmt(C->getAlignment()); +} + void OMPClauseEnqueue::VisitOMPUnifiedAddressClause( const OMPUnifiedAddressClause *) {} @@ -2328,6 +2444,12 @@ void OMPClauseEnqueue::VisitOMPDynamicAllocatorsClause( void OMPClauseEnqueue::VisitOMPAtomicDefaultMemOrderClause( const OMPAtomicDefaultMemOrderClause *) {} +void OMPClauseEnqueue::VisitOMPAtClause(const OMPAtClause *) {} + +void OMPClauseEnqueue::VisitOMPSeverityClause(const OMPSeverityClause *) {} + +void OMPClauseEnqueue::VisitOMPMessageClause(const OMPMessageClause *) {} + void OMPClauseEnqueue::VisitOMPDeviceClause(const OMPDeviceClause *C) { Visitor->AddStmt(C->getDevice()); } @@ -2559,6 +2681,10 @@ void OMPClauseEnqueue::VisitOMPIsDevicePtrClause( const OMPIsDevicePtrClause *C) { VisitOMPClauseList(C); } +void OMPClauseEnqueue::VisitOMPHasDeviceAddrClause( + const OMPHasDeviceAddrClause *C) { + VisitOMPClauseList(C); +} void OMPClauseEnqueue::VisitOMPNontemporalClause( const OMPNontemporalClause *C) { VisitOMPClauseList(C); @@ -2579,6 +2705,13 @@ void OMPClauseEnqueue::VisitOMPAffinityClause(const OMPAffinityClause *C) { for (const Expr *E : C->varlists()) Visitor->AddStmt(E); } +void OMPClauseEnqueue::VisitOMPBindClause(const OMPBindClause *C) {} +void OMPClauseEnqueue::VisitOMPXDynCGroupMemClause( + const OMPXDynCGroupMemClause *C) { + VisitOMPClauseWithPreInit(C); + Visitor->AddStmt(C->getSize()); +} + } // namespace void EnqueueVisitor::EnqueueChildren(const OMPClause *S) { @@ -2628,7 +2761,7 @@ void EnqueueVisitor::VisitCXXNewExpr(const CXXNewExpr *E) { // Enqueue the initializer , if any. AddStmt(E->getInitializer()); // Enqueue the array size, if any. - AddStmt(E->getArraySize().getValueOr(nullptr)); + AddStmt(E->getArraySize().value_or(nullptr)); // Enqueue the allocated type. AddTypeLoc(E->getAllocatedTypeSourceInfo()); // Enqueue the placement arguments. @@ -2870,6 +3003,18 @@ void EnqueueVisitor::VisitLambdaExpr(const LambdaExpr *E) { AddStmt(E->getBody()); WL.push_back(LambdaExprParts(E, Parent)); } +void EnqueueVisitor::VisitConceptSpecializationExpr( + const ConceptSpecializationExpr *E) { + WL.push_back(ConceptSpecializationExprVisit(E, Parent)); +} +void EnqueueVisitor::VisitRequiresExpr(const RequiresExpr *E) { + WL.push_back(RequiresExprVisit(E, Parent)); + for (ParmVarDecl *VD : E->getLocalParameters()) + AddDecl(VD); +} +void EnqueueVisitor::VisitCXXParenListInitExpr(const CXXParenListInitExpr *E) { + EnqueueChildren(E); +} void EnqueueVisitor::VisitPseudoObjectExpr(const PseudoObjectExpr *E) { // Treat the expression like its syntactic form. Visit(E->getSyntacticForm()); @@ -2901,12 +3046,17 @@ void EnqueueVisitor::VisitOMPSimdDirective(const OMPSimdDirective *D) { VisitOMPLoopDirective(D); } -void EnqueueVisitor::VisitOMPTileDirective(const OMPTileDirective *D) { +void EnqueueVisitor::VisitOMPLoopTransformationDirective( + const OMPLoopTransformationDirective *D) { VisitOMPLoopBasedDirective(D); } +void EnqueueVisitor::VisitOMPTileDirective(const OMPTileDirective *D) { + VisitOMPLoopTransformationDirective(D); +} + void EnqueueVisitor::VisitOMPUnrollDirective(const OMPUnrollDirective *D) { - VisitOMPLoopBasedDirective(D); + VisitOMPLoopTransformationDirective(D); } void EnqueueVisitor::VisitOMPForDirective(const OMPForDirective *D) { @@ -2953,6 +3103,11 @@ void EnqueueVisitor::VisitOMPParallelMasterDirective( VisitOMPExecutableDirective(D); } +void EnqueueVisitor::VisitOMPParallelMaskedDirective( + const OMPParallelMaskedDirective *D) { + VisitOMPExecutableDirective(D); +} + void EnqueueVisitor::VisitOMPParallelSectionsDirective( const OMPParallelSectionsDirective *D) { VisitOMPExecutableDirective(D); @@ -2975,6 +3130,10 @@ void EnqueueVisitor::VisitOMPTaskwaitDirective(const OMPTaskwaitDirective *D) { VisitOMPExecutableDirective(D); } +void EnqueueVisitor::VisitOMPErrorDirective(const OMPErrorDirective *D) { + VisitOMPExecutableDirective(D); +} + void EnqueueVisitor::VisitOMPTaskgroupDirective( const OMPTaskgroupDirective *D) { VisitOMPExecutableDirective(D); @@ -3058,21 +3217,41 @@ void EnqueueVisitor::VisitOMPMasterTaskLoopDirective( VisitOMPLoopDirective(D); } +void EnqueueVisitor::VisitOMPMaskedTaskLoopDirective( + const OMPMaskedTaskLoopDirective *D) { + VisitOMPLoopDirective(D); +} + void EnqueueVisitor::VisitOMPMasterTaskLoopSimdDirective( const OMPMasterTaskLoopSimdDirective *D) { VisitOMPLoopDirective(D); } +void EnqueueVisitor::VisitOMPMaskedTaskLoopSimdDirective( + const OMPMaskedTaskLoopSimdDirective *D) { + VisitOMPLoopDirective(D); +} + void EnqueueVisitor::VisitOMPParallelMasterTaskLoopDirective( const OMPParallelMasterTaskLoopDirective *D) { VisitOMPLoopDirective(D); } +void EnqueueVisitor::VisitOMPParallelMaskedTaskLoopDirective( + const OMPParallelMaskedTaskLoopDirective *D) { + VisitOMPLoopDirective(D); +} + void EnqueueVisitor::VisitOMPParallelMasterTaskLoopSimdDirective( const OMPParallelMasterTaskLoopSimdDirective *D) { VisitOMPLoopDirective(D); } +void EnqueueVisitor::VisitOMPParallelMaskedTaskLoopSimdDirective( + const OMPParallelMaskedTaskLoopSimdDirective *D) { + VisitOMPLoopDirective(D); +} + void EnqueueVisitor::VisitOMPDistributeDirective( const OMPDistributeDirective *D) { VisitOMPLoopDirective(D); @@ -3329,9 +3508,11 @@ bool CursorVisitor::RunVisitorWorkList(VisitorWorkList &WL) { C != CEnd; ++C) { if (!C->capturesVariable()) continue; - - if (Visit(MakeCursorVariableRef(C->getCapturedVar(), C->getLocation(), - TU))) + // TODO: handle structured bindings here ? + if (!isa<VarDecl>(C->getCapturedVar())) + continue; + if (Visit(MakeCursorVariableRef(cast<VarDecl>(C->getCapturedVar()), + C->getLocation(), TU))) return true; } // Visit init captures @@ -3358,6 +3539,36 @@ bool CursorVisitor::RunVisitorWorkList(VisitorWorkList &WL) { break; } + case VisitorJob::ConceptSpecializationExprVisitKind: { + const ConceptSpecializationExpr *E = + cast<ConceptSpecializationExprVisit>(&LI)->get(); + if (NestedNameSpecifierLoc QualifierLoc = + E->getNestedNameSpecifierLoc()) { + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + } + + if (E->getNamedConcept() && + Visit(MakeCursorTemplateRef(E->getNamedConcept(), + E->getConceptNameLoc(), TU))) + return true; + + if (auto Args = E->getTemplateArgsAsWritten()) { + for (const auto &Arg : Args->arguments()) { + if (VisitTemplateArgumentLoc(Arg)) + return true; + } + } + break; + } + + case VisitorJob::RequiresExprVisitKind: { + const RequiresExpr *E = cast<RequiresExprVisit>(&LI)->get(); + for (const concepts::Requirement *R : E->getRequirements()) + VisitConceptRequirement(*R); + break; + } + case VisitorJob::PostChildrenVisitKind: if (PostChildrenVisitor(Parent, ClientData)) return true; @@ -3595,8 +3806,10 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename, } // Configure the diagnostics. + std::unique_ptr<DiagnosticOptions> DiagOpts = CreateAndPopulateDiagOpts( + llvm::ArrayRef(command_line_args, num_command_line_args)); IntrusiveRefCntPtr<DiagnosticsEngine> Diags( - CompilerInstance::createDiagnostics(new DiagnosticOptions)); + CompilerInstance::createDiagnostics(DiagOpts.release())); if (options & CXTranslationUnit_KeepGoing) Diags->setFatalsAsError(true); @@ -3677,7 +3890,7 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename, LibclangInvocationReporter InvocationReporter( *CXXIdx, LibclangInvocationReporter::OperationKind::ParseOperation, - options, llvm::makeArrayRef(*Args), /*InvocationArgs=*/None, + options, llvm::ArrayRef(*Args), /*InvocationArgs=*/std::nullopt, unsaved_files); std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCommandLine( Args->data(), Args->data() + Args->size(), @@ -3764,7 +3977,7 @@ enum CXErrorCode clang_parseTranslationUnit2FullArgv( noteBottomOfStack(); result = clang_parseTranslationUnit_Impl( CIdx, source_filename, command_line_args, num_command_line_args, - llvm::makeArrayRef(unsaved_files, num_unsaved_files), options, out_TU); + llvm::ArrayRef(unsaved_files, num_unsaved_files), options, out_TU); }; llvm::CrashRecoveryContext CRC; @@ -3993,7 +4206,7 @@ static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) { } if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) { - const ImplicitCastExpr *I = dyn_cast<ImplicitCastExpr>(expr); + const auto *I = cast<ImplicitCastExpr>(expr); auto *subExpr = I->getSubExprAsWritten(); if (subExpr->getStmtClass() == Stmt::StringLiteralClass || subExpr->getStmtClass() == Stmt::ObjCStringLiteralClass) { @@ -4295,7 +4508,7 @@ int clang_reparseTranslationUnit(CXTranslationUnit TU, CXErrorCode result; auto ReparseTranslationUnitImpl = [=, &result]() { result = clang_reparseTranslationUnit_Impl( - TU, llvm::makeArrayRef(unsaved_files, num_unsaved_files), options); + TU, llvm::ArrayRef(unsaved_files, num_unsaved_files), options); }; llvm::CrashRecoveryContext CRC; @@ -4418,7 +4631,7 @@ const char *clang_getFileContents(CXTranslationUnit TU, CXFile file, const SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); FileID fid = SM.translateFile(static_cast<FileEntry *>(file)); - llvm::Optional<llvm::MemoryBufferRef> buf = SM.getBufferOrNone(fid); + std::optional<llvm::MemoryBufferRef> buf = SM.getBufferOrNone(fid); if (!buf) { if (size) *size = 0; @@ -4932,7 +5145,7 @@ CXStringSet *clang_Cursor_getObjCManglings(CXCursor C) { CXPrintingPolicy clang_getCursorPrintingPolicy(CXCursor C) { if (clang_Cursor_isNull(C)) - return 0; + return nullptr; return new PrintingPolicy(getCursorContext(C).getPrintingPolicy()); } @@ -5379,6 +5592,12 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("ObjCMessageExpr"); case CXCursor_BuiltinBitCastExpr: return cxstring::createRef("BuiltinBitCastExpr"); + case CXCursor_ConceptSpecializationExpr: + return cxstring::createRef("ConceptSpecializationExpr"); + case CXCursor_RequiresExpr: + return cxstring::createRef("RequiresExpr"); + case CXCursor_CXXParenListInitExpr: + return cxstring::createRef("CXXParenListInitExpr"); case CXCursor_UnexposedStmt: return cxstring::createRef("UnexposedStmt"); case CXCursor_DeclStmt: @@ -5582,6 +5801,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("ModuleImport"); case CXCursor_OMPCanonicalLoop: return cxstring::createRef("OMPCanonicalLoop"); + case CXCursor_OMPMetaDirective: + return cxstring::createRef("OMPMetaDirective"); case CXCursor_OMPParallelDirective: return cxstring::createRef("OMPParallelDirective"); case CXCursor_OMPSimdDirective: @@ -5610,6 +5831,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("OMPParallelForSimdDirective"); case CXCursor_OMPParallelMasterDirective: return cxstring::createRef("OMPParallelMasterDirective"); + case CXCursor_OMPParallelMaskedDirective: + return cxstring::createRef("OMPParallelMaskedDirective"); case CXCursor_OMPParallelSectionsDirective: return cxstring::createRef("OMPParallelSectionsDirective"); case CXCursor_OMPTaskDirective: @@ -5620,6 +5843,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("OMPBarrierDirective"); case CXCursor_OMPTaskwaitDirective: return cxstring::createRef("OMPTaskwaitDirective"); + case CXCursor_OMPErrorDirective: + return cxstring::createRef("OMPErrorDirective"); case CXCursor_OMPTaskgroupDirective: return cxstring::createRef("OMPTaskgroupDirective"); case CXCursor_OMPFlushDirective: @@ -5658,12 +5883,20 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("OMPTaskLoopSimdDirective"); case CXCursor_OMPMasterTaskLoopDirective: return cxstring::createRef("OMPMasterTaskLoopDirective"); + case CXCursor_OMPMaskedTaskLoopDirective: + return cxstring::createRef("OMPMaskedTaskLoopDirective"); case CXCursor_OMPMasterTaskLoopSimdDirective: return cxstring::createRef("OMPMasterTaskLoopSimdDirective"); + case CXCursor_OMPMaskedTaskLoopSimdDirective: + return cxstring::createRef("OMPMaskedTaskLoopSimdDirective"); case CXCursor_OMPParallelMasterTaskLoopDirective: return cxstring::createRef("OMPParallelMasterTaskLoopDirective"); + case CXCursor_OMPParallelMaskedTaskLoopDirective: + return cxstring::createRef("OMPParallelMaskedTaskLoopDirective"); case CXCursor_OMPParallelMasterTaskLoopSimdDirective: return cxstring::createRef("OMPParallelMasterTaskLoopSimdDirective"); + case CXCursor_OMPParallelMaskedTaskLoopSimdDirective: + return cxstring::createRef("OMPParallelMaskedTaskLoopSimdDirective"); case CXCursor_OMPDistributeDirective: return cxstring::createRef("OMPDistributeDirective"); case CXCursor_OMPDistributeParallelForDirective: @@ -5701,6 +5934,16 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("OMPDispatchDirective"); case CXCursor_OMPMaskedDirective: return cxstring::createRef("OMPMaskedDirective"); + case CXCursor_OMPGenericLoopDirective: + return cxstring::createRef("OMPGenericLoopDirective"); + case CXCursor_OMPTeamsGenericLoopDirective: + return cxstring::createRef("OMPTeamsGenericLoopDirective"); + case CXCursor_OMPTargetTeamsGenericLoopDirective: + return cxstring::createRef("OMPTargetTeamsGenericLoopDirective"); + case CXCursor_OMPParallelGenericLoopDirective: + return cxstring::createRef("OMPParallelGenericLoopDirective"); + case CXCursor_OMPTargetParallelGenericLoopDirective: + return cxstring::createRef("OMPTargetParallelGenericLoopDirective"); case CXCursor_OverloadCandidate: return cxstring::createRef("OverloadCandidate"); case CXCursor_TypeAliasTemplateDecl: @@ -5717,6 +5960,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("attribute(warn_unused_result)"); case CXCursor_AlignedAttr: return cxstring::createRef("attribute(aligned)"); + case CXCursor_ConceptDecl: + return cxstring::createRef("ConceptDecl"); } llvm_unreachable("Unhandled CXCursorKind"); @@ -6445,6 +6690,8 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::Binding: case Decl::MSProperty: case Decl::MSGuid: + case Decl::HLSLBuffer: + case Decl::UnnamedGlobalConstant: case Decl::TemplateParamObject: case Decl::IndirectField: case Decl::ObjCIvar: @@ -6460,6 +6707,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::Export: case Decl::ObjCPropertyImpl: case Decl::FileScopeAsm: + case Decl::TopLevelStmt: case Decl::StaticAssert: case Decl::Block: case Decl::Captured: @@ -6479,6 +6727,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::PragmaDetectMismatch: case Decl::UsingPack: case Decl::Concept: + case Decl::ImplicitConceptSpecialization: case Decl::LifetimeExtendedTemporary: case Decl::RequiresExprBody: case Decl::UnresolvedUsingIfExists: @@ -6719,8 +6968,8 @@ void clang_getDefinitionSpellingAndExtent( CXCursor C, const char **startBuf, const char **endBuf, unsigned *startLine, unsigned *startColumn, unsigned *endLine, unsigned *endColumn) { assert(getCursorDecl(C) && "CXCursor has null decl"); - const FunctionDecl *FD = dyn_cast<FunctionDecl>(getCursorDecl(C)); - CompoundStmt *Body = dyn_cast<CompoundStmt>(FD->getBody()); + const auto *FD = cast<FunctionDecl>(getCursorDecl(C)); + const auto *Body = cast<CompoundStmt>(FD->getBody()); SourceManager &SM = FD->getASTContext().getSourceManager(); *startBuf = SM.getCharacterData(Body->getLBracLoc()); @@ -6788,7 +7037,7 @@ void clang_enableStackTraces(void) { void clang_executeOnThread(void (*fn)(void *), void *user_data, unsigned stack_size) { llvm::thread Thread(stack_size == 0 ? clang::DesiredStackSize - : llvm::Optional<unsigned>(stack_size), + : std::optional<unsigned>(stack_size), fn, user_data); Thread.join(); } @@ -6954,16 +7203,16 @@ CXToken *clang_getToken(CXTranslationUnit TU, CXSourceLocation Location) { if (isNotUsableTU(TU)) { LOG_BAD_TU(TU); - return NULL; + return nullptr; } ASTUnit *CXXUnit = cxtu::getASTUnit(TU); if (!CXXUnit) - return NULL; + return nullptr; SourceLocation Begin = cxloc::translateSourceLocation(Location); if (Begin.isInvalid()) - return NULL; + return nullptr; SourceManager &SM = CXXUnit->getSourceManager(); std::pair<FileID, unsigned> DecomposedEnd = SM.getDecomposedLoc(Begin); DecomposedEnd.second += @@ -6976,7 +7225,7 @@ CXToken *clang_getToken(CXTranslationUnit TU, CXSourceLocation Location) { getTokens(CXXUnit, SourceRange(Begin, End), CXTokens); if (CXTokens.empty()) - return NULL; + return nullptr; CXTokens.resize(1); CXToken *Token = static_cast<CXToken *>(llvm::safe_malloc(sizeof(CXToken))); @@ -7159,7 +7408,7 @@ AnnotateTokensWorker::PostChildrenActions AnnotateTokensWorker::DetermineChildActions(CXCursor Cursor) const { PostChildrenActions actions; - // The DeclRefExpr of CXXOperatorCallExpr refering to the custom operator is + // The DeclRefExpr of CXXOperatorCallExpr referring to the custom operator is // visited before the arguments to the operator call. For the Call and // Subscript operator the range of this DeclRefExpr includes the whole call // expression, so that all tokens in that range would be mapped to the @@ -7992,14 +8241,14 @@ static CXVersion convertVersion(VersionTuple In) { Out.Major = In.getMajor(); - Optional<unsigned> Minor = In.getMinor(); - if (Minor.hasValue()) + std::optional<unsigned> Minor = In.getMinor(); + if (Minor) Out.Minor = *Minor; else return Out; - Optional<unsigned> Subminor = In.getSubminor(); - if (Subminor.hasValue()) + std::optional<unsigned> Subminor = In.getSubminor(); + if (Subminor) Out.Subminor = *Subminor; return Out; @@ -8046,8 +8295,35 @@ static void getCursorPlatformAvailabilityForDecl( deprecated_message, always_unavailable, unavailable_message, AvailabilityAttrs); - if (AvailabilityAttrs.empty()) + // If no availability attributes are found, inherit the attribute from the + // containing decl or the class or category interface decl. + if (AvailabilityAttrs.empty()) { + const ObjCContainerDecl *CD = nullptr; + const DeclContext *DC = D->getDeclContext(); + + if (auto *IMD = dyn_cast<ObjCImplementationDecl>(D)) + CD = IMD->getClassInterface(); + else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D)) + CD = CatD->getClassInterface(); + else if (auto *IMD = dyn_cast<ObjCCategoryImplDecl>(D)) + CD = IMD->getCategoryDecl(); + else if (auto *ID = dyn_cast<ObjCInterfaceDecl>(DC)) + CD = ID; + else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(DC)) + CD = CatD; + else if (auto *IMD = dyn_cast<ObjCImplementationDecl>(DC)) + CD = IMD->getClassInterface(); + else if (auto *IMD = dyn_cast<ObjCCategoryImplDecl>(DC)) + CD = IMD->getCategoryDecl(); + else if (auto *PD = dyn_cast<ObjCProtocolDecl>(DC)) + CD = PD; + + if (CD) + getCursorPlatformAvailabilityForDecl( + CD, always_deprecated, deprecated_message, always_unavailable, + unavailable_message, AvailabilityAttrs); return; + } llvm::sort( AvailabilityAttrs, [](AvailabilityAttr *LHS, AvailabilityAttr *RHS) { @@ -8122,9 +8398,8 @@ int clang_getCursorPlatformAvailability(CXCursor cursor, int *always_deprecated, getCursorPlatformAvailabilityForDecl(D, always_deprecated, deprecated_message, always_unavailable, unavailable_message, AvailabilityAttrs); - for (const auto &Avail : - llvm::enumerate(llvm::makeArrayRef(AvailabilityAttrs) - .take_front(availability_size))) { + for (const auto &Avail : llvm::enumerate( + llvm::ArrayRef(AvailabilityAttrs).take_front(availability_size))) { availability[Avail.index()].Platform = cxstring::createDup(Avail.value()->getPlatform()->getName()); availability[Avail.index()].Introduced = @@ -8260,7 +8535,8 @@ CXFile clang_getIncludedFile(CXCursor cursor) { return nullptr; const InclusionDirective *ID = getCursorInclusionDirective(cursor); - return const_cast<FileEntry *>(ID->getFile()); + OptionalFileEntryRef File = ID->getFile(); + return const_cast<FileEntry *>(File ? &File->getFileEntry() : nullptr); } unsigned clang_Cursor_getObjCPropertyAttributes(CXCursor C, unsigned reserved) { @@ -8268,7 +8544,7 @@ unsigned clang_Cursor_getObjCPropertyAttributes(CXCursor C, unsigned reserved) { return CXObjCPropertyAttr_noattr; unsigned Result = CXObjCPropertyAttr_noattr; - const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(getCursorDecl(C)); + const auto *PD = cast<ObjCPropertyDecl>(getCursorDecl(C)); ObjCPropertyAttribute::Kind Attr = PD->getPropertyAttributesAsWritten(); #define SET_CXOBJCPROP_ATTR(A) \ @@ -8296,7 +8572,7 @@ CXString clang_Cursor_getObjCPropertyGetterName(CXCursor C) { if (C.kind != CXCursor_ObjCPropertyDecl) return cxstring::createNull(); - const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(getCursorDecl(C)); + const auto *PD = cast<ObjCPropertyDecl>(getCursorDecl(C)); Selector sel = PD->getGetterName(); if (sel.isNull()) return cxstring::createNull(); @@ -8308,7 +8584,7 @@ CXString clang_Cursor_getObjCPropertySetterName(CXCursor C) { if (C.kind != CXCursor_ObjCPropertyDecl) return cxstring::createNull(); - const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(getCursorDecl(C)); + const auto *PD = cast<ObjCPropertyDecl>(getCursorDecl(C)); Selector sel = PD->getSetterName(); if (sel.isNull()) return cxstring::createNull(); @@ -8619,6 +8895,16 @@ unsigned clang_CXXMethod_isDefaulted(CXCursor C) { return (Method && Method->isDefaulted()) ? 1 : 0; } +unsigned clang_CXXMethod_isDeleted(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXMethodDecl *Method = + D ? dyn_cast_if_present<CXXMethodDecl>(D->getAsFunction()) : nullptr; + return (Method && Method->isDeleted()) ? 1 : 0; +} + unsigned clang_CXXMethod_isStatic(CXCursor C) { if (!clang_isDeclaration(C.kind)) return 0; @@ -8639,6 +8925,28 @@ unsigned clang_CXXMethod_isVirtual(CXCursor C) { return (Method && Method->isVirtual()) ? 1 : 0; } +unsigned clang_CXXMethod_isCopyAssignmentOperator(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXMethodDecl *Method = + D ? dyn_cast_or_null<CXXMethodDecl>(D->getAsFunction()) : nullptr; + + return (Method && Method->isCopyAssignmentOperator()) ? 1 : 0; +} + +unsigned clang_CXXMethod_isMoveAssignmentOperator(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXMethodDecl *Method = + D ? dyn_cast_or_null<CXXMethodDecl>(D->getAsFunction()) : nullptr; + + return (Method && Method->isMoveAssignmentOperator()) ? 1 : 0; +} + unsigned clang_CXXRecord_isAbstract(CXCursor C) { if (!clang_isDeclaration(C.kind)) return 0; @@ -8989,7 +9297,9 @@ void clang::setThreadBackgroundPriority() { return; #if LLVM_ENABLE_THREADS - llvm::set_thread_priority(llvm::ThreadPriority::Background); + // The function name setThreadBackgroundPriority is for historical reasons; + // Low is more appropriate. + llvm::set_thread_priority(llvm::ThreadPriority::Low); #endif } @@ -9078,7 +9388,7 @@ cxindex::checkForMacroInMacroDefinition(const MacroInfo *MI, const Token &Tok, return nullptr; // Check that the identifier is not one of the macro arguments. - if (std::find(MI->param_begin(), MI->param_end(), &II) != MI->param_end()) + if (llvm::is_contained(MI->params(), &II)) return nullptr; MacroDirective *InnerMD = PP.getLocalMacroDirectiveHistory(&II); diff --git a/gnu/llvm/clang/tools/libclang/CIndexCodeCompletion.cpp b/gnu/llvm/clang/tools/libclang/CIndexCodeCompletion.cpp index 68f35c41efd..d7df36dc7b8 100644 --- a/gnu/llvm/clang/tools/libclang/CIndexCodeCompletion.cpp +++ b/gnu/llvm/clang/tools/libclang/CIndexCodeCompletion.cpp @@ -541,6 +541,7 @@ static unsigned long long getContextsForContextKind( case CodeCompletionContext::CCC_MacroName: case CodeCompletionContext::CCC_PreprocessorExpression: case CodeCompletionContext::CCC_PreprocessorDirective: + case CodeCompletionContext::CCC_Attribute: case CodeCompletionContext::CCC_TypeQualifiers: { //Only Clang results should be accepted, so we'll set all of the other //context bits to 0 (i.e. the empty set) @@ -655,14 +656,15 @@ namespace { void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, OverloadCandidate *Candidates, unsigned NumCandidates, - SourceLocation OpenParLoc) override { + SourceLocation OpenParLoc, + bool Braced) override { StoredResults.reserve(StoredResults.size() + NumCandidates); for (unsigned I = 0; I != NumCandidates; ++I) { - CodeCompletionString *StoredCompletion - = Candidates[I].CreateSignatureString(CurrentArg, S, getAllocator(), + CodeCompletionString *StoredCompletion = + Candidates[I].CreateSignatureString(CurrentArg, S, getAllocator(), getCodeCompletionTUInfo(), - includeBriefComments()); - + includeBriefComments(), Braced); + CXCompletionResult R; R.CursorKind = CXCursor_OverloadCandidate; R.CompletionString = StoredCompletion; @@ -867,7 +869,7 @@ CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU, auto CodeCompleteAtImpl = [=, &result]() { result = clang_codeCompleteAt_Impl( TU, complete_filename, complete_line, complete_column, - llvm::makeArrayRef(unsaved_files, num_unsaved_files), options); + llvm::ArrayRef(unsaved_files, num_unsaved_files), options); }; llvm::CrashRecoveryContext CRC; diff --git a/gnu/llvm/clang/tools/libclang/CIndexHigh.cpp b/gnu/llvm/clang/tools/libclang/CIndexHigh.cpp index 04699ccb01a..0b49d1842e1 100644 --- a/gnu/llvm/clang/tools/libclang/CIndexHigh.cpp +++ b/gnu/llvm/clang/tools/libclang/CIndexHigh.cpp @@ -109,14 +109,14 @@ struct FindFileIdRefVisitData { private: bool isOverriddingMethod(const Decl *D) const { - if (llvm::find(TopMethods, D) != TopMethods.end()) + if (llvm::is_contained(TopMethods, D)) return true; TopMethodsTy methods; getTopOverriddenMethods(TU, D, methods); for (TopMethodsTy::iterator I = methods.begin(), E = methods.end(); I != E; ++I) { - if (llvm::find(TopMethods, *I) != TopMethods.end()) + if (llvm::is_contained(TopMethods, *I)) return true; } diff --git a/gnu/llvm/clang/tools/libclang/CIndexInclusionStack.cpp b/gnu/llvm/clang/tools/libclang/CIndexInclusionStack.cpp index 3e05cff12c4..9cb9e18401f 100644 --- a/gnu/llvm/clang/tools/libclang/CIndexInclusionStack.cpp +++ b/gnu/llvm/clang/tools/libclang/CIndexInclusionStack.cpp @@ -59,8 +59,8 @@ void getInclusions(bool IsLocal, unsigned n, CXTranslationUnit TU, // Callback to the client. // FIXME: We should have a function to construct CXFiles. - CB(static_cast<CXFile>( - const_cast<FileEntry *>(FI.getContentCache().OrigEntry)), + CB(static_cast<CXFile>(const_cast<FileEntry *>( + static_cast<const FileEntry *>(FI.getContentCache().OrigEntry))), InclusionStack.data(), InclusionStack.size(), clientData); } } diff --git a/gnu/llvm/clang/tools/libclang/CIndexer.cpp b/gnu/llvm/clang/tools/libclang/CIndexer.cpp index c7baab3a2c4..77da2e4fa5e 100644 --- a/gnu/llvm/clang/tools/libclang/CIndexer.cpp +++ b/gnu/llvm/clang/tools/libclang/CIndexer.cpp @@ -125,13 +125,23 @@ const std::string &CIndexer::getClangResourcesPath() { #elif defined(_AIX) getClangResourcesPathImplAIX(LibClangPath); #else - // This silly cast below avoids a C++ warning. Dl_info info; - if (dladdr((void *)(uintptr_t)clang_createTranslationUnit, &info) == 0) - llvm_unreachable("Call to dladdr() failed"); + std::string Path; + // This silly cast below avoids a C++ warning. + if (dladdr((void *)(uintptr_t)clang_createTranslationUnit, &info) != 0) { + // We now have the CIndex directory, locate clang relative to it. + LibClangPath += info.dli_fname; + } else if (!(Path = llvm::sys::fs::getMainExecutable(nullptr, nullptr)).empty()) { + // If we can't get the path using dladdr, try to get the main executable + // path. This may be needed when we're statically linking libclang with + // musl libc, for example. + LibClangPath += Path; + } else { + // It's rather unlikely we end up here. But it could happen, so report an + // error instead of crashing. + llvm::report_fatal_error("could not locate Clang resource path"); + } - // We now have the CIndex directory, locate clang relative to it. - LibClangPath += info.dli_fname; #endif // Cache our result. @@ -166,7 +176,7 @@ LibclangInvocationReporter::LibclangInvocationReporter( if (llvm::sys::fs::createUniqueFile(TempPath, FD, TempPath, llvm::sys::fs::OF_Text)) return; - File = std::string(TempPath.begin(), TempPath.end()); + File = static_cast<std::string>(TempPath); llvm::raw_fd_ostream OS(FD, /*ShouldClose=*/true); // Write out the information about the invocation to it. diff --git a/gnu/llvm/clang/tools/libclang/CMakeLists.txt b/gnu/llvm/clang/tools/libclang/CMakeLists.txt index bf88dca0a34..4f23065a247 100644 --- a/gnu/llvm/clang/tools/libclang/CMakeLists.txt +++ b/gnu/llvm/clang/tools/libclang/CMakeLists.txt @@ -2,7 +2,22 @@ # ABI, and when it is updated, it should be updated to the current # LLVM_VERSION_MAJOR. # Please also see clang/tools/libclang/libclang.map -set(CLANG_SONAME 13) + +# This option defaults to CLANG_FORCE_MATCHING_LIBCLANG_SOVERSION +# to ON - which means that it by default matches CLANG_VERSION_MAJOR +# +# TODO: This should probably not be a option going forward but we +# we should commit to a way to do it. But due to getting this out +# in LLVM 15.x we opted for a option. +set(LIBCLANG_SOVERSION_ARG) +if(NOT CLANG_FORCE_MATCHING_LIBCLANG_SOVERSION) + set(LIBCLANG_SOVERSION_ARG SOVERSION 13) +endif() + +# TODO: harmonize usage of LIBCLANG_SOVERSION / LIBCLANG_LIBARY_VERSION +# below; this was added under time-pressure to avoid reverting the +# better default from LLVM 14 for LLVM 15.0.0-rc3, hence no time +# to clean up previous inconsistencies. set(SOURCES ARCMigrate.cpp @@ -17,6 +32,7 @@ set(SOURCES CIndexer.cpp CXComment.cpp CXCursor.cpp + CXExtractAPI.cpp CXIndexDataConsumer.cpp CXCompilationDatabase.cpp CXLoadedDiagnostic.cpp @@ -45,6 +61,7 @@ set(LIBS clangAST clangBasic clangDriver + clangExtractAPI clangFrontend clangIndex clangLex @@ -132,6 +149,7 @@ add_clang_library(libclang ${ENABLE_SHARED} ${ENABLE_STATIC} INSTALL_WITH_TOOLCH ${LLVM_TARGETS_TO_BUILD} Core Support + TargetParser ) if(ENABLE_STATIC) @@ -179,14 +197,14 @@ if(ENABLE_SHARED) set_target_properties(libclang PROPERTIES VERSION ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}${LLVM_VERSION_SUFFIX} - SOVERSION ${CLANG_SONAME}) + ${LIBCLANG_SOVERSION_ARG}) endif() endif() if(INTERNAL_INSTALL_PREFIX) set(LIBCLANG_HEADERS_INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/include") else() - set(LIBCLANG_HEADERS_INSTALL_DESTINATION include) + set(LIBCLANG_HEADERS_INSTALL_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") endif() install(DIRECTORY ../../include/clang-c diff --git a/gnu/llvm/clang/tools/libclang/CXCursor.cpp b/gnu/llvm/clang/tools/libclang/CXCursor.cpp index 6fb47300efb..d48063f105f 100644 --- a/gnu/llvm/clang/tools/libclang/CXCursor.cpp +++ b/gnu/llvm/clang/tools/libclang/CXCursor.cpp @@ -299,8 +299,6 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::BinaryConditionalOperatorClass: case Stmt::TypeTraitExprClass: case Stmt::CoawaitExprClass: - case Stmt::ConceptSpecializationExprClass: - case Stmt::RequiresExprClass: case Stmt::DependentCoawaitExprClass: case Stmt::CoyieldExprClass: case Stmt::CXXBindTemporaryExprClass: @@ -637,12 +635,27 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, return getSelectorIdentifierCursor(SelectorIdIndex, C); } + case Stmt::ConceptSpecializationExprClass: + K = CXCursor_ConceptSpecializationExpr; + break; + + case Stmt::RequiresExprClass: + K = CXCursor_RequiresExpr; + break; + + case Stmt::CXXParenListInitExprClass: + K = CXCursor_CXXParenListInitExpr; + break; + case Stmt::MSDependentExistsStmtClass: K = CXCursor_UnexposedStmt; break; case Stmt::OMPCanonicalLoopClass: K = CXCursor_OMPCanonicalLoop; break; + case Stmt::OMPMetaDirectiveClass: + K = CXCursor_OMPMetaDirective; + break; case Stmt::OMPParallelDirectiveClass: K = CXCursor_OMPParallelDirective; break; @@ -685,6 +698,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::OMPParallelMasterDirectiveClass: K = CXCursor_OMPParallelMasterDirective; break; + case Stmt::OMPParallelMaskedDirectiveClass: + K = CXCursor_OMPParallelMaskedDirective; + break; case Stmt::OMPParallelSectionsDirectiveClass: K = CXCursor_OMPParallelSectionsDirective; break; @@ -700,6 +716,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::OMPTaskwaitDirectiveClass: K = CXCursor_OMPTaskwaitDirective; break; + case Stmt::OMPErrorDirectiveClass: + K = CXCursor_OMPErrorDirective; + break; case Stmt::OMPTaskgroupDirectiveClass: K = CXCursor_OMPTaskgroupDirective; break; @@ -757,15 +776,27 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::OMPMasterTaskLoopDirectiveClass: K = CXCursor_OMPMasterTaskLoopDirective; break; + case Stmt::OMPMaskedTaskLoopDirectiveClass: + K = CXCursor_OMPMaskedTaskLoopDirective; + break; case Stmt::OMPMasterTaskLoopSimdDirectiveClass: K = CXCursor_OMPMasterTaskLoopSimdDirective; break; + case Stmt::OMPMaskedTaskLoopSimdDirectiveClass: + K = CXCursor_OMPMaskedTaskLoopSimdDirective; + break; case Stmt::OMPParallelMasterTaskLoopDirectiveClass: K = CXCursor_OMPParallelMasterTaskLoopDirective; break; + case Stmt::OMPParallelMaskedTaskLoopDirectiveClass: + K = CXCursor_OMPParallelMaskedTaskLoopDirective; + break; case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass: K = CXCursor_OMPParallelMasterTaskLoopSimdDirective; break; + case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass: + K = CXCursor_OMPParallelMaskedTaskLoopSimdDirective; + break; case Stmt::OMPDistributeDirectiveClass: K = CXCursor_OMPDistributeDirective; break; @@ -820,6 +851,21 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::OMPMaskedDirectiveClass: K = CXCursor_OMPMaskedDirective; break; + case Stmt::OMPGenericLoopDirectiveClass: + K = CXCursor_OMPGenericLoopDirective; + break; + case Stmt::OMPTeamsGenericLoopDirectiveClass: + K = CXCursor_OMPTeamsGenericLoopDirective; + break; + case Stmt::OMPTargetTeamsGenericLoopDirectiveClass: + K = CXCursor_OMPTargetTeamsGenericLoopDirective; + break; + case Stmt::OMPParallelGenericLoopDirectiveClass: + K = CXCursor_OMPParallelGenericLoopDirective; + break; + case Stmt::OMPTargetParallelGenericLoopDirectiveClass: + K = CXCursor_OMPTargetParallelGenericLoopDirective; + break; case Stmt::BuiltinBitCastExprClass: K = CXCursor_BuiltinBitCastExpr; } @@ -1314,34 +1360,43 @@ CXCursor clang_Cursor_getArgument(CXCursor C, unsigned i) { } int clang_Cursor_getNumTemplateArguments(CXCursor C) { - if (clang_getCursorKind(C) != CXCursor_FunctionDecl) { + CXCursorKind kind = clang_getCursorKind(C); + if (kind != CXCursor_FunctionDecl && kind != CXCursor_StructDecl && + kind != CXCursor_ClassDecl && + kind != CXCursor_ClassTemplatePartialSpecialization) { return -1; } - const FunctionDecl *FD = - llvm::dyn_cast_or_null<clang::FunctionDecl>(getCursorDecl(C)); - if (!FD) { - return -1; + if (const auto *FD = + llvm::dyn_cast_if_present<clang::FunctionDecl>(getCursorDecl(C))) { + const FunctionTemplateSpecializationInfo *SpecInfo = + FD->getTemplateSpecializationInfo(); + if (!SpecInfo) { + return -1; + } + return SpecInfo->TemplateArguments->size(); } - const FunctionTemplateSpecializationInfo *SpecInfo = - FD->getTemplateSpecializationInfo(); - if (!SpecInfo) { - return -1; + if (const auto *SD = + llvm::dyn_cast_if_present<clang::ClassTemplateSpecializationDecl>( + getCursorDecl(C))) { + return SD->getTemplateArgs().size(); } - return SpecInfo->TemplateArguments->size(); + return -1; } enum CXGetTemplateArgumentStatus { /** The operation completed successfully */ CXGetTemplateArgumentStatus_Success = 0, - /** The specified cursor did not represent a FunctionDecl. */ - CXGetTemplateArgumentStatus_CursorNotFunctionDecl = -1, + /** The specified cursor did not represent a FunctionDecl or + ClassTemplateSpecializationDecl. */ + CXGetTemplateArgumentStatus_CursorNotCompatibleDecl = -1, - /** The specified cursor was not castable to a FunctionDecl. */ - CXGetTemplateArgumentStatus_BadFunctionDeclCast = -2, + /** The specified cursor was not castable to a FunctionDecl or + ClassTemplateSpecializationDecl. */ + CXGetTemplateArgumentStatus_BadDeclCast = -2, /** A NULL FunctionTemplateSpecializationInfo was retrieved. */ CXGetTemplateArgumentStatus_NullTemplSpecInfo = -3, @@ -1352,28 +1407,42 @@ enum CXGetTemplateArgumentStatus { static int clang_Cursor_getTemplateArgument(CXCursor C, unsigned I, TemplateArgument *TA) { - if (clang_getCursorKind(C) != CXCursor_FunctionDecl) { - return CXGetTemplateArgumentStatus_CursorNotFunctionDecl; + CXCursorKind kind = clang_getCursorKind(C); + if (kind != CXCursor_FunctionDecl && kind != CXCursor_StructDecl && + kind != CXCursor_ClassDecl && + kind != CXCursor_ClassTemplatePartialSpecialization) { + return -1; } - const FunctionDecl *FD = - llvm::dyn_cast_or_null<clang::FunctionDecl>(getCursorDecl(C)); - if (!FD) { - return CXGetTemplateArgumentStatus_BadFunctionDeclCast; - } + if (const auto *FD = + llvm::dyn_cast_if_present<clang::FunctionDecl>(getCursorDecl(C))) { - const FunctionTemplateSpecializationInfo *SpecInfo = - FD->getTemplateSpecializationInfo(); - if (!SpecInfo) { - return CXGetTemplateArgumentStatus_NullTemplSpecInfo; + const FunctionTemplateSpecializationInfo *SpecInfo = + FD->getTemplateSpecializationInfo(); + if (!SpecInfo) { + return CXGetTemplateArgumentStatus_NullTemplSpecInfo; + } + + if (I >= SpecInfo->TemplateArguments->size()) { + return CXGetTemplateArgumentStatus_InvalidIndex; + } + + *TA = SpecInfo->TemplateArguments->get(I); + return 0; } - if (I >= SpecInfo->TemplateArguments->size()) { - return CXGetTemplateArgumentStatus_InvalidIndex; + if (const auto *SD = + llvm::dyn_cast_if_present<clang::ClassTemplateSpecializationDecl>( + getCursorDecl(C))) { + if (I >= SD->getTemplateArgs().size()) { + return CXGetTemplateArgumentStatus_InvalidIndex; + } + + *TA = SD->getTemplateArgs()[I]; + return 0; } - *TA = SpecInfo->TemplateArguments->get(I); - return 0; + return CXGetTemplateArgumentStatus_BadDeclCast; } enum CXTemplateArgumentKind clang_Cursor_getTemplateArgumentKind(CXCursor C, @@ -1705,7 +1774,7 @@ CXType clang_Cursor_getReceiverType(CXCursor C) { ME = dyn_cast_or_null<MemberExpr>(CE->getCallee()); if (ME) { - if (dyn_cast_or_null<CXXMethodDecl>(ME->getMemberDecl())) { + if (isa_and_nonnull<CXXMethodDecl>(ME->getMemberDecl())) { auto receiverTy = ME->getBase()->IgnoreImpCasts()->getType(); return cxtype::MakeCXType(receiverTy, TU); } diff --git a/gnu/llvm/clang/tools/libclang/CXExtractAPI.cpp b/gnu/llvm/clang/tools/libclang/CXExtractAPI.cpp new file mode 100644 index 00000000000..787334ab1bb --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXExtractAPI.cpp @@ -0,0 +1,151 @@ +//===- CXExtractAPI.cpp - libclang APIs for manipulating CXAPISet ---------===// +// +// 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 defines all libclang APIs related to manipulation CXAPISet +// +//===----------------------------------------------------------------------===// + +#include "CXCursor.h" +#include "CXString.h" +#include "CXTranslationUnit.h" +#include "clang-c/CXErrorCode.h" +#include "clang-c/Documentation.h" +#include "clang-c/Index.h" +#include "clang-c/Platform.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/ExtractAPI/API.h" +#include "clang/ExtractAPI/ExtractAPIVisitor.h" +#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Index/USRGeneration.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/CBindingWrapping.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::extractapi; + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(APISet, CXAPISet) + +static void WalkupFromMostDerivedType(ExtractAPIVisitor &Visitor, Decl *D); + +template <typename DeclTy> +static bool WalkupParentContext(DeclContext *Parent, + ExtractAPIVisitor &Visitor) { + if (auto *D = dyn_cast<DeclTy>(Parent)) { + WalkupFromMostDerivedType(Visitor, D); + return true; + } + return false; +} + +static void WalkupFromMostDerivedType(ExtractAPIVisitor &Visitor, Decl *D) { + switch (D->getKind()) { +#define ABSTRACT_DECL(DECL) +#define DECL(CLASS, BASE) \ + case Decl::CLASS: \ + Visitor.WalkUpFrom##CLASS##Decl(static_cast<CLASS##Decl *>(D)); \ + break; +#include "clang/AST/DeclNodes.inc" + } + + for (auto *Parent = D->getDeclContext(); Parent != nullptr; + Parent = Parent->getParent()) { + if (WalkupParentContext<ObjCContainerDecl>(Parent, Visitor)) + return; + if (WalkupParentContext<TagDecl>(Parent, Visitor)) + return; + } +} + +static CXString GenerateCXStringFromSymbolGraphData(llvm::json::Object Obj) { + llvm::SmallString<0> BackingString; + llvm::raw_svector_ostream OS(BackingString); + OS << Value(std::move(Obj)); + return cxstring::createDup(BackingString.str()); +} + +enum CXErrorCode clang_createAPISet(CXTranslationUnit tu, CXAPISet *out_api) { + if (cxtu::isNotUsableTU(tu) || !out_api) + return CXError_InvalidArguments; + + ASTUnit *Unit = cxtu::getASTUnit(tu); + + auto &Ctx = Unit->getASTContext(); + auto Lang = Unit->getInputKind().getLanguage(); + APISet *API = new APISet(Ctx.getTargetInfo().getTriple(), Lang, + Unit->getMainFileName().str()); + ExtractAPIVisitor Visitor( + Ctx, [](SourceLocation Loc) { return true; }, *API); + + for (auto It = Unit->top_level_begin(); It != Unit->top_level_end(); ++It) { + Visitor.TraverseDecl(*It); + } + + *out_api = wrap(API); + return CXError_Success; +} + +void clang_disposeAPISet(CXAPISet api) { delete unwrap(api); } + +CXString clang_getSymbolGraphForUSR(const char *usr, CXAPISet api) { + auto *API = unwrap(api); + + if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(usr, *API)) + return GenerateCXStringFromSymbolGraphData(std::move(*SGF)); + + return cxstring::createNull(); +} + +CXString clang_getSymbolGraphForCursor(CXCursor cursor) { + CXCursorKind Kind = clang_getCursorKind(cursor); + if (clang_isDeclaration(Kind)) { + const Decl *D = cxcursor::getCursorDecl(cursor); + + if (!D) + return cxstring::createNull(); + + CXTranslationUnit TU = cxcursor::getCursorTU(cursor); + if (!TU) + return cxstring::createNull(); + + ASTUnit *Unit = cxtu::getASTUnit(TU); + + auto &Ctx = Unit->getASTContext(); + auto Lang = Unit->getInputKind().getLanguage(); + APISet API(Ctx.getTargetInfo().getTriple(), Lang, + Unit->getMainFileName().str()); + ExtractAPIVisitor Visitor( + Ctx, [](SourceLocation Loc) { return true; }, API); + + SmallString<128> USR; + if (index::generateUSRForDecl(D, USR)) + return cxstring::createNull(); + + WalkupFromMostDerivedType(Visitor, const_cast<Decl *>(D)); + auto *Record = API.findRecordForUSR(USR); + + if (!Record) + return cxstring::createNull(); + + for (const auto &Fragment : Record->Declaration.getFragments()) { + if (Fragment.Declaration) + WalkupFromMostDerivedType(Visitor, + const_cast<Decl *>(Fragment.Declaration)); + } + + if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(USR, API)) + return GenerateCXStringFromSymbolGraphData(std::move(*SGF)); + } + + return cxstring::createNull(); +} diff --git a/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.cpp b/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.cpp index 2f892fd1a43..da58a48001c 100644 --- a/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.cpp +++ b/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.cpp @@ -146,6 +146,11 @@ public: DataConsumer.importedModule(D); return true; } + + bool VisitConceptDecl(const ConceptDecl *D) { + DataConsumer.handleConcept(D); + return true; + } }; CXSymbolRole getSymbolRole(SymbolRoleSet Role) { @@ -458,21 +463,23 @@ void CXIndexDataConsumer::enteredMainFile(const FileEntry *File) { } void CXIndexDataConsumer::ppIncludedFile(SourceLocation hashLoc, - StringRef filename, - const FileEntry *File, - bool isImport, bool isAngled, - bool isModuleImport) { + StringRef filename, + OptionalFileEntryRef File, + bool isImport, bool isAngled, + bool isModuleImport) { if (!CB.ppIncludedFile) return; + const FileEntry *FE = File ? &File->getFileEntry() : nullptr; + ScratchAlloc SA(*this); CXIdxIncludedFileInfo Info = { getIndexLoc(hashLoc), SA.toCStr(filename), static_cast<CXFile>( - const_cast<FileEntry *>(File)), + const_cast<FileEntry *>(FE)), isImport, isAngled, isModuleImport }; CXIdxClientFile idxFile = CB.ppIncludedFile(ClientData, &Info); - FileMap[File] = idxFile; + FileMap[FE] = idxFile; } void CXIndexDataConsumer::importedModule(const ImportDecl *ImportD) { @@ -881,6 +888,12 @@ bool CXIndexDataConsumer::handleTypeAliasTemplate(const TypeAliasTemplateDecl *D return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } +bool CXIndexDataConsumer::handleConcept(const ConceptDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), + /*isDefinition=*/true, /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + bool CXIndexDataConsumer::handleReference(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, const NamedDecl *Parent, @@ -1286,6 +1299,8 @@ static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage case SymbolKind::Destructor: return CXIdxEntity_CXXDestructor; case SymbolKind::ConversionFunction: return CXIdxEntity_CXXConversionFunction; case SymbolKind::Parameter: return CXIdxEntity_Variable; + case SymbolKind::Concept: + return CXIdxEntity_CXXConcept; } llvm_unreachable("invalid symbol kind"); } diff --git a/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.h b/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.h index ace9d59bf04..b78ef367eea 100644 --- a/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.h +++ b/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.h @@ -332,10 +332,9 @@ class CXIndexDataConsumer : public index::IndexDataConsumer { public: CXIndexDataConsumer(CXClientData clientData, IndexerCallbacks &indexCallbacks, - unsigned indexOptions, CXTranslationUnit cxTU) - : Ctx(nullptr), ClientData(clientData), CB(indexCallbacks), - IndexOptions(indexOptions), CXTU(cxTU), - StrScratch(), StrAdapterCount(0) { } + unsigned indexOptions, CXTranslationUnit cxTU) + : Ctx(nullptr), ClientData(clientData), CB(indexCallbacks), + IndexOptions(indexOptions), CXTU(cxTU), StrAdapterCount(0) {} ASTContext &getASTContext() const { return *Ctx; } CXTranslationUnit getCXTU() const { return CXTU; } @@ -363,9 +362,9 @@ public: void enteredMainFile(const FileEntry *File); - void ppIncludedFile(SourceLocation hashLoc, - StringRef filename, const FileEntry *File, - bool isImport, bool isAngled, bool isModuleImport); + void ppIncludedFile(SourceLocation hashLoc, StringRef filename, + OptionalFileEntryRef File, bool isImport, bool isAngled, + bool isModuleImport); void importedModule(const ImportDecl *ImportD); void importedPCH(const FileEntry *File); @@ -410,6 +409,8 @@ public: bool handleFunctionTemplate(const FunctionTemplateDecl *D); bool handleTypeAliasTemplate(const TypeAliasTemplateDecl *D); + bool handleConcept(const ConceptDecl *D); + bool handleReference(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, const NamedDecl *Parent, const DeclContext *DC, diff --git a/gnu/llvm/clang/tools/libclang/CXLoadedDiagnostic.cpp b/gnu/llvm/clang/tools/libclang/CXLoadedDiagnostic.cpp index b3dcf977b92..bb6942a45f4 100644 --- a/gnu/llvm/clang/tools/libclang/CXLoadedDiagnostic.cpp +++ b/gnu/llvm/clang/tools/libclang/CXLoadedDiagnostic.cpp @@ -235,7 +235,7 @@ protected: public: DiagLoader(enum CXLoadDiag_Error *e, CXString *es) - : SerializedDiagnosticReader(), error(e), errorString(es) { + : error(e), errorString(es) { if (error) *error = CXLoadDiag_None; if (errorString) diff --git a/gnu/llvm/clang/tools/libclang/CXString.cpp b/gnu/llvm/clang/tools/libclang/CXString.cpp index 2754795f4a6..5e427957a10 100644 --- a/gnu/llvm/clang/tools/libclang/CXString.cpp +++ b/gnu/llvm/clang/tools/libclang/CXString.cpp @@ -78,13 +78,22 @@ CXString createDup(const char *String) { } CXString createRef(StringRef String) { + if (!String.data()) + return createNull(); + + // If the string is empty, it might point to a position in another string + // while having zero length. Make sure we don't create a reference to the + // larger string. + if (String.empty()) + return createEmpty(); + // If the string is not nul-terminated, we have to make a copy. // FIXME: This is doing a one past end read, and should be removed! For memory // we don't manage, the API string can become unterminated at any time outside // our control. - if (!String.empty() && String.data()[String.size()] != 0) + if (String.data()[String.size()] != 0) return createDup(String); CXString Result; diff --git a/gnu/llvm/clang/tools/libclang/CXType.cpp b/gnu/llvm/clang/tools/libclang/CXType.cpp index 0199c95b356..a833da374be 100644 --- a/gnu/llvm/clang/tools/libclang/CXType.cpp +++ b/gnu/llvm/clang/tools/libclang/CXType.cpp @@ -22,6 +22,7 @@ #include "clang/AST/Type.h" #include "clang/Basic/AddressSpaces.h" #include "clang/Frontend/ASTUnit.h" +#include <optional> using namespace clang; @@ -60,6 +61,7 @@ static CXTypeKind GetBuiltinTypeKind(const BuiltinType *BT) { BTCASE(ULongAccum); BTCASE(Float16); BTCASE(Float128); + BTCASE(Ibm128); BTCASE(NullPtr); BTCASE(Overload); BTCASE(Dependent); @@ -115,6 +117,7 @@ static CXTypeKind GetTypeKind(QualType T) { TKCASE(Elaborated); TKCASE(Pipe); TKCASE(Attributed); + TKCASE(BTFTagAttributed); TKCASE(Atomic); default: return CXType_Unexposed; @@ -135,6 +138,10 @@ CXType cxtype::MakeCXType(QualType T, CXTranslationUnit TU) { return MakeCXType(ATT->getEquivalentType(), TU); } } + if (auto *ATT = T->getAs<BTFTagAttributedType>()) { + if (!(TU->ParsingOptions & CXTranslationUnit_IncludeAttributedTypes)) + return MakeCXType(ATT->getWrappedType(), TU); + } // Handle paren types as the original type if (auto *PTT = T->getAs<ParenType>()) { return MakeCXType(PTT->getInnerType(), TU); @@ -174,7 +181,7 @@ static inline CXTranslationUnit GetTU(CXType CT) { return static_cast<CXTranslationUnit>(CT.data[1]); } -static Optional<ArrayRef<TemplateArgument>> +static std::optional<ArrayRef<TemplateArgument>> GetTemplateArguments(QualType Type) { assert(!Type.isNull()); if (const auto *Specialization = Type->getAs<TemplateSpecializationType>()) @@ -187,16 +194,17 @@ GetTemplateArguments(QualType Type) { return TemplateDecl->getTemplateArgs().asArray(); } - return None; + return std::nullopt; } -static Optional<QualType> TemplateArgumentToQualType(const TemplateArgument &A) { +static std::optional<QualType> +TemplateArgumentToQualType(const TemplateArgument &A) { if (A.getKind() == TemplateArgument::Type) return A.getAsType(); - return None; + return std::nullopt; } -static Optional<QualType> +static std::optional<QualType> FindTemplateArgumentTypeAt(ArrayRef<TemplateArgument> TA, unsigned index) { unsigned current = 0; for (const auto &A : TA) { @@ -210,7 +218,7 @@ FindTemplateArgumentTypeAt(ArrayRef<TemplateArgument> TA, unsigned index) { return TemplateArgumentToQualType(A); current++; } - return None; + return std::nullopt; } CXType clang_getCursorType(CXCursor C) { @@ -478,6 +486,14 @@ try_again: return MakeCXType(T, GetTU(CT)); } +CXType clang_getUnqualifiedType(CXType CT) { + return MakeCXType(GetQualType(CT).getUnqualifiedType(), GetTU(CT)); +} + +CXType clang_getNonReferenceType(CXType CT) { + return MakeCXType(GetQualType(CT).getNonReferenceType(), GetTU(CT)); +} + CXCursor clang_getTypeDeclaration(CXType CT) { if (CT.kind == CXType_Invalid) return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); @@ -577,6 +593,7 @@ CXString clang_getTypeKindSpelling(enum CXTypeKind K) { TKIND(ULongAccum); TKIND(Float16); TKIND(Float128); + TKIND(Ibm128); TKIND(NullPtr); TKIND(Overload); TKIND(Dependent); @@ -608,6 +625,7 @@ CXString clang_getTypeKindSpelling(enum CXTypeKind K) { TKIND(Elaborated); TKIND(Pipe); TKIND(Attributed); + TKIND(BTFTagAttributed); TKIND(BFloat16); #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) TKIND(Id); #include "clang/Basic/OpenCLImageTypes.def" @@ -658,6 +676,7 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { TCALLINGCONV(X86RegCall); TCALLINGCONV(X86VectorCall); TCALLINGCONV(AArch64VectorCall); + TCALLINGCONV(AArch64SVEPCS); TCALLINGCONV(Win64); TCALLINGCONV(X86_64SysV); TCALLINGCONV(AAPCS); @@ -668,6 +687,7 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { TCALLINGCONV(PreserveMost); TCALLINGCONV(PreserveAll); case CC_SpirFunction: return CXCallingConv_Unexposed; + case CC_AMDGPUKernelCall: return CXCallingConv_Unexposed; case CC_OpenCLKernel: return CXCallingConv_Unexposed; break; } @@ -1049,6 +1069,9 @@ CXType clang_Type_getModifiedType(CXType CT) { if (auto *ATT = T->getAs<AttributedType>()) return MakeCXType(ATT->getModifiedType(), GetTU(CT)); + if (auto *ATT = T->getAs<BTFTagAttributedType>()) + return MakeCXType(ATT->getWrappedType(), GetTU(CT)); + return MakeCXType(QualType(), GetTU(CT)); } @@ -1142,7 +1165,7 @@ int clang_Type_getNumTemplateArguments(CXType CT) { if (!TA) return -1; - return GetTemplateArgumentArraySize(TA.getValue()); + return GetTemplateArgumentArraySize(*TA); } CXType clang_Type_getTemplateArgumentAsType(CXType CT, unsigned index) { @@ -1154,8 +1177,8 @@ CXType clang_Type_getTemplateArgumentAsType(CXType CT, unsigned index) { if (!TA) return MakeCXType(QualType(), GetTU(CT)); - Optional<QualType> QT = FindTemplateArgumentTypeAt(TA.getValue(), index); - return MakeCXType(QT.getValueOr(QualType()), GetTU(CT)); + std::optional<QualType> QT = FindTemplateArgumentTypeAt(*TA, index); + return MakeCXType(QT.value_or(QualType()), GetTU(CT)); } CXType clang_Type_getObjCObjectBaseType(CXType CT) { @@ -1309,8 +1332,7 @@ enum CXTypeNullabilityKind clang_Type_getNullability(CXType CT) { if (T.isNull()) return CXTypeNullability_Invalid; - ASTContext &Ctx = cxtu::getASTUnit(GetTU(CT))->getASTContext(); - if (auto nullability = T->getNullability(Ctx)) { + if (auto nullability = T->getNullability()) { switch (*nullability) { case NullabilityKind::NonNull: return CXTypeNullability_NonNull; diff --git a/gnu/llvm/clang/tools/libclang/CXType.h b/gnu/llvm/clang/tools/libclang/CXType.h index 1d458086c09..ffe70a9b1c3 100644 --- a/gnu/llvm/clang/tools/libclang/CXType.h +++ b/gnu/llvm/clang/tools/libclang/CXType.h @@ -17,9 +17,6 @@ #include "clang/AST/Type.h" namespace clang { - -class ASTUnit; - namespace cxtype { CXType MakeCXType(QualType T, CXTranslationUnit TU); diff --git a/gnu/llvm/clang/tools/libclang/CursorVisitor.h b/gnu/llvm/clang/tools/libclang/CursorVisitor.h index 364d9fdebdb..2a9d7a7de16 100644 --- a/gnu/llvm/clang/tools/libclang/CursorVisitor.h +++ b/gnu/llvm/clang/tools/libclang/CursorVisitor.h @@ -14,11 +14,16 @@ #include "Index_Internal.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/TypeLocVisitor.h" +#include <optional> namespace clang { class PreprocessingRecord; class ASTUnit; +namespace concepts { +class Requirement; +} + namespace cxcursor { class VisitorJob { @@ -37,6 +42,8 @@ public: MemberRefVisitKind, SizeOfPackExprPartsKind, LambdaExprPartsKind, + ConceptSpecializationExprVisitKind, + RequiresExprVisitKind, PostChildrenVisitKind }; @@ -201,7 +208,7 @@ public: bool VisitAttributes(Decl *D); bool VisitBlockDecl(BlockDecl *B); bool VisitCXXRecordDecl(CXXRecordDecl *D); - Optional<bool> shouldVisitCursor(CXCursor C); + std::optional<bool> shouldVisitCursor(CXCursor C); bool VisitDeclContext(DeclContext *DC); bool VisitTranslationUnitDecl(TranslationUnitDecl *D); bool VisitTypedefDecl(TypedefDecl *D); @@ -242,6 +249,9 @@ public: bool VisitStaticAssertDecl(StaticAssertDecl *D); bool VisitFriendDecl(FriendDecl *D); bool VisitDecompositionDecl(DecompositionDecl *D); + bool VisitConceptDecl(ConceptDecl *D); + bool VisitTypeConstraint(const TypeConstraint &TC); + bool VisitConceptRequirement(const concepts::Requirement &R); // Name visitor bool VisitDeclarationNameInfo(DeclarationNameInfo Name); @@ -269,7 +279,7 @@ public: LLVM_ATTRIBUTE_NOINLINE bool Visit(const Stmt *S); private: - Optional<bool> handleDeclForVisitation(const Decl *D); + std::optional<bool> handleDeclForVisitation(const Decl *D); }; } // namespace cxcursor diff --git a/gnu/llvm/clang/tools/libclang/FatalErrorHandler.cpp b/gnu/llvm/clang/tools/libclang/FatalErrorHandler.cpp index ef21569637f..506b047c1b1 100644 --- a/gnu/llvm/clang/tools/libclang/FatalErrorHandler.cpp +++ b/gnu/llvm/clang/tools/libclang/FatalErrorHandler.cpp @@ -9,13 +9,14 @@ #include "clang-c/FatalErrorHandler.h" #include "llvm/Support/ErrorHandling.h" +#include <stdio.h> #include <stdlib.h> -static void aborting_fatal_error_handler(void *, const std::string &reason, +static void aborting_fatal_error_handler(void *, const char *reason, bool) { // Write the result out to stderr avoiding errs() because raw_ostreams can // call report_fatal_error. - fprintf(stderr, "LIBCLANG FATAL ERROR: %s\n", reason.c_str()); + fprintf(stderr, "LIBCLANG FATAL ERROR: %s\n", reason); ::abort(); } diff --git a/gnu/llvm/clang/tools/libclang/Indexing.cpp b/gnu/llvm/clang/tools/libclang/Indexing.cpp index 0e83ec6ca79..24ca98d4de4 100644 --- a/gnu/llvm/clang/tools/libclang/Indexing.cpp +++ b/gnu/llvm/clang/tools/libclang/Indexing.cpp @@ -261,9 +261,9 @@ public: void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, - CharSourceRange FilenameRange, const FileEntry *File, - StringRef SearchPath, StringRef RelativePath, - const Module *Imported, + CharSourceRange FilenameRange, + OptionalFileEntryRef File, StringRef SearchPath, + StringRef RelativePath, const Module *Imported, SrcMgr::CharacteristicKind FileType) override { bool isImport = (IncludeTok.is(tok::identifier) && IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import); @@ -508,8 +508,11 @@ static CXErrorCode clang_indexSourceFile_Impl( if (source_filename) Args->push_back(source_filename); + CreateInvocationOptions CIOpts; + CIOpts.Diags = Diags; + CIOpts.ProbePrecompiled = true; // FIXME: historical default. Needed? std::shared_ptr<CompilerInvocation> CInvok = - createInvocationFromCommandLine(*Args, Diags); + createInvocation(*Args, std::move(CIOpts)); if (!CInvok) return CXError_Failure; @@ -900,9 +903,8 @@ int clang_indexSourceFileFullArgv( result = clang_indexSourceFile_Impl( idxAction, client_data, index_callbacks, index_callbacks_size, index_options, source_filename, command_line_args, - num_command_line_args, - llvm::makeArrayRef(unsaved_files, num_unsaved_files), out_TU, - TU_options); + num_command_line_args, llvm::ArrayRef(unsaved_files, num_unsaved_files), + out_TU, TU_options); }; llvm::CrashRecoveryContext CRC; diff --git a/gnu/llvm/clang/tools/libclang/libclang.map b/gnu/llvm/clang/tools/libclang/libclang.map index 716e2474966..e9818707185 100644 --- a/gnu/llvm/clang/tools/libclang/libclang.map +++ b/gnu/llvm/clang/tools/libclang/libclang.map @@ -405,6 +405,19 @@ LLVM_13 { local: *; }; +LLVM_16 { + global: + clang_getUnqualifiedType; + clang_getNonReferenceType; + clang_CXXMethod_isDeleted; + clang_CXXMethod_isCopyAssignmentOperator; + clang_CXXMethod_isMoveAssignmentOperator; + clang_createAPISet; + clang_disposeAPISet; + clang_getSymbolGraphForCursor; + clang_getSymbolGraphForUSR; +}; + # Example of how to add a new symbol version entry. If you do add a new symbol # version, please update the example to depend on the version you added. # LLVM_X { diff --git a/gnu/llvm/clang/tools/nvptx-arch/CMakeLists.txt b/gnu/llvm/clang/tools/nvptx-arch/CMakeLists.txt new file mode 100644 index 00000000000..9b31cdd772d --- /dev/null +++ b/gnu/llvm/clang/tools/nvptx-arch/CMakeLists.txt @@ -0,0 +1,26 @@ +# //===--------------------------------------------------------------------===// +# // +# // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# // See https://llvm.org/LICENSE.txt for details. +# // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# // +# //===--------------------------------------------------------------------===// + +set(LLVM_LINK_COMPONENTS Support) +add_clang_tool(nvptx-arch NVPTXArch.cpp) + +# TODO: This is deprecated. Since CMake 3.17 we can use FindCUDAToolkit instead. +find_package(CUDA QUIET) +find_library(cuda-library NAMES cuda PATHS /lib64) +if (NOT cuda-library AND CUDA_FOUND) + get_filename_component(CUDA_LIBDIR "${CUDA_cudart_static_LIBRARY}" DIRECTORY) + find_library(cuda-library NAMES cuda HINTS "${CUDA_LIBDIR}/stubs") +endif() + +# If we found the CUDA library directly we just dynamically link against it. +if (CUDA_FOUND AND cuda-library) + target_include_directories(nvptx-arch PRIVATE ${CUDA_INCLUDE_DIRS}) + target_link_libraries(nvptx-arch PRIVATE ${cuda-library}) +else() + target_compile_definitions(nvptx-arch PRIVATE "DYNAMIC_CUDA") +endif() diff --git a/gnu/llvm/clang/tools/nvptx-arch/NVPTXArch.cpp b/gnu/llvm/clang/tools/nvptx-arch/NVPTXArch.cpp new file mode 100644 index 00000000000..91723324c28 --- /dev/null +++ b/gnu/llvm/clang/tools/nvptx-arch/NVPTXArch.cpp @@ -0,0 +1,116 @@ +//===- NVPTXArch.cpp - list installed NVPTX devies ------*- 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 CUDA gpus installed in the +// system. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Error.h" +#include <cstdint> +#include <cstdio> +#include <memory> + +#if DYNAMIC_CUDA +typedef enum cudaError_enum { + CUDA_SUCCESS = 0, + CUDA_ERROR_NO_DEVICE = 100, +} CUresult; + +typedef enum CUdevice_attribute_enum { + CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR = 75, + CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR = 76, +} CUdevice_attribute; + +typedef uint32_t CUdevice; + +CUresult (*cuInit)(unsigned int); +CUresult (*cuDeviceGetCount)(int *); +CUresult (*cuGetErrorString)(CUresult, const char **); +CUresult (*cuDeviceGet)(CUdevice *, int); +CUresult (*cuDeviceGetAttribute)(int *, CUdevice_attribute, CUdevice); + +constexpr const char *DynamicCudaPath = "libcuda.so"; + +llvm::Error loadCUDA() { + std::string ErrMsg; + auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>( + llvm::sys::DynamicLibrary::getPermanentLibrary(DynamicCudaPath, &ErrMsg)); + if (!DynlibHandle->isValid()) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Failed to 'dlopen' %s", DynamicCudaPath); + } +#define DYNAMIC_INIT(SYMBOL) \ + { \ + void *SymbolPtr = DynlibHandle->getAddressOfSymbol(#SYMBOL); \ + if (!SymbolPtr) \ + return llvm::createStringError(llvm::inconvertibleErrorCode(), \ + "Failed to 'dlsym' " #SYMBOL); \ + SYMBOL = reinterpret_cast<decltype(SYMBOL)>(SymbolPtr); \ + } + DYNAMIC_INIT(cuInit); + DYNAMIC_INIT(cuDeviceGetCount); + DYNAMIC_INIT(cuGetErrorString); + DYNAMIC_INIT(cuDeviceGet); + DYNAMIC_INIT(cuDeviceGetAttribute); +#undef DYNAMIC_INIT + return llvm::Error::success(); +} +#else + +#include "cuda.h" +llvm::Error loadCUDA() { return llvm::Error::success(); } + +#endif + +static int handleError(CUresult Err) { + const char *ErrStr = nullptr; + CUresult Result = cuGetErrorString(Err, &ErrStr); + if (Result != CUDA_SUCCESS) + return 1; + fprintf(stderr, "CUDA error: %s\n", ErrStr); + return 1; +} + +int main(int argc, char *argv[]) { + // Attempt to load the NVPTX driver runtime. + if (llvm::Error Err = loadCUDA()) { + logAllUnhandledErrors(std::move(Err), llvm::errs()); + return 1; + } + + if (CUresult Err = cuInit(0)) { + if (Err == CUDA_ERROR_NO_DEVICE) + return 0; + else + return handleError(Err); + } + + int Count = 0; + if (CUresult Err = cuDeviceGetCount(&Count)) + return handleError(Err); + if (Count == 0) + return 0; + for (int DeviceId = 0; DeviceId < Count; ++DeviceId) { + CUdevice Device; + if (CUresult Err = cuDeviceGet(&Device, DeviceId)) + return handleError(Err); + + int32_t Major, Minor; + if (CUresult Err = cuDeviceGetAttribute( + &Major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, Device)) + return handleError(Err); + if (CUresult Err = cuDeviceGetAttribute( + &Minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, Device)) + return handleError(Err); + + printf("sm_%d%d\n", Major, Minor); + } + return 0; +} diff --git a/gnu/llvm/clang/tools/scan-build-py/CMakeLists.txt b/gnu/llvm/clang/tools/scan-build-py/CMakeLists.txt index c9f1cb7d6b2..3aca22c0b0a 100644 --- a/gnu/llvm/clang/tools/scan-build-py/CMakeLists.txt +++ b/gnu/llvm/clang/tools/scan-build-py/CMakeLists.txt @@ -43,7 +43,7 @@ foreach(BinFile ${BinFiles}) ${CMAKE_BINARY_DIR}/bin/scan-build-py DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin/scan-build) install (PROGRAMS "bin/scan-build" - DESTINATION bin + DESTINATION "${CMAKE_INSTALL_BINDIR}" RENAME scan-build-py COMPONENT scan-build-py) list(APPEND Depends ${CMAKE_BINARY_DIR}/bin/scan-build-py) @@ -56,7 +56,7 @@ foreach(BinFile ${BinFiles}) ${CMAKE_BINARY_DIR}/bin/ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile}) install(PROGRAMS bin/${BinFile} - DESTINATION bin + DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT scan-build-py) list(APPEND Depends ${CMAKE_BINARY_DIR}/bin/${BinFile}) endif() @@ -72,7 +72,7 @@ foreach(lib ${LibExecs}) DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libexec/${lib}) list(APPEND Depends ${CMAKE_BINARY_DIR}/libexec/${lib}) install(PROGRAMS libexec/${lib} - DESTINATION libexec + DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}" COMPONENT scan-build-py) endforeach() @@ -87,8 +87,8 @@ foreach(lib ${LibScanbuild}) ${CMAKE_BINARY_DIR}/lib/libscanbuild/ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/lib/libscanbuild/${lib}) list(APPEND Depends ${CMAKE_BINARY_DIR}/lib/libscanbuild/${lib}) - install(PROGRAMS lib/libscanbuild/${lib} - DESTINATION lib/libscanbuild + install(FILES lib/libscanbuild/${lib} + DESTINATION lib${CLANG_LIBDIR_SUFFIX}/libscanbuild COMPONENT scan-build-py) endforeach() @@ -105,8 +105,8 @@ foreach(resource ${LibScanbuildResources}) ${CMAKE_BINARY_DIR}/lib/libscanbuild/resources DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/lib/libscanbuild/resources/${resource}) list(APPEND Depends ${CMAKE_BINARY_DIR}/lib/libscanbuild/resources/${resource}) - install(PROGRAMS lib/libscanbuild/resources/${resource} - DESTINATION lib/libscanbuild/resources + install(FILES lib/libscanbuild/resources/${resource} + DESTINATION lib${CLANG_LIBDIR_SUFFIX}/libscanbuild/resources COMPONENT scan-build-py) endforeach() @@ -121,8 +121,8 @@ foreach(lib ${LibEar}) ${CMAKE_BINARY_DIR}/lib/libear/ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/lib/libear/${lib}) list(APPEND Depends ${CMAKE_BINARY_DIR}/lib/libear/${lib}) - install(PROGRAMS lib/libear/${lib} - DESTINATION lib/libear + install(FILES lib/libear/${lib} + DESTINATION lib${CLANG_LIBDIR_SUFFIX}/libear COMPONENT scan-build-py) endforeach() diff --git a/gnu/llvm/clang/tools/scan-build-py/bin/analyze-build b/gnu/llvm/clang/tools/scan-build-py/bin/analyze-build index b3f61429906..636b5aa4dd3 100755 --- a/gnu/llvm/clang/tools/scan-build-py/bin/analyze-build +++ b/gnu/llvm/clang/tools/scan-build-py/bin/analyze-build @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/gnu/llvm/clang/tools/scan-build-py/bin/intercept-build b/gnu/llvm/clang/tools/scan-build-py/bin/intercept-build index 9ecde399844..dcd0473ef54 100755 --- a/gnu/llvm/clang/tools/scan-build-py/bin/intercept-build +++ b/gnu/llvm/clang/tools/scan-build-py/bin/intercept-build @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/gnu/llvm/clang/tools/scan-build-py/bin/scan-build b/gnu/llvm/clang/tools/scan-build-py/bin/scan-build index a341751d993..3e5a5ade304 100755 --- a/gnu/llvm/clang/tools/scan-build-py/bin/scan-build +++ b/gnu/llvm/clang/tools/scan-build-py/bin/scan-build @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/gnu/llvm/clang/tools/scan-build-py/lib/libear/ear.c b/gnu/llvm/clang/tools/scan-build-py/lib/libear/ear.c index b06ec7ab000..323251181a4 100644 --- a/gnu/llvm/clang/tools/scan-build-py/lib/libear/ear.c +++ b/gnu/llvm/clang/tools/scan-build-py/lib/libear/ear.c @@ -411,6 +411,7 @@ static void bear_report_call(char const *fun, char const *const argv[]) { const char *cwd = getcwd(NULL, 0); if (0 == cwd) { perror("bear: getcwd"); + pthread_mutex_unlock(&mutex); exit(EXIT_FAILURE); } char const *const out_dir = initial_env[0]; @@ -419,11 +420,13 @@ static void bear_report_call(char const *fun, char const *const argv[]) { if (-1 == snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) { perror("bear: snprintf"); + pthread_mutex_unlock(&mutex); exit(EXIT_FAILURE); } FILE *fd = fopen(filename, "a+"); if (0 == fd) { perror("bear: fopen"); + pthread_mutex_unlock(&mutex); exit(EXIT_FAILURE); } fprintf(fd, "%d%c", getpid(), RS); @@ -437,13 +440,14 @@ static void bear_report_call(char const *fun, char const *const argv[]) { fprintf(fd, "%c", GS); if (fclose(fd)) { perror("bear: fclose"); + pthread_mutex_unlock(&mutex); exit(EXIT_FAILURE); } free((void *)cwd); pthread_mutex_unlock(&mutex); } -/* update environment assure that chilren processes will copy the desired +/* update environment assure that children processes will copy the desired * behaviour */ static int bear_capture_env_t(bear_env_t *env) { @@ -598,4 +602,4 @@ static void bear_strings_release(char const **in) { free((void *)*it); } free((void *)in); -}
\ No newline at end of file +} diff --git a/gnu/llvm/clang/tools/scan-build-py/lib/libscanbuild/analyze.py b/gnu/llvm/clang/tools/scan-build-py/lib/libscanbuild/analyze.py index 9a249a8e15c..ebd6df1dc75 100644 --- a/gnu/llvm/clang/tools/scan-build-py/lib/libscanbuild/analyze.py +++ b/gnu/llvm/clang/tools/scan-build-py/lib/libscanbuild/analyze.py @@ -39,8 +39,10 @@ from libscanbuild.shell import decode __all__ = ['scan_build', 'analyze_build', 'analyze_compiler_wrapper'] -COMPILER_WRAPPER_CC = 'analyze-cc' -COMPILER_WRAPPER_CXX = 'analyze-c++' +scanbuild_dir = os.path.dirname(os.path.realpath(__import__('sys').argv[0])) + +COMPILER_WRAPPER_CC = os.path.join(scanbuild_dir, '..', 'libexec', 'analyze-cc') +COMPILER_WRAPPER_CXX = os.path.join(scanbuild_dir, '..', 'libexec', 'analyze-c++') CTU_EXTDEF_MAP_FILENAME = 'externalDefMap.txt' CTU_TEMP_DEFMAP_FOLDER = 'tmpExternalDefMaps' @@ -355,6 +357,7 @@ def report_directory(hint, keep, output_format): try: yield name finally: + args = (name,) if os.listdir(name): if output_format not in ['sarif', 'sarif-html']: # FIXME: # 'scan-view' currently does not support sarif format. @@ -362,6 +365,7 @@ def report_directory(hint, keep, output_format): elif output_format == 'sarif-html': msg = "Run 'scan-view %s' to examine bug reports or see " \ "merged sarif results at %s/results-merged.sarif." + args = (name, name) else: msg = "View merged sarif results at %s/results-merged.sarif." keep = True @@ -370,7 +374,7 @@ def report_directory(hint, keep, output_format): msg = "Report directory '%s' contains no report, but kept." else: msg = "Removing directory '%s' because it contains no report." - logging.warning(msg, name) + logging.warning(msg, *args) if not keep: os.rmdir(name) @@ -382,8 +386,6 @@ def analyzer_params(args): result = [] - if args.store_model: - result.append('-analyzer-store={0}'.format(args.store_model)) if args.constraints_model: result.append('-analyzer-constraints={0}'.format( args.constraints_model)) diff --git a/gnu/llvm/clang/tools/scan-build-py/lib/libscanbuild/report.py b/gnu/llvm/clang/tools/scan-build-py/lib/libscanbuild/report.py index 729b25e6350..0962b636a92 100644 --- a/gnu/llvm/clang/tools/scan-build-py/lib/libscanbuild/report.py +++ b/gnu/llvm/clang/tools/scan-build-py/lib/libscanbuild/report.py @@ -417,7 +417,7 @@ def parse_bug_html(filename): 'bug_path_length': 1 } - with open(filename) as handler: + with open(filename, encoding='utf-8') as handler: for line in handler.readlines(): # do not read the file further if endsign.match(line): diff --git a/gnu/llvm/clang/tools/scan-build-py/libexec/analyze-c++ b/gnu/llvm/clang/tools/scan-build-py/libexec/analyze-c++ index a280b2fb4dd..5c72dfff4a8 100755 --- a/gnu/llvm/clang/tools/scan-build-py/libexec/analyze-c++ +++ b/gnu/llvm/clang/tools/scan-build-py/libexec/analyze-c++ @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/gnu/llvm/clang/tools/scan-build-py/libexec/analyze-cc b/gnu/llvm/clang/tools/scan-build-py/libexec/analyze-cc index 36bbcd93f98..fbdbde8f5b2 100755 --- a/gnu/llvm/clang/tools/scan-build-py/libexec/analyze-cc +++ b/gnu/llvm/clang/tools/scan-build-py/libexec/analyze-cc @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/gnu/llvm/clang/tools/scan-build-py/libexec/intercept-c++ b/gnu/llvm/clang/tools/scan-build-py/libexec/intercept-c++ index 9e541a180d6..b8c8e923ec7 100755 --- a/gnu/llvm/clang/tools/scan-build-py/libexec/intercept-c++ +++ b/gnu/llvm/clang/tools/scan-build-py/libexec/intercept-c++ @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/gnu/llvm/clang/tools/scan-build-py/libexec/intercept-cc b/gnu/llvm/clang/tools/scan-build-py/libexec/intercept-cc index 9e541a180d6..b8c8e923ec7 100755 --- a/gnu/llvm/clang/tools/scan-build-py/libexec/intercept-cc +++ b/gnu/llvm/clang/tools/scan-build-py/libexec/intercept-cc @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/gnu/llvm/clang/tools/scan-build/CMakeLists.txt b/gnu/llvm/clang/tools/scan-build/CMakeLists.txt index ec0702d76f1..ef687b0e90a 100644 --- a/gnu/llvm/clang/tools/scan-build/CMakeLists.txt +++ b/gnu/llvm/clang/tools/scan-build/CMakeLists.txt @@ -47,7 +47,7 @@ if(CLANG_INSTALL_SCANBUILD) DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile}) list(APPEND Depends ${CMAKE_BINARY_DIR}/bin/${BinFile}) install(PROGRAMS bin/${BinFile} - DESTINATION bin + DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT scan-build) endforeach() @@ -61,21 +61,21 @@ if(CLANG_INSTALL_SCANBUILD) DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libexec/${LibexecFile}) list(APPEND Depends ${CMAKE_BINARY_DIR}/libexec/${LibexecFile}) install(PROGRAMS libexec/${LibexecFile} - DESTINATION libexec + DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}" COMPONENT scan-build) endforeach() foreach(ManPage ${ManPages}) - add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1/${ManPage} + add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1/${ManPage}" COMMAND ${CMAKE_COMMAND} -E make_directory - ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1 + "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1" COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/man/${ManPage} - ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1/ + "${CMAKE_CURRENT_SOURCE_DIR}/man/${ManPage}" + "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1/" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/man/${ManPage}) - list(APPEND Depends ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1/${ManPage}) - install(PROGRAMS man/${ManPage} - DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 + list(APPEND Depends "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1/${ManPage}") + install(FILES man/${ManPage} + DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" COMPONENT scan-build) endforeach() @@ -89,7 +89,7 @@ if(CLANG_INSTALL_SCANBUILD) DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/share/scan-build/${ShareFile}) list(APPEND Depends ${CMAKE_BINARY_DIR}/share/scan-build/${ShareFile}) install(FILES share/scan-build/${ShareFile} - DESTINATION share/scan-build + DESTINATION "${CMAKE_INSTALL_DATADIR}/scan-build" COMPONENT scan-build) endforeach() diff --git a/gnu/llvm/clang/tools/scan-build/bin/scan-build b/gnu/llvm/clang/tools/scan-build/bin/scan-build index 645f5507b6f..8cd525f054f 100755 --- a/gnu/llvm/clang/tools/scan-build/bin/scan-build +++ b/gnu/llvm/clang/tools/scan-build/bin/scan-build @@ -14,7 +14,6 @@ use strict; use warnings; use FindBin qw($RealBin); -use Digest::MD5; use File::Basename; use File::Find; use File::Copy qw(copy); @@ -62,7 +61,6 @@ my %Options = ( UseCC => undef, # C compiler to use for compilation. UseCXX => undef, # C++ compiler to use for compilation. AnalyzerTarget => undef, - StoreModel => undef, ConstraintsModel => undef, InternalStats => undef, OutputFormat => "html", @@ -269,27 +267,6 @@ sub SetHtmlEnv { } ##----------------------------------------------------------------------------## -# ComputeDigest - Compute a digest of the specified file. -##----------------------------------------------------------------------------## - -sub ComputeDigest { - my $FName = shift; - DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName); - - # Use Digest::MD5. We don't have to be cryptographically secure. We're - # just looking for duplicate files that come from a non-malicious source. - # We use Digest::MD5 because it is a standard Perl module that should - # come bundled on most systems. - open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n"); - binmode FILE; - my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest; - close(FILE); - - # Return the digest. - return $Result; -} - -##----------------------------------------------------------------------------## # UpdatePrefix - Compute the common prefix of files. ##----------------------------------------------------------------------------## @@ -374,8 +351,6 @@ sub AddStatLine { # Sometimes a source file is scanned more than once, and thus produces # multiple error reports. We use a cache to solve this problem. -my %AlreadyScanned; - sub ScanFile { my $Index = shift; @@ -383,19 +358,6 @@ sub ScanFile { my $FName = shift; my $Stats = shift; - # Compute a digest for the report file. Determine if we have already - # scanned a file that looks just like it. - - my $digest = ComputeDigest("$Dir/$FName"); - - if (defined $AlreadyScanned{$digest}) { - # Redundant file. Remove it. - unlink("$Dir/$FName"); - return; - } - - $AlreadyScanned{$digest} = 1; - # At this point the report file is not world readable. Make it happen. chmod(0644, "$Dir/$FName"); @@ -1014,8 +976,7 @@ sub SetEnv { die "$var is undefined\n" if (!defined $var); $ENV{$var} = $EnvVars->{$var}; } - foreach my $var ('CCC_ANALYZER_STORE_MODEL', - 'CCC_ANALYZER_CONSTRAINTS_MODEL', + foreach my $var ('CCC_ANALYZER_CONSTRAINTS_MODEL', 'CCC_ANALYZER_INTERNAL_STATS', 'CCC_ANALYZER_OUTPUT_FORMAT', 'CCC_CC', @@ -1741,12 +1702,6 @@ sub ProcessArgs { next; } - if ($arg eq "-store") { - shift @$Args; - $Options{StoreModel} = shift @$Args; - next; - } - if ($arg eq "-constraints") { shift @$Args; $Options{ConstraintsModel} = shift @$Args; @@ -1995,7 +1950,6 @@ my %EnvVars = ( 'CCC_CC' => $Options{UseCC}, 'CCC_CXX' => $Options{UseCXX}, 'CCC_REPORT_FAILURES' => $Options{ReportFailures}, - 'CCC_ANALYZER_STORE_MODEL' => $Options{StoreModel}, 'CCC_ANALYZER_CONSTRAINTS_MODEL' => $Options{ConstraintsModel}, 'CCC_ANALYZER_INTERNAL_STATS' => $Options{InternalStats}, 'CCC_ANALYZER_OUTPUT_FORMAT' => $Options{OutputFormat}, diff --git a/gnu/llvm/clang/tools/scan-build/libexec/ccc-analyzer b/gnu/llvm/clang/tools/scan-build/libexec/ccc-analyzer index ed0d4d3d73f..0c900293956 100755 --- a/gnu/llvm/clang/tools/scan-build/libexec/ccc-analyzer +++ b/gnu/llvm/clang/tools/scan-build/libexec/ccc-analyzer @@ -72,7 +72,7 @@ my $AnalyzerTarget; # If on OSX, use xcrun to determine the SDK root. my $UseXCRUN = 0; -if (`uname -a` =~ m/Darwin/) { +if (`uname -s` =~ m/Darwin/) { $DefaultCCompiler = 'clang'; $DefaultCXXCompiler = 'clang++'; # Older versions of OSX do not have xcrun to @@ -80,7 +80,7 @@ if (`uname -a` =~ m/Darwin/) { if (-x "/usr/bin/xcrun") { $UseXCRUN = 1; } -} elsif (`uname -a` =~ m/OpenBSD/) { +} elsif (`uname -s` =~ m/(FreeBSD|OpenBSD)/) { $DefaultCCompiler = 'cc'; $DefaultCXXCompiler = 'c++'; } else { @@ -466,9 +466,6 @@ my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'}; # Get the plugins to load. my $Plugins = $ENV{'CCC_ANALYZER_PLUGINS'}; -# Get the store model. -my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'}; - # Get the constraints engine. my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'}; @@ -729,10 +726,6 @@ if ($Action eq 'compile' or $Action eq 'link') { push @CmdArgs, '-x', $FileLang; } - if (defined $StoreModel) { - push @AnalyzeArgs, "-analyzer-store=$StoreModel"; - } - if (defined $ConstraintsModel) { push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel"; } diff --git a/gnu/llvm/clang/tools/scan-build/man/scan-build.1 b/gnu/llvm/clang/tools/scan-build/man/scan-build.1 index 4f3cd8d1033..10593dc1cd9 100644 --- a/gnu/llvm/clang/tools/scan-build/man/scan-build.1 +++ b/gnu/llvm/clang/tools/scan-build/man/scan-build.1 @@ -2,9 +2,9 @@ .\" See https://llvm.org/LICENSE.txt for license information. .\" SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception .\" $Id$ -.Dd May 25, 2012 +.Dd Aug 1, 2022 .Dt SCAN-BUILD 1 -.Os "clang" "3.5" +.Os "clang" "16" .Sh NAME .Nm scan-build .Nd Clang static analyzer @@ -110,7 +110,7 @@ increases verbosity. .It Fl V , Fl Fl view View analysis results in a web browser when the build completes. .It Fl constraints Op Ar model -Specify the contraint engine used by the analyzer. By default the +Specify the constraint engine used by the analyzer. By default the .Ql range model is used. Specifying .Ql basic diff --git a/gnu/llvm/clang/tools/scan-view/CMakeLists.txt b/gnu/llvm/clang/tools/scan-view/CMakeLists.txt index eccc6b83195..07aec76ee66 100644 --- a/gnu/llvm/clang/tools/scan-view/CMakeLists.txt +++ b/gnu/llvm/clang/tools/scan-view/CMakeLists.txt @@ -20,7 +20,7 @@ if(CLANG_INSTALL_SCANVIEW) DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile}) list(APPEND Depends ${CMAKE_BINARY_DIR}/bin/${BinFile}) install(PROGRAMS bin/${BinFile} - DESTINATION bin + DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT scan-view) endforeach() @@ -34,7 +34,7 @@ if(CLANG_INSTALL_SCANVIEW) DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/share/${ShareFile}) list(APPEND Depends ${CMAKE_BINARY_DIR}/share/scan-view/${ShareFile}) install(FILES share/${ShareFile} - DESTINATION share/scan-view + DESTINATION "${CMAKE_INSTALL_DATADIR}/scan-view" COMPONENT scan-view) endforeach() |