diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp --- a/lld/COFF/Chunks.cpp +++ b/lld/COFF/Chunks.cpp @@ -428,7 +428,7 @@ return; warn("some relocations in " + file->getName() + " are not sorted"); MutableArrayRef newRelocs( - bAlloc.Allocate(relocsSize), relocsSize); + bAlloc().Allocate(relocsSize), relocsSize); memcpy(newRelocs.data(), relocsData, relocsSize * sizeof(coff_relocation)); llvm::sort(newRelocs, cmpByVa); setRelocs(newRelocs); diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp --- a/lld/COFF/DLL.cpp +++ b/lld/COFF/DLL.cpp @@ -658,14 +658,14 @@ // Add a syntentic symbol for this load thunk, using the "__imp_load" // prefix, in case this thunk needs to be added to the list of valid // call targets for Control Flow Guard. - StringRef symName = saver.save("__imp_load_" + extName); + StringRef symName = saver().save("__imp_load_" + extName); s->loadThunkSym = cast(symtab->addSynthetic(symName, t)); } } thunks.push_back(tm); StringRef tmName = - saver.save("__tailMerge_" + syms[0]->getDLLName().lower()); + saver().save("__tailMerge_" + syms[0]->getDLLName().lower()); symtab->addSynthetic(tmName, tm); // Terminate with null values. addresses.push_back(make(8)); diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -64,14 +64,9 @@ Configuration *config; LinkerDriver *driver; -bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, - raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; - +bool link(ArrayRef args) { errorHandler().cleanupCallback = []() { TpiSource::clear(); - freeArena(); ObjFile::instances.clear(); PDBInputFile::instances.clear(); ImportFile::instances.clear(); @@ -84,8 +79,6 @@ errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now" " (use /errorlimit:0 to see all errors)"; - errorHandler().exitEarly = canExitEarly; - stderrOS.enable_colors(stderrOS.has_colors()); config = make(); symtab = make(); @@ -93,14 +86,7 @@ driver->linkerMain(args); - // Call exit() if we can to avoid calling destructors. - if (canExitEarly) - exitLld(errorCount() ? 1 : 0); - - bool ret = errorCount() == 0; - if (!canExitEarly) - errorHandler().reset(); - return ret; + return errorCount() == 0; } // Parse options of the form "old;new". @@ -170,7 +156,7 @@ static StringRef mangle(StringRef sym) { assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); if (config->machine == I386) - return saver.save("_" + sym); + return saver().save("_" + sym); return sym; } @@ -371,9 +357,9 @@ Export exp = parseExport(e); if (config->machine == I386 && config->mingw) { if (!isDecorated(exp.name)) - exp.name = saver.save("_" + exp.name); + exp.name = saver().save("_" + exp.name); if (!exp.extName.empty() && !isDecorated(exp.extName)) - exp.extName = saver.save("_" + exp.extName); + exp.extName = saver().save("_" + exp.extName); } exp.directives = true; config->exports.push_back(exp); @@ -455,11 +441,11 @@ SmallString<128> path = dir; sys::path::append(path, filename); if (sys::fs::exists(path.str())) - return saver.save(path.str()); + return saver().save(path.str()); if (!hasExt) { path.append(".obj"); if (sys::fs::exists(path.str())) - return saver.save(path.str()); + return saver().save(path.str()); } } return filename; @@ -496,7 +482,7 @@ SmallString<128> s = filename; sys::path::replace_extension(s, ".a"); - StringRef libName = saver.save("lib" + s.str()); + StringRef libName = saver().save("lib" + s.str()); return doFindFile(libName); } @@ -505,7 +491,7 @@ // Add ".lib" to Filename if that has no file extension. bool hasExt = filename.contains('.'); if (!hasExt) - filename = saver.save(filename + ".lib"); + filename = saver().save(filename + ".lib"); StringRef ret = doFindFile(filename); // For MinGW, if the find above didn't turn up anything, try // looking for a MinGW formatted library name. @@ -538,7 +524,7 @@ Optional envOpt = Process::GetEnv("LIB"); if (!envOpt.hasValue()) return; - StringRef env = saver.save(*envOpt); + StringRef env = saver().save(*envOpt); while (!env.empty()) { StringRef path; std::tie(path, env) = env.split(';'); @@ -889,8 +875,8 @@ driver->takeBuffer(std::move(mb)); if (config->outputFile.empty()) - config->outputFile = std::string(saver.save(m.OutputFile)); - config->importName = std::string(saver.save(m.ImportName)); + config->outputFile = std::string(saver().save(m.OutputFile)); + config->importName = std::string(saver().save(m.ImportName)); if (m.ImageBase) config->imageBase = m.ImageBase; if (m.StackReserve) @@ -918,13 +904,13 @@ // DLL instead. This is supported by both MS and GNU linkers. if (!e1.ExtName.empty() && e1.ExtName != e1.Name && StringRef(e1.Name).contains('.')) { - e2.name = saver.save(e1.ExtName); - e2.forwardTo = saver.save(e1.Name); + e2.name = saver().save(e1.ExtName); + e2.forwardTo = saver().save(e1.Name); config->exports.push_back(e2); continue; } - e2.name = saver.save(e1.Name); - e2.extName = saver.save(e1.ExtName); + e2.name = saver().save(e1.Name); + e2.extName = saver().save(e1.ExtName); e2.ordinal = e1.Ordinal; e2.noname = e1.Noname; e2.data = e1.Data; @@ -1918,9 +1904,9 @@ Export e = parseExport(arg->getValue()); if (config->machine == I386) { if (!isDecorated(e.name)) - e.name = saver.save("_" + e.name); + e.name = saver().save("_" + e.name); if (!e.extName.empty() && !isDecorated(e.extName)) - e.extName = saver.save("_" + e.extName); + e.extName = saver().save("_" + e.extName); } config->exports.push_back(e); } diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -48,17 +48,17 @@ class Executor { public: - explicit Executor(StringRef s) : prog(saver.save(s)) {} - void add(StringRef s) { args.push_back(saver.save(s)); } - void add(std::string &s) { args.push_back(saver.save(s)); } - void add(Twine s) { args.push_back(saver.save(s)); } - void add(const char *s) { args.push_back(saver.save(s)); } + explicit Executor(StringRef s) : prog(saver().save(s)) {} + void add(StringRef s) { args.push_back(saver().save(s)); } + void add(std::string &s) { args.push_back(saver().save(s)); } + void add(Twine s) { args.push_back(saver().save(s)); } + void add(const char *s) { args.push_back(saver().save(s)); } void run() { ErrorOr exeOrErr = sys::findProgramByName(prog); if (auto ec = exeOrErr.getError()) fatal("unable to find " + prog + " in PATH: " + ec.message()); - StringRef exe = saver.save(*exeOrErr); + StringRef exe = saver().save(*exeOrErr); args.insert(args.begin(), exe); if (sys::ExecuteAndWait(args[0], args) != 0) @@ -622,14 +622,14 @@ sym = sym.substr(0, sym.find('@', 1)); if (!sym.startswith("@")) { if (prefix && !sym.startswith("_")) - return saver.save("_" + sym); + return saver().save("_" + sym); return sym; } // For fastcall, remove the leading @ and replace it with an // underscore, if prefixes are used. sym = sym.substr(1); if (prefix) - sym = saver.save("_" + sym); + sym = saver().save("_" + sym); return sym; } @@ -840,7 +840,7 @@ argv.data() + argv.size()); if (!args.hasArg(OPT_lldignoreenv)) addLINK(expandedArgv); - cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); + cl::ExpandResponseFiles(saver(), getQuotingStyle(args), expandedArgv); args = optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex, missingCount); @@ -887,7 +887,7 @@ // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for // potentially every symbol in the object, so they must be handled quickly. SmallVector tokens; - cl::TokenizeWindowsCommandLineNoCopy(s, saver, tokens); + cl::TokenizeWindowsCommandLineNoCopy(s, saver(), tokens); for (StringRef tok : tokens) { if (tok.startswith_insensitive("/export:") || tok.startswith_insensitive("-export:")) @@ -900,7 +900,7 @@ // already copied quoted arguments for us, so those do not need to be // copied again. bool HasNul = tok.end() != s.end() && tok.data()[tok.size()] == '\0'; - rest.push_back(HasNul ? tok.data() : saver.save(tok).data()); + rest.push_back(HasNul ? tok.data() : saver().save(tok).data()); } } @@ -934,7 +934,7 @@ std::vector ArgParser::tokenize(StringRef s) { SmallVector tokens; - cl::TokenizeWindowsCommandLine(s, saver, tokens); + cl::TokenizeWindowsCommandLine(s, saver(), tokens); return std::vector(tokens.begin(), tokens.end()); } diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -941,7 +941,7 @@ Optional> ret = dwarf->getVariableLoc(var); if (!ret) return None; - return std::make_pair(saver.save(ret->first), ret->second); + return std::make_pair(saver().save(ret->first), ret->second); } // Used only for DWARF debug info, which is not common (except in MinGW @@ -966,8 +966,8 @@ fatal("broken import library"); // Read names and create an __imp_ symbol. - StringRef name = saver.save(StringRef(buf + sizeof(*hdr))); - StringRef impName = saver.save("__imp_" + name); + StringRef name = saver().save(StringRef(buf + sizeof(*hdr))); + StringRef impName = saver().save("__imp_" + name); const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1; dllName = std::string(StringRef(nameStart)); StringRef extName; @@ -1025,11 +1025,12 @@ // into consideration at LTO time (which very likely causes undefined // symbols later in the link stage). So we append file offset to make // filename unique. - MemoryBufferRef mbref( - mb.getBuffer(), - saver.save(archiveName.empty() ? path - : archiveName + sys::path::filename(path) + - utostr(offsetInArchive))); + MemoryBufferRef mbref(mb.getBuffer(), + saver().save(archiveName.empty() + ? path + : archiveName + + sys::path::filename(path) + + utostr(offsetInArchive))); obj = check(lto::InputFile::create(mbref)); } @@ -1065,6 +1066,7 @@ } // namespace void BitcodeFile::parse() { + llvm::StringSaver &saver = lld::saver(); std::vector> comdat(obj->getComdatTable().size()); for (size_t i = 0; i != obj->getComdatTable().size(); ++i) // FIXME: Check nodeduplicate @@ -1180,11 +1182,11 @@ s->nameType = ImportNameType::IMPORT_NAME; if (coffObj->getMachine() == I386) { - s->symbolName = symbolName = saver.save("_" + symbolName); + s->symbolName = symbolName = saver().save("_" + symbolName); s->nameType = ImportNameType::IMPORT_NAME_NOPREFIX; } - StringRef impName = saver.save("__imp_" + symbolName); + StringRef impName = saver().save("__imp_" + symbolName); symtab->addLazyDLLSymbol(this, s, impName); if (code) symtab->addLazyDLLSymbol(this, s, symbolName); @@ -1203,7 +1205,7 @@ size_t impSize = s->dllName.size() + s->symbolName.size() + 2; // +2 for NULs size_t size = sizeof(coff_import_header) + impSize; - char *buf = bAlloc.Allocate(size); + char *buf = bAlloc().Allocate(size); memset(buf, 0, size); char *p = buf; auto *imp = reinterpret_cast(p); diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp --- a/lld/COFF/LTO.cpp +++ b/lld/COFF/LTO.cpp @@ -171,12 +171,14 @@ files[task] = std::move(mb); })); + CommonLinkingContext &ctx = context(); + checkError(ltoObj->run( [&](size_t task) { return std::make_unique( std::make_unique(buf[task])); }, - cache)); + cache, [&ctx] { attachContext(ctx); })); // Emit empty index files for non-indexed files for (StringRef s : thinIndices) { @@ -208,8 +210,8 @@ // - foo.exe.lto.1.obj // - ... StringRef ltoObjName = - saver.save(Twine(config->outputFile) + ".lto" + - (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj"); + saver().save(Twine(config->outputFile) + ".lto" + + (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj"); // Get the native object contents either from the cache or from memory. Do // not use the cached MemoryBuffer directly, or the PDB will not be diff --git a/lld/COFF/MinGW.cpp b/lld/COFF/MinGW.cpp --- a/lld/COFF/MinGW.cpp +++ b/lld/COFF/MinGW.cpp @@ -182,8 +182,8 @@ static StringRef mangle(Twine sym) { assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); if (config->machine == I386) - return saver.save("_" + sym); - return saver.save(sym); + return saver().save("_" + sym); + return saver().save(sym); } // Handles -wrap option. @@ -246,7 +246,7 @@ // referenced it or not, though.) if (imp) { DefinedLocalImport *wrapimp = make( - saver.save("__imp_" + w.wrap->getName()), d); + saver().save("__imp_" + w.wrap->getName()), d); symtab->localImportChunks.push_back(wrapimp->getChunk()); map[imp] = wrapimp; } diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp --- a/lld/COFF/PDB.cpp +++ b/lld/COFF/PDB.cpp @@ -84,7 +84,7 @@ public: PDBLinker(SymbolTable *symtab) - : symtab(symtab), builder(bAlloc), tMerger(bAlloc) { + : symtab(symtab), builder(bAlloc()), tMerger(bAlloc()) { // This isn't strictly necessary, but link.exe usually puts an empty string // as the first "valid" string in the string table, so we do the same in // order to maintain as much byte-for-byte compatibility as possible. @@ -509,7 +509,7 @@ case SymbolKind::S_LPROCREF: { // sym is a temporary object, so we have to copy and reallocate the record // to stabilize it. - uint8_t *mem = bAlloc.Allocate(sym.length()); + uint8_t *mem = bAlloc().Allocate(sym.length()); memcpy(mem, sym.data().data(), sym.length()); builder.addGlobalSymbol(CVSymbol(makeArrayRef(mem, sym.length()))); break; @@ -1010,7 +1010,7 @@ // Allocate memory for a .debug$S / .debug$F section and relocate it. static ArrayRef relocateDebugChunk(SectionChunk &debugChunk) { - uint8_t *buffer = bAlloc.Allocate(debugChunk.getSize()); + uint8_t *buffer = bAlloc().Allocate(debugChunk.getSize()); assert(debugChunk.getOutputSectionIdx() == 0 && "debug sections should not be in output sections"); debugChunk.writeTo(buffer); @@ -1425,6 +1425,7 @@ ebs.Fields.push_back(path); ebs.Fields.push_back("cmd"); ebs.Fields.push_back(argStr); + llvm::BumpPtrAllocator &bAlloc = lld::bAlloc(); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( ons, bAlloc, CodeViewContainer::Pdb)); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( @@ -1456,7 +1457,7 @@ cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE; mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - cgs, bAlloc, CodeViewContainer::Pdb)); + cgs, bAlloc(), CodeViewContainer::Pdb)); } static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, @@ -1469,7 +1470,7 @@ sym.Rva = os.getRVA(); sym.SectionNumber = os.sectionIndex; mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - sym, bAlloc, CodeViewContainer::Pdb)); + sym, bAlloc(), CodeViewContainer::Pdb)); // Skip COFF groups in MinGW because it adds a significant footprint to the // PDB, due to each function being in its own section @@ -1544,6 +1545,7 @@ ts.Segment = thunkOS->sectionIndex; ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA(); + llvm::BumpPtrAllocator &bAlloc = lld::bAlloc(); mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( ons, bAlloc, CodeViewContainer::Pdb)); mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -129,7 +129,7 @@ const DILineInfo &lineInfo = *optionalLineInfo; if (lineInfo.FileName == DILineInfo::BadString) return None; - return std::make_pair(saver.save(lineInfo.FileName), lineInfo.Line); + return std::make_pair(saver().save(lineInfo.FileName), lineInfo.Line); } static Optional> diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -492,7 +492,7 @@ MutableArrayRef newRelocs; if (originalRelocs.data() == curRelocs.data()) { newRelocs = makeMutableArrayRef( - bAlloc.Allocate(originalRelocs.size()), + bAlloc().Allocate(originalRelocs.size()), originalRelocs.size()); } else { newRelocs = makeMutableArrayRef( diff --git a/lld/Common/CMakeLists.txt b/lld/Common/CMakeLists.txt --- a/lld/Common/CMakeLists.txt +++ b/lld/Common/CMakeLists.txt @@ -26,11 +26,15 @@ PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) +# Enable errors for any global constructors. +add_flag_if_supported("-Werror=global-constructors" WERROR_GLOBAL_CONSTRUCTOR) + add_lld_library(lldCommon Args.cpp DWARF.cpp ErrorHandler.cpp Filesystem.cpp + Globals.cpp Memory.cpp Reproduce.cpp Strings.cpp diff --git a/lld/Common/ErrorHandler.cpp b/lld/Common/ErrorHandler.cpp --- a/lld/Common/ErrorHandler.cpp +++ b/lld/Common/ErrorHandler.cpp @@ -7,14 +7,13 @@ //===----------------------------------------------------------------------===// #include "lld/Common/ErrorHandler.h" - -#include "llvm/Support/Parallel.h" - +#include "lld/Common/Globals.h" #include "llvm/ADT/Twine.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" @@ -24,14 +23,31 @@ using namespace llvm; using namespace lld; -// The functions defined in this file can be called from multiple threads, -// but lld::outs() or lld::errs() are not thread-safe. We protect them using a -// mutex. -static std::mutex mu; +namespace lld { +struct ErrorState { + // The functions defined in this file can be called from multiple threads, + // but lld::outs() or lld::errs() are not thread-safe. We protect them using a + // mutex. + std::mutex mu; + + // We want to separate multi-line messages with a newline. `sep` is "\n" + // if the last messages was multi-line. Otherwise "". + StringRef sep; + + // We wrap stdout and stderr so that you can pass alternative stdout/stderr as + // arguments to lld::*::link() functions. + llvm::raw_ostream *stdoutOS{}; + llvm::raw_ostream *stderrOS{}; + + ErrorHandler handler; +}; -// We want to separate multi-line messages with a newline. `sep` is "\n" -// if the last messages was multi-line. Otherwise "". -static StringRef sep; +ErrorState *initError(char *const start, char *&next, unsigned &offset) { + return initContextHelper(start, next, offset); +} + +void freeError(ErrorState *e) { e->~ErrorState(); } +} // namespace lld static StringRef getSeparator(const Twine &msg) { if (StringRef(msg.str()).contains('\n')) @@ -39,30 +55,41 @@ return ""; } -raw_ostream *lld::stdoutOS; -raw_ostream *lld::stderrOS; +ErrorHandler::~ErrorHandler() { + if (cleanupCallback) + cleanupCallback(); +} + +ErrorHandler &lld::errorHandler() { return context().e().handler; } -ErrorHandler &lld::errorHandler() { - static ErrorHandler handler; - return handler; +void lld::setStdPipes(llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS) { + CommonLinkingContext &ctx = context(); + ctx.e().stdoutOS = &stdoutOS; + ctx.e().stderrOS = &stderrOS; } raw_ostream &lld::outs() { - if (errorHandler().disableOutput) + ErrorState &e = context().e(); + if (e.handler.disableOutput) return llvm::nulls(); - return stdoutOS ? *stdoutOS : llvm::outs(); + return e.stdoutOS ? *e.stdoutOS : llvm::outs(); } raw_ostream &lld::errs() { - if (errorHandler().disableOutput) + ErrorState &e = context().e(); + if (e.handler.disableOutput) return llvm::nulls(); - return stderrOS ? *stderrOS : llvm::errs(); + return e.stderrOS ? *e.stderrOS : llvm::errs(); } void lld::exitLld(int val) { - // Delete any temporary file, while keeping the memory mapping open. - if (errorHandler().outputBuffer) - errorHandler().outputBuffer->discard(); + if (hasContext()) { + ErrorState &e = context().e(); + // Delete any temporary file, while keeping the memory mapping open. + if (e.handler.outputBuffer) + e.handler.outputBuffer->discard(); + } // Re-throw a possible signal or exception once/if it was catched by // safeLldMain(). @@ -75,11 +102,13 @@ if (!CrashRecoveryContext::GetCurrent()) llvm_shutdown(); - { - std::lock_guard lock(mu); + if (hasContext()) { + ErrorState &e = context().e(); + std::lock_guard lock(e.mu); lld::outs().flush(); lld::errs().flush(); } + // When running inside safeLldMain(), restore the control flow back to the // CrashRecoveryContext. Otherwise simply use _exit(), meanning no cleanup, // since we want to avoid further crashes on shutdown. @@ -171,14 +200,16 @@ void ErrorHandler::log(const Twine &msg) { if (!verbose || disableOutput) return; - std::lock_guard lock(mu); + ErrorState &e = context().e(); + std::lock_guard lock(e.mu); lld::errs() << logName << ": " << msg << "\n"; } void ErrorHandler::message(const Twine &msg) { if (disableOutput) return; - std::lock_guard lock(mu); + ErrorState &e = context().e(); + std::lock_guard lock(e.mu); lld::outs() << msg << "\n"; lld::outs().flush(); } @@ -189,13 +220,15 @@ return; } - std::lock_guard lock(mu); - lld::errs() << sep << getLocation(msg) << ": " << Colors::MAGENTA + ErrorState &e = context().e(); + std::lock_guard lock(e.mu); + lld::errs() << e.sep << getLocation(msg) << ": " << Colors::MAGENTA << "warning: " << Colors::RESET << msg << "\n"; - sep = getSeparator(msg); + e.sep = getSeparator(msg); } void ErrorHandler::error(const Twine &msg) { + ErrorState &e = context().e(); // If Visual Studio-style error message mode is enabled, // this particular error is printed out as two errors. if (vsDiagnostics) { @@ -214,19 +247,19 @@ bool exit = false; { - std::lock_guard lock(mu); + std::lock_guard lock(e.mu); if (errorLimit == 0 || errorCount < errorLimit) { - lld::errs() << sep << getLocation(msg) << ": " << Colors::RED + lld::errs() << e.sep << getLocation(msg) << ": " << Colors::RED << "error: " << Colors::RESET << msg << "\n"; } else if (errorCount == errorLimit) { - lld::errs() << sep << getLocation(msg) << ": " << Colors::RED + lld::errs() << e.sep << getLocation(msg) << ": " << Colors::RED << "error: " << Colors::RESET << errorLimitExceededMsg << "\n"; exit = exitEarly; } - sep = getSeparator(msg); + e.sep = getSeparator(msg); ++errorCount; } diff --git a/lld/Common/Globals.cpp b/lld/Common/Globals.cpp new file mode 100644 --- /dev/null +++ b/lld/Common/Globals.cpp @@ -0,0 +1,103 @@ +//===- Globals.cpp --------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Globals.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" + +using namespace llvm; +using namespace lld; + +namespace lld { +MemoryState *initMemoryState(char *const start, char *&next, unsigned &offset, + llvm::BumpPtrAllocator &bAlloc); +TargetOptState *initTargetOpt(char *const start, char *&next, unsigned &offset); +ErrorState *initError(char *const start, char *&next, unsigned &offset); +TimerState *initTimer(char *const start, char *&next, unsigned &offset); + +void freeMemory(MemoryState *m); +void freeTargetOpt(TargetOptState *to); +void freeError(ErrorState *e); +void freeTimer(TimerState *t); +} // namespace lld + +MemoryState &lld::CommonLinkingContext::m() { + return *(MemoryState *)((char *)storage + memoryOffset); +} +TargetOptState &lld::CommonLinkingContext::to() { + return *(TargetOptState *)((char *)storage + targetOptOffset); +} +ErrorState &lld::CommonLinkingContext::e() { + return *(ErrorState *)((char *)storage + errorOffset); +} +TimerState &lld::CommonLinkingContext::t() { + return *(TimerState *)((char *)storage + timerOffset); +} + +namespace { +// Reference to the current LLD instance. Each thread must be associated to a +// valid LLD instance, through `attachContext()`. +static LLVM_THREAD_LOCAL CommonLinkingContext *lctx; +} // namespace + +unsigned lld::CommonLinkingContext::id = 0; + +lld::CommonLinkingContext::CommonLinkingContext() { + char *start = (char *)storage; + char *next = (char *)storage; +#ifndef NDEBUG + char *end = next + sizeof(storage); +#endif + initMemoryState(start, next, memoryOffset, bAlloc); + assert(next <= end); + initTargetOpt(start, next, targetOptOffset); + assert(next <= end); + initError(start, next, errorOffset); + assert(next <= end); + initTimer(start, next, timerOffset); + assert(next <= end); + + valid = &id; + + // The calling thread shall have a reference to the global state. + lctx = this; +} + +lld::CommonLinkingContext::~CommonLinkingContext() { + if (valid != &id) + return; + assert(lctx && "The current thread must reference a global state!"); + + // Release states in reverse order. + freeTimer(&lctx->t()); + freeError(&lctx->e()); + freeTargetOpt(&lctx->to()); + freeMemory(&lctx->m()); + valid = nullptr; + + // Detach the current thread from the global state. + lctx = nullptr; +} + +void lld::CommonLinkingContext::shutdown() { delete lctx; } + +bool lld::CommonLinkingContext::isValid() { return valid == &id; } + +void lld::attachContext(CommonLinkingContext &ctx) { + assert(ctx.isValid()); + lctx = &ctx; +} + +void lld::detachContext() { lctx = nullptr; } + +CommonLinkingContext &lld::commonContext() { + assert(lctx && lctx->isValid()); + return *lctx; +} + +bool lld::hasContext() { return lctx != nullptr; } diff --git a/lld/Common/Memory.cpp b/lld/Common/Memory.cpp --- a/lld/Common/Memory.cpp +++ b/lld/Common/Memory.cpp @@ -7,16 +7,41 @@ //===----------------------------------------------------------------------===// #include "lld/Common/Memory.h" +#include "lld/Common/Globals.h" using namespace llvm; using namespace lld; -BumpPtrAllocator lld::bAlloc; -StringSaver lld::saver{bAlloc}; -std::vector lld::SpecificAllocBase::instances; +namespace lld { +struct MemoryState { + MemoryState(llvm::BumpPtrAllocator &bAlloc) : saver(bAlloc) {} + llvm::StringSaver saver; + llvm::DenseMap instances; +}; +MemoryState *initMemoryState(char *const start, char *&next, unsigned &offset, + llvm::BumpPtrAllocator &bAlloc) { + return initContextHelper(start, next, offset, bAlloc); +} + +void freeMemory(MemoryState *m) { + for (auto &instance : m->instances) + instance.second->reset(); + m->~MemoryState(); +} +} // namespace lld + +llvm::StringSaver &lld::saver() { return context().m().saver; } + +llvm::BumpPtrAllocator &lld::bAlloc() { return context().bAlloc; } -void lld::freeArena() { - for (SpecificAllocBase *alloc : SpecificAllocBase::instances) - alloc->reset(); - bAlloc.Reset(); +SpecificAllocBase * +lld::SpecificAllocBase::getOrCreate(void *tag, size_t size, size_t align, + SpecificAllocBase *(&creator)(void *)) { + auto &instances = context().m().instances; + auto &instance = instances[tag]; + if (instance == nullptr) { + void *storage = context().bAlloc.Allocate(size, align); + instance = creator(storage); + } + return instance; } diff --git a/lld/Common/TargetOptionsCommandFlags.cpp b/lld/Common/TargetOptionsCommandFlags.cpp --- a/lld/Common/TargetOptionsCommandFlags.cpp +++ b/lld/Common/TargetOptionsCommandFlags.cpp @@ -7,11 +7,22 @@ //===----------------------------------------------------------------------===// #include "lld/Common/TargetOptionsCommandFlags.h" - +#include "lld/Common/Globals.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/Target/TargetOptions.h" -static llvm::codegen::RegisterCodeGenFlags CGF; +using namespace lld; + +namespace lld { +struct TargetOptState { + llvm::codegen::RegisterCodeGenFlags cgf; +}; +TargetOptState *initTargetOpt(char *const start, char *&next, + unsigned &offset) { + return initContextHelper(start, next, offset); +} +void freeTargetOpt(TargetOptState *to) { to->~TargetOptState(); } +} // namespace lld llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() { return llvm::codegen::InitTargetOptionsFromCodeGenFlags(llvm::Triple()); diff --git a/lld/Common/Timer.cpp b/lld/Common/Timer.cpp --- a/lld/Common/Timer.cpp +++ b/lld/Common/Timer.cpp @@ -13,6 +13,16 @@ using namespace lld; using namespace llvm; +namespace lld { +struct TimerState { + Timer rootTimer{"Total Link Time"}; +}; +TimerState *initTimer(char *const start, char *&next, unsigned &offset) { + return initContextHelper(start, next, offset); +} +void freeTimer(TimerState *t) { t->~TimerState(); } +} // namespace lld + ScopedTimer::ScopedTimer(Timer &t) : t(&t) { startTime = std::chrono::high_resolution_clock::now(); } @@ -34,10 +44,14 @@ Timer &Timer::root() { static Timer rootTimer("Total Link Time"); return rootTimer; + // FIXME: Can't use the "commonGlobals" root timer just yet since globals in + // other LLD libs are using this root timer, but context() isn't + // available before main(), by design. + // return context().t().rootTimer; } void Timer::print() { - double totalDuration = static_cast(root().millis()); + double totalDuration = static_cast(millis()); // We want to print the grand total under all the intermediate phases, so we // print all children first, then print the total under that. @@ -47,7 +61,7 @@ message(std::string(50, '-')); - root().print(0, root().millis(), false); + print(0, millis(), false); } double Timer::millis() const { diff --git a/lld/ELF/AArch64ErrataFix.cpp b/lld/ELF/AArch64ErrataFix.cpp --- a/lld/ELF/AArch64ErrataFix.cpp +++ b/lld/ELF/AArch64ErrataFix.cpp @@ -398,9 +398,9 @@ patchee(p), patcheeOffset(off) { this->parent = p->getParent(); patchSym = addSyntheticLocal( - saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0, - getSize(), *this); - addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this); + saver().save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, + 0, getSize(), *this); + addSyntheticLocal(saver().save("$x"), STT_NOTYPE, 0, 0, *this); } uint64_t Patch843419Section::getLDSTAddr() const { diff --git a/lld/ELF/ARMErrataFix.cpp b/lld/ELF/ARMErrataFix.cpp --- a/lld/ELF/ARMErrataFix.cpp +++ b/lld/ELF/ARMErrataFix.cpp @@ -142,9 +142,9 @@ patchee(p), patcheeOffset(off), instr(instr), isARM(isARM) { parent = p->getParent(); patchSym = addSyntheticLocal( - saver.save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC, + saver().save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC, isARM ? 0 : 1, getSize(), *this); - addSyntheticLocal(saver.save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this); + addSyntheticLocal(saver().save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this); } uint64_t Patch657417Section::getBranchAddr() const { diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp --- a/lld/ELF/Arch/PPC64.cpp +++ b/lld/ELF/Arch/PPC64.cpp @@ -202,7 +202,7 @@ Symbol *sym = symtab->find(name); if (!sym || sym->isDefined()) return false; - sym->resolve(Defined{/*file=*/nullptr, saver.save(name), STB_GLOBAL, + sym->resolve(Defined{/*file=*/nullptr, saver().save(name), STB_GLOBAL, STV_HIDDEN, STT_FUNC, value, /*size=*/0, /*section=*/nullptr}); defined.push_back(cast(sym)); diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -77,14 +77,8 @@ static void setConfigs(opt::InputArgList &args); static void readConfigs(opt::InputArgList &args); -bool elf::link(ArrayRef args, bool canExitEarly, - raw_ostream &stdoutOS, raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; - +bool elf::link(ArrayRef args) { errorHandler().cleanupCallback = []() { - freeArena(); - inputSections.clear(); outputSections.clear(); archiveFiles.clear(); @@ -107,8 +101,6 @@ errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now (use " "-error-limit=0 to see all errors)"; - errorHandler().exitEarly = canExitEarly; - stderrOS.enable_colors(stderrOS.has_colors()); config = make(); driver = make(); @@ -121,16 +113,7 @@ driver->linkerMain(args); - // Exit immediately if we don't need to return to the caller. - // This saves time because the overhead of calling destructors - // for all globally-allocated objects is not negligible. - if (canExitEarly) - exitLld(errorCount() ? 1 : 0); - - bool ret = errorCount() == 0; - if (!canExitEarly) - errorHandler().reset(); - return ret; + return errorCount() == 0; } // Parses a linker -m option. @@ -1253,7 +1236,7 @@ // Parse LTO options. if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq)) - parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())), + parseClangOption(saver().save("-mcpu=" + StringRef(arg->getValue())), arg->getSpelling()); for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq_minus)) @@ -2030,9 +2013,9 @@ if (!sym) continue; - Symbol *real = addUnusedUndefined(saver.save("__real_" + name)); + Symbol *real = addUnusedUndefined(saver().save("__real_" + name)); Symbol *wrap = - addUnusedUndefined(saver.save("__wrap_" + name), sym->binding); + addUnusedUndefined(saver().save("__wrap_" + name), sym->binding); v.push_back({sym, real, wrap}); // We want to tell LTO not to inline symbols to be overwritten diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp --- a/lld/ELF/DriverUtils.cpp +++ b/lld/ELF/DriverUtils.cpp @@ -102,7 +102,7 @@ for (size_t i = 0, e = args.size(); i != e; ++i) { StringRef s = args[i]; if ((s == "-plugin-opt" || s == "--plugin-opt") && i + 1 != e) { - v.push_back(saver.save(s + "=" + args[i + 1]).data()); + v.push_back(saver().save(s + "=" + args[i + 1]).data()); ++i; } else { v.push_back(args[i]); @@ -125,7 +125,7 @@ // Expand response files (arguments in the form of @) // and then parse the argument again. - cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec); + cl::ExpandResponseFiles(saver(), getQuotingStyle(args), vec); concatLTOPluginOptions(vec); args = this->ParseArgs(vec, missingIndex, missingCount); diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -110,7 +110,7 @@ // The --chroot option changes our virtual root directory. // This is useful when you are dealing with files created by --reproduce. if (!config->chroot.empty() && path.startswith("/")) - path = saver.save(config->chroot + path); + path = saver().save(config->chroot + path); log(path); config->dependencyFiles.insert(llvm::CachedHashString(path)); @@ -1561,8 +1561,8 @@ } StringRef verName = this->stringTable.data() + verneeds[idx]; versionedNameBuffer.clear(); - name = - saver.save((name + "@" + verName).toStringRef(versionedNameBuffer)); + name = saver().save( + (name + "@" + verName).toStringRef(versionedNameBuffer)); } Symbol *s = symtab->addSymbol( Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()}); @@ -1604,7 +1604,7 @@ reinterpret_cast(verdefs[idx])->getAux()->vda_name; versionedNameBuffer.clear(); name = (name + "@" + verName).toStringRef(versionedNameBuffer); - symtab->addSymbol(SharedSymbol{*this, saver.save(name), sym.getBinding(), + symtab->addSymbol(SharedSymbol{*this, saver().save(name), sym.getBinding(), sym.st_other, sym.getType(), sym.st_value, sym.st_size, alignment, idx}); } @@ -1684,11 +1684,10 @@ // into consideration at LTO time (which very likely causes undefined // symbols later in the link stage). So we append file offset to make // filename unique. - StringRef name = - archiveName.empty() - ? saver.save(path) - : saver.save(archiveName + "(" + path::filename(path) + " at " + - utostr(offsetInArchive) + ")"); + StringRef name = archiveName.empty() + ? saver().save(path) + : saver().save(archiveName + "(" + path::filename(path) + + " at " + utostr(offsetInArchive) + ")"); MemoryBufferRef mbref(mb.getBuffer(), name); obj = CHECK(lto::InputFile::create(mbref), this); @@ -1715,7 +1714,7 @@ static Symbol *createBitcodeSymbol(const std::vector &keptComdats, const lto::InputFile::Symbol &objSym, BitcodeFile &f) { - StringRef name = saver.save(objSym.getName()); + StringRef name = saver().save(objSym.getName()); uint8_t binding = objSym.isWeak() ? STB_WEAK : STB_GLOBAL; uint8_t type = objSym.isTLS() ? STT_TLS : STT_NOTYPE; uint8_t visibility = mapVisibility(objSym.getVisibility()); @@ -1773,6 +1772,8 @@ if (!isAlnum(s[i])) s[i] = '_'; + llvm::StringSaver &saver = lld::saver(); + symtab->addSymbol(Defined{nullptr, saver.save(s + "_start"), STB_GLOBAL, STV_DEFAULT, STT_OBJECT, 0, 0, section}); symtab->addSymbol(Defined{nullptr, saver.save(s + "_end"), STB_GLOBAL, @@ -1825,7 +1826,7 @@ for (const lto::InputFile::Symbol &sym : obj->symbols()) { if (sym.isUndefined()) continue; - symtab->addSymbol(LazyObject{*this, saver.save(sym.getName())}); + symtab->addSymbol(LazyObject{*this, saver().save(sym.getName())}); } return; } diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -164,7 +164,7 @@ { static std::mutex mu; std::lock_guard lock(mu); - uncompressedBuf = bAlloc.Allocate(size); + uncompressedBuf = bAlloc().Allocate(size); } if (Error e = zlib::uncompress(toStringRef(rawData), uncompressedBuf, size)) @@ -244,7 +244,7 @@ // Restore the original section name. // (e.g. ".zdebug_info" -> ".debug_info") - name = saver.save("." + name.substr(2)); + name = saver().save("." + name.substr(2)); return; } diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -313,13 +313,15 @@ files[task] = std::move(mb); })); + CommonLinkingContext &ctx = context(); + if (!bitcodeFiles.empty()) checkError(ltoObj->run( [&](size_t task) { return std::make_unique( std::make_unique(buf[task])); }, - cache)); + cache, [&ctx] { attachContext(ctx); })); // Emit empty index files for non-indexed files but not in single-module mode. if (config->thinLTOModulesToCompile.empty()) { diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp --- a/lld/ELF/MarkLive.cpp +++ b/lld/ELF/MarkLive.cpp @@ -275,8 +275,8 @@ // As a workaround for glibc libc.a before 2.34 // (https://sourceware.org/PR27492), retain __libc_atexit and similar // sections regardless of zStartStopGC. - cNamedSections[saver.save("__start_" + sec->name)].push_back(sec); - cNamedSections[saver.save("__stop_" + sec->name)].push_back(sec); + cNamedSections[saver().save("__start_" + sec->name)].push_back(sec); + cNamedSections[saver().save("__stop_" + sec->name)].push_back(sec); } } diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -289,7 +289,7 @@ SmallString<128> pathData; StringRef path = (config->sysroot + s).toStringRef(pathData); if (sys::fs::exists(path)) - driver->addFile(saver.save(path), /*withLOption=*/false); + driver->addFile(saver().save(path), /*withLOption=*/false); else setError("cannot find " + s + " inside " + config->sysroot); return; @@ -303,7 +303,7 @@ if (config->sysroot.empty()) driver->addFile(s.substr(1), /*withLOption=*/false); else - driver->addFile(saver.save(config->sysroot + "/" + s.substr(1)), + driver->addFile(saver().save(config->sysroot + "/" + s.substr(1)), /*withLOption=*/false); } else if (s.startswith("-l")) { // Case 3: search in the list of library paths. @@ -326,7 +326,7 @@ } else { // Finally, search in the list of library paths. if (Optional path = findFromSearchPaths(s)) - driver->addFile(saver.save(*path), /*withLOption=*/true); + driver->addFile(saver().save(*path), /*withLOption=*/true); else setError("unable to find " + s); } diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -73,7 +73,7 @@ // This is only for testing. StringRef s = getenv("LLD_VERSION"); if (s.empty()) - s = saver.save(Twine("Linker: ") + getLLDVersion()); + s = saver().save(Twine("Linker: ") + getLLDVersion()); // +1 to include the terminating '\0'. return {(const uint8_t *)s.data(), s.size() + 1}; @@ -255,7 +255,7 @@ InputSection *elf::createInterpSection() { // StringSaver guarantees that the returned string ends with '\0'. - StringRef s = saver.save(config->dynamicLinker); + StringRef s = saver().save(config->dynamicLinker); ArrayRef contents = {(const uint8_t *)s.data(), s.size() + 1}; return make(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents, diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp --- a/lld/ELF/Thunks.cpp +++ b/lld/ELF/Thunks.cpp @@ -452,7 +452,7 @@ } void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__AArch64AbsLongThunk_" + destination.getName()), + addSymbol(saver().save("__AArch64AbsLongThunk_" + destination.getName()), STT_FUNC, 0, isec); addSymbol("$x", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 8, isec); @@ -478,8 +478,8 @@ } void AArch64ADRPThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__AArch64ADRPThunk_" + destination.getName()), STT_FUNC, - 0, isec); + addSymbol(saver().save("__AArch64ADRPThunk_" + destination.getName()), + STT_FUNC, 0, isec); addSymbol("$x", STT_NOTYPE, 0, isec); } @@ -578,7 +578,7 @@ } void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMv7ABSLongThunk_" + destination.getName()), + addSymbol(saver().save("__ARMv7ABSLongThunk_" + destination.getName()), STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); } @@ -596,7 +596,7 @@ } void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__Thumbv7ABSLongThunk_" + destination.getName()), + addSymbol(saver().save("__Thumbv7ABSLongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); } @@ -617,8 +617,8 @@ } void ARMV7PILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMV7PILongThunk_" + destination.getName()), STT_FUNC, - 0, isec); + addSymbol(saver().save("__ARMV7PILongThunk_" + destination.getName()), + STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); } @@ -638,7 +638,7 @@ } void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ThumbV7PILongThunk_" + destination.getName()), + addSymbol(saver().save("__ThumbV7PILongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); } @@ -653,7 +653,7 @@ } void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMv5ABSLongThunk_" + destination.getName()), + addSymbol(saver().save("__ARMv5ABSLongThunk_" + destination.getName()), STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 4, isec); @@ -679,8 +679,8 @@ } void ARMV5PILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMV5PILongThunk_" + destination.getName()), STT_FUNC, - 0, isec); + addSymbol(saver().save("__ARMV5PILongThunk_" + destination.getName()), + STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 12, isec); } @@ -709,7 +709,7 @@ } void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__Thumbv6MABSLongThunk_" + destination.getName()), + addSymbol(saver().save("__Thumbv6MABSLongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 8, isec); @@ -735,7 +735,7 @@ } void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__Thumbv6MPILongThunk_" + destination.getName()), + addSymbol(saver().save("__Thumbv6MPILongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 12, isec); @@ -753,7 +753,7 @@ } void MipsThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); } @@ -776,8 +776,9 @@ } void MicroMipsThunk::addSymbols(ThunkSection &isec) { - Defined *d = addSymbol( - saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); + Defined *d = + addSymbol(saver().save("__microLA25Thunk_" + destination.getName()), + STT_FUNC, 0, isec); d->stOther |= STO_MIPS_MICROMIPS; } @@ -800,8 +801,9 @@ } void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) { - Defined *d = addSymbol( - saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); + Defined *d = + addSymbol(saver().save("__microLA25Thunk_" + destination.getName()), + STT_FUNC, 0, isec); d->stOther |= STO_MIPS_MICROMIPS; } @@ -860,7 +862,7 @@ else os << ".plt_pic32."; os << destination.getName(); - addSymbol(saver.save(os.str()), STT_FUNC, 0, isec); + addSymbol(saver().save(os.str()), STT_FUNC, 0, isec); } bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec, @@ -869,7 +871,7 @@ } void PPC32LongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__LongThunk_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__LongThunk_" + destination.getName()), STT_FUNC, 0, isec); } @@ -913,8 +915,8 @@ } void PPC64PltCallStub::addSymbols(ThunkSection &isec) { - Defined *s = addSymbol(saver.save("__plt_" + destination.getName()), STT_FUNC, - 0, isec); + Defined *s = addSymbol(saver().save("__plt_" + destination.getName()), + STT_FUNC, 0, isec); s->needsTocRestore = true; s->file = destination.file; } @@ -964,7 +966,7 @@ } void PPC64R2SaveStub::addSymbols(ThunkSection &isec) { - Defined *s = addSymbol(saver.save("__toc_save_" + destination.getName()), + Defined *s = addSymbol(saver().save("__toc_save_" + destination.getName()), STT_FUNC, 0, isec); s->needsTocRestore = true; } @@ -1000,7 +1002,7 @@ } void PPC64R12SetupStub::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__gep_setup_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__gep_setup_" + destination.getName()), STT_FUNC, 0, isec); } @@ -1036,7 +1038,7 @@ } void PPC64PCRelPLTStub::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0, isec); } @@ -1052,7 +1054,7 @@ } void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__long_branch_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__long_branch_" + destination.getName()), STT_FUNC, 0, isec); } @@ -1088,7 +1090,7 @@ } void PPC64PCRelLongBranchThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__long_branch_pcrel_" + destination.getName()), + addSymbol(saver().save("__long_branch_pcrel_" + destination.getName()), STT_FUNC, 0, isec); } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -106,8 +106,8 @@ if (InputSectionBase *rel = isec->getRelocatedSection()) { OutputSection *out = rel->getOutputSection(); if (s->type == SHT_RELA) - return saver.save(".rela" + out->name); - return saver.save(".rel" + out->name); + return saver().save(".rela" + out->name); + return saver().save(".rel" + out->name); } } @@ -2308,9 +2308,9 @@ StringRef s = sec->name; if (!isValidCIdentifier(s)) return; - addOptionalRegular(saver.save("__start_" + s), sec, 0, + addOptionalRegular(saver().save("__start_" + s), sec, 0, config->zStartStopVisibility); - addOptionalRegular(saver.save("__stop_" + s), sec, -1, + addOptionalRegular(saver().save("__stop_" + s), sec, -1, config->zStartStopVisibility); } diff --git a/lld/MachO/ConcatOutputSection.cpp b/lld/MachO/ConcatOutputSection.cpp --- a/lld/MachO/ConcatOutputSection.cpp +++ b/lld/MachO/ConcatOutputSection.cpp @@ -323,8 +323,8 @@ // get written are happy. thunkInfo.isec->live = true; - StringRef thunkName = saver.save(funcSym->getName() + ".thunk." + - std::to_string(thunkInfo.sequence++)); + StringRef thunkName = saver().save(funcSym->getName() + ".thunk." + + std::to_string(thunkInfo.sequence++)); r.referent = thunkInfo.sym = symtab->addDefined( thunkName, /*file=*/nullptr, thunkInfo.isec, /*value=*/0, /*size=*/thunkSize, /*isWeakDef=*/false, /*isPrivateExtern=*/true, diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -145,7 +145,7 @@ path::append(buffer, path); // Do not warn about paths that are computed via the syslib roots if (fs::is_directory(buffer)) { - paths.push_back(saver.save(buffer.str())); + paths.push_back(saver().save(buffer.str())); found = true; } } @@ -163,7 +163,7 @@ SmallString<261> buffer(root); path::append(buffer, path); if (fs::is_directory(buffer)) - paths.push_back(saver.save(buffer.str())); + paths.push_back(saver().save(buffer.str())); } } return paths; @@ -1062,15 +1062,8 @@ symtab->addUndefined("dyld_stub_binder", /*file=*/nullptr, /*isWeak=*/false); } -bool macho::link(ArrayRef argsArr, bool canExitEarly, - raw_ostream &stdoutOS, raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; - - errorHandler().cleanupCallback = []() { freeArena(); }; - +bool macho::link(ArrayRef argsArr) { errorHandler().logName = args::getFilenameWithoutExe(argsArr[0]); - stderrOS.enable_colors(stderrOS.has_colors()); MachOOptTable parser; InputArgList args = parser.parse(argsArr.slice(1)); @@ -1369,7 +1362,7 @@ // Parse LTO options. if (const Arg *arg = args.getLastArg(OPT_mcpu)) - parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())), + parseClangOption(saver().save("-mcpu=" + StringRef(arg->getValue())), arg->getSpelling()); for (const Arg *arg : args.filtered(OPT_mllvm)) @@ -1450,9 +1443,5 @@ timeTraceProfilerCleanup(); } - - if (canExitEarly) - exitLld(errorCount() ? 1 : 0); - - return !errorCount(); + return errorCount() == 0; } diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp --- a/lld/MachO/DriverUtils.cpp +++ b/lld/MachO/DriverUtils.cpp @@ -82,7 +82,7 @@ // Expand response files (arguments in the form of @) // and then parse the argument again. - cl::ExpandResponseFiles(saver, cl::TokenizeGNUCommandLine, vec); + cl::ExpandResponseFiles(saver(), cl::TokenizeGNUCommandLine, vec); InputArgList args = ParseArgs(vec, missingIndex, missingCount); // Handle -fatal_warnings early since it converts missing argument warnings @@ -259,7 +259,7 @@ bool exists = fs::exists(location); searchedDylib(location, exists); if (exists) - return saver.save(location.str()); + return saver().save(location.str()); } } return {}; diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -195,6 +195,8 @@ return mbref; } + llvm::BumpPtrAllocator &bAlloc = lld::bAlloc(); + // Object files and archive files may be fat files, which contain multiple // real files for different CPU ISAs. Here, we search for a file that matches // with the current link target and returns it as a MemoryBufferRef. @@ -225,7 +227,7 @@ } InputFile::InputFile(Kind kind, const InterfaceFile &interface) - : id(idCount++), fileKind(kind), name(saver.save(interface.getPath())) {} + : id(idCount++), fileKind(kind), name(saver().save(interface.getPath())) {} template void ObjFile::parseSections(ArrayRef
sections) { @@ -1019,7 +1021,7 @@ auto *c = reinterpret_cast(cmd); parseTrie(buf + c->export_off, c->export_size, [&](const Twine &name, uint64_t flags) { - StringRef savedName = saver.save(name); + StringRef savedName = saver().save(name); if (handleLDSymbol(savedName)) return; bool isWeakDef = flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; @@ -1082,7 +1084,7 @@ umbrella = this; this->umbrella = umbrella; - installName = saver.save(interface.getInstallName()); + installName = saver().save(interface.getInstallName()); compatibilityVersion = interface.getCompatibilityVersion().rawValue(); currentVersion = interface.getCurrentVersion().rawValue(); @@ -1101,7 +1103,7 @@ exportingFile = isImplicitlyLinked(installName) ? this : umbrella; auto addSymbol = [&](const Twine &name) -> void { - symbols.push_back(symtab->addDylib(saver.save(name), exportingFile, + symbols.push_back(symtab->addDylib(saver().save(name), exportingFile, /*isWeakDef=*/false, /*isTlv=*/false)); }; @@ -1202,7 +1204,7 @@ config->platformInfo.minimum >= end) return; - this->installName = saver.save(installName); + this->installName = saver().save(installName); if (!compatVersion.empty()) { VersionTuple cVersion; @@ -1224,7 +1226,7 @@ if (!condition.consume_front("os") || version.tryParse(condition)) warn("failed to parse os version, symbol '" + originalName + "' ignored"); else if (version == config->platformInfo.minimum) - this->installName = saver.save(installName); + this->installName = saver().save(installName); } void DylibFile::checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const { @@ -1306,7 +1308,7 @@ static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym, BitcodeFile &file) { - StringRef name = saver.save(objSym.getName()); + StringRef name = saver().save(objSym.getName()); // TODO: support weak references if (objSym.isUndefined()) @@ -1347,11 +1349,12 @@ // So, we append the archive name to disambiguate two members with the same // name from multiple different archives, and offset within the archive to // disambiguate two members of the same name from a single archive. - MemoryBufferRef mbref( - mb.getBuffer(), - saver.save(archiveName.empty() ? path - : archiveName + sys::path::filename(path) + - utostr(offsetInArchive))); + MemoryBufferRef mbref(mb.getBuffer(), + saver().save(archiveName.empty() + ? path + : archiveName + + sys::path::filename(path) + + utostr(offsetInArchive))); obj = check(lto::InputFile::create(mbref)); diff --git a/lld/MachO/LTO.cpp b/lld/MachO/LTO.cpp --- a/lld/MachO/LTO.cpp +++ b/lld/MachO/LTO.cpp @@ -112,12 +112,14 @@ files[task] = std::move(mb); })); + CommonLinkingContext &ctx = context(); + checkError(ltoObj->run( [&](size_t task) { return std::make_unique( std::make_unique(buf[task])); }, - cache)); + cache, [&ctx] { attachContext(ctx); })); if (!config->thinLTOCacheDir.empty()) pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy); @@ -147,7 +149,7 @@ modTime = getModTime(filePath); } ret.push_back(make( - MemoryBufferRef(buf[i], saver.save(filePath.str())), modTime, "")); + MemoryBufferRef(buf[i], saver().save(filePath.str())), modTime, "")); } for (std::unique_ptr &file : files) if (file) diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -836,7 +836,7 @@ if (!dir.endswith(sep)) dir += sep; stab.strx = stringTableSection.addString( - saver.save(dir + compileUnit->getUnitDIE().getShortName())); + saver().save(dir + compileUnit->getUnitDIE().getShortName())); stabs.emplace_back(std::move(stab)); } @@ -858,7 +858,7 @@ if (!file->archiveName.empty()) path.append({"(", file->getName(), ")"}); - stab.strx = stringTableSection.addString(saver.save(path.str())); + stab.strx = stringTableSection.addString(saver().save(path.str())); stab.desc = 1; stab.value = file->modTime; stabs.emplace_back(std::move(stab)); diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -835,8 +835,8 @@ if (f->archiveName.empty()) filename = path::filename(f->getName()); else - filename = saver.save(path::filename(f->archiveName) + "(" + - path::filename(f->getName()) + ")"); + filename = saver().save(path::filename(f->archiveName) + "(" + + path::filename(f->getName()) + ")"); return std::max(entry.objectFiles.lookup(filename), entry.anyObjectFile); } @@ -1144,7 +1144,7 @@ // This section contains space for just a single word, and will be used by // dyld to cache an address to the image loader it uses. - uint8_t *arr = bAlloc.Allocate(target->wordSize); + uint8_t *arr = bAlloc().Allocate(target->wordSize); memset(arr, 0, target->wordSize); in.imageLoaderCache = make( segment_names::data, section_names::data, /*file=*/nullptr, diff --git a/lld/MinGW/Driver.cpp b/lld/MinGW/Driver.cpp --- a/lld/MinGW/Driver.cpp +++ b/lld/MinGW/Driver.cpp @@ -100,7 +100,7 @@ unsigned missingCount; SmallVector vec(argv.data(), argv.data() + argv.size()); - cl::ExpandResponseFiles(saver, getQuotingStyle(), vec); + cl::ExpandResponseFiles(saver(), getQuotingStyle(), vec); opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount); if (missingCount) @@ -154,13 +154,7 @@ // Convert Unix-ish command line arguments to Windows-ish ones and // then call coff::link. -bool mingw::link(ArrayRef argsArr, bool canExitEarly, - raw_ostream &stdoutOS, raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; - - stderrOS.enable_colors(stderrOS.has_colors()); - +bool mingw::link(ArrayRef argsArr) { MinGWOptTable parser; opt::InputArgList args = parser.parse(argsArr.slice(1)); @@ -445,5 +439,5 @@ // Pass the actual binary name, to make error messages be printed with // the right prefix. vec[0] = argsArr[0]; - return coff::link(vec, canExitEarly, stdoutOS, stderrOS); + return coff::link(vec); } diff --git a/lld/include/lld/Common/Driver.h b/lld/include/lld/Common/Driver.h --- a/lld/include/lld/Common/Driver.h +++ b/lld/include/lld/Common/Driver.h @@ -9,6 +9,7 @@ #ifndef LLD_COMMON_DRIVER_H #define LLD_COMMON_DRIVER_H +#include "lld/Common/Globals.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/raw_ostream.h" @@ -28,33 +29,27 @@ llvm::raw_ostream &stderrOS); namespace coff { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args); } namespace mingw { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args); } namespace elf { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args); } namespace mach_o { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args); } namespace macho { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args); } namespace wasm { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args); } } diff --git a/lld/include/lld/Common/ErrorHandler.h b/lld/include/lld/Common/ErrorHandler.h --- a/lld/include/lld/Common/ErrorHandler.h +++ b/lld/include/lld/Common/ErrorHandler.h @@ -68,8 +68,8 @@ #ifndef LLD_COMMON_ERRORHANDLER_H #define LLD_COMMON_ERRORHANDLER_H +#include "lld/Common/Globals.h" #include "lld/Common/LLVM.h" - #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileOutputBuffer.h" @@ -81,17 +81,12 @@ namespace lld { -// We wrap stdout and stderr so that you can pass alternative stdout/stderr as -// arguments to lld::*::link() functions. -extern llvm::raw_ostream *stdoutOS; -extern llvm::raw_ostream *stderrOS; - llvm::raw_ostream &outs(); llvm::raw_ostream &errs(); enum class ErrorTag { LibNotFound, SymbolNotFound }; -class ErrorHandler { +class ErrorHandler final { public: uint64_t errorCount = 0; uint64_t errorLimit = 20; @@ -112,11 +107,7 @@ void message(const Twine &msg); void warn(const Twine &msg); - void reset() { - if (cleanupCallback) - cleanupCallback(); - *this = ErrorHandler(); - } + ~ErrorHandler(); std::unique_ptr outputBuffer; @@ -129,6 +120,8 @@ /// Returns the default error handler. ErrorHandler &errorHandler(); +void setStdPipes(llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); + inline void error(const Twine &msg) { errorHandler().error(msg); } inline void error(const Twine &msg, ErrorTag tag, ArrayRef args) { errorHandler().error(msg, tag, args); diff --git a/lld/include/lld/Common/Globals.h b/lld/include/lld/Common/Globals.h new file mode 100644 --- /dev/null +++ b/lld/include/lld/Common/Globals.h @@ -0,0 +1,98 @@ +//===- Globals.h ------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Entry point for all global state in lldCommon. The objective is for LLD to be +// used "as a library" in a thread-safe manner. +// +// Instead of program-wide globals or function-local statics, we prefer +// aggregating all "global" states into a heap-based structure +// (CommonLinkingContext). This also achieves deterministic initialization & +// shutdown for all "global" states. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COMMON_GLOBALS_H +#define LLD_COMMON_GLOBALS_H + +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Allocator.h" + +namespace lld { + +struct MemoryState; +struct TargetOptState; +struct ErrorState; +struct TimerState; + +class CommonLinkingContext { +public: + CommonLinkingContext(); + virtual ~CommonLinkingContext(); + + static void shutdown(); + + bool isValid(); + + llvm::BumpPtrAllocator bAlloc; + + // Obtain global states as incomplete types. Client code needs to #include the + // corresponding headers to access their APIs. + MemoryState &m(); + TargetOptState &to(); + ErrorState &e(); + TimerState &t(); + +private: + // Provide storage so that all globals are aggregated on the stack closer to + // each other, since they should be accessed quite frequently. + uint64_t storage[50]{}; + + unsigned memoryOffset{}; + unsigned targetOptOffset{}; + unsigned errorOffset{}; + unsigned timerOffset{}; + + static unsigned id; + unsigned *valid; +}; + +// Attach global state to a thread. This should be usually be called just after +// the thread has been created, and before any calls to any LLD APIs. +void attachContext(CommonLinkingContext &ctx); + +// Detach the global state to prevent accidental re-use betwen tasks scheduled +// on the same thread. The thread pool could possibly be shared between LLD +// instances in the future. +void detachContext(); + +// Retrieve the global state associated to the current thread. There could be +// several global states, one for each running LLD instance in memory. +CommonLinkingContext &commonContext(); + +template T &context() { + return static_cast(commonContext()); +} + +// Tells whether this thread has a binding to a global state structure. +bool hasContext(); + +// Helper for building the *State classes. This is only to keep the *State +// classes private and only expose APIs that act on them (a bit like pImpl) +template +T *initContextHelper(char *const start, char *&addr, unsigned &offset, + U &&...args) { + void *storage = (char *)llvm::alignAddr(addr, llvm::Align(alignof(T))); + T *global = new (storage) T(std::forward(args)...); + addr = (char *)storage + sizeof(T); + offset = (char *)global - start; + return global; +} + +} // namespace lld + +#endif diff --git a/lld/include/lld/Common/Memory.h b/lld/include/lld/Common/Memory.h --- a/lld/include/lld/Common/Memory.h +++ b/lld/include/lld/Common/Memory.h @@ -23,37 +23,40 @@ #include "llvm/Support/Allocator.h" #include "llvm/Support/StringSaver.h" -#include namespace lld { - -// Use this arena if your object doesn't have a destructor. -extern llvm::BumpPtrAllocator bAlloc; -extern llvm::StringSaver saver; - -void freeArena(); +llvm::StringSaver &saver(); +llvm::BumpPtrAllocator &bAlloc(); // These two classes are hack to keep track of all // SpecificBumpPtrAllocator instances. struct SpecificAllocBase { - SpecificAllocBase() { instances.push_back(this); } virtual ~SpecificAllocBase() = default; virtual void reset() = 0; - static std::vector instances; + static SpecificAllocBase *getOrCreate(void *tag, size_t size, size_t align, + SpecificAllocBase *(&creator)(void *)); }; template struct SpecificAlloc : public SpecificAllocBase { + static SpecificAllocBase *create(void *storage) { + return new (storage) SpecificAlloc(); + } void reset() override { alloc.DestroyAll(); } llvm::SpecificBumpPtrAllocator alloc; + static int id; }; +template int SpecificAlloc::id = 0; + // Use a static local for these singletons so they are only registered if an // object of this instance is ever constructed. Otherwise we will create and // register ELF allocators for COFF and the reverse. template inline llvm::SpecificBumpPtrAllocator &getSpecificAllocSingleton() { - static SpecificAlloc instance; - return instance.alloc; + SpecificAllocBase *instance = SpecificAllocBase::getOrCreate( + &SpecificAlloc::id, sizeof(SpecificAlloc), + alignof(SpecificAlloc), SpecificAlloc::create); + return ((SpecificAlloc *)instance)->alloc; } // Use this arena if your object has a destructor. diff --git a/lld/include/lld/Common/Timer.h b/lld/include/lld/Common/Timer.h --- a/lld/include/lld/Common/Timer.h +++ b/lld/include/lld/Common/Timer.h @@ -46,6 +46,7 @@ double millis() const; private: + friend struct TimerState; explicit Timer(llvm::StringRef name); void print(int depth, double totalDuration, bool recurse = true) const; diff --git a/lld/include/lld/Core/LinkingContext.h b/lld/include/lld/Core/LinkingContext.h --- a/lld/include/lld/Core/LinkingContext.h +++ b/lld/include/lld/Core/LinkingContext.h @@ -9,6 +9,7 @@ #ifndef LLD_CORE_LINKING_CONTEXT_H #define LLD_CORE_LINKING_CONTEXT_H +#include "lld/Common/Globals.h" #include "lld/Core/Node.h" #include "lld/Core/Reader.h" #include "llvm/ADT/ArrayRef.h" @@ -34,7 +35,7 @@ /// The base class LinkingContext contains the options needed by core linking. /// Subclasses of LinkingContext have additional options needed by specific /// Writers. -class LinkingContext { +class LinkingContext : public CommonLinkingContext { public: virtual ~LinkingContext(); diff --git a/lld/lib/Driver/DarwinLdDriver.cpp b/lld/lib/Driver/DarwinLdDriver.cpp --- a/lld/lib/Driver/DarwinLdDriver.cpp +++ b/lld/lib/Driver/DarwinLdDriver.cpp @@ -14,6 +14,7 @@ #include "lld/Common/Args.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Globals.h" #include "lld/Common/LLVM.h" #include "lld/Core/ArchiveLibraryFile.h" #include "lld/Core/Error.h" @@ -1144,19 +1145,13 @@ } /// This is where the link is actually performed. -bool link(llvm::ArrayRef args, bool CanExitEarly, - raw_ostream &StdoutOS, raw_ostream &StderrOS) { - lld::stdoutOS = &StdoutOS; - lld::stderrOS = &StderrOS; - +bool link(llvm::ArrayRef args) { errorHandler().logName = args::getFilenameWithoutExe(args[0]); errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now (use " "'-error-limit 0' to see all errors)"; - errorHandler().exitEarly = CanExitEarly; - StderrOS.enable_colors(StderrOS.has_colors()); - MachOLinkingContext ctx; + MachOLinkingContext &ctx = context(); if (!parse(args, ctx)) return false; if (ctx.doNothing()) @@ -1216,13 +1211,7 @@ logAllUnhandledErrors(std::move(ec), lld::errs(), std::string()); return false; } - - // Call exit() if we can to avoid calling destructors. - if (CanExitEarly) - exitLld(errorCount() ? 1 : 0); - - - return true; + return errorCount() == 0; } } // end namespace mach_o diff --git a/lld/tools/lld/lld.cpp b/lld/tools/lld/lld.cpp --- a/lld/tools/lld/lld.cpp +++ b/lld/tools/lld/lld.cpp @@ -28,6 +28,7 @@ #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSwitch.h" @@ -37,6 +38,7 @@ #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" #include "llvm/Support/PluginLoader.h" #include @@ -89,7 +91,9 @@ // Expand response files (arguments in the form of @) // to allow detecting the -m argument from arguments in them. SmallVector expandedArgs(v.data(), v.data() + v.size()); - cl::ExpandResponseFiles(saver, getDefaultQuotingStyle(), expandedArgs); + BumpPtrAllocator A; + StringSaver Saver(A); + cl::ExpandResponseFiles(Saver, getDefaultQuotingStyle(), expandedArgs); for (auto it = expandedArgs.begin(); it + 1 != expandedArgs.end(); ++it) { if (StringRef(*it) != "-m") continue; @@ -136,29 +140,69 @@ return parseProgname(arg0); } +bool inTestOutputDisabled = false; + /// Universal linker main(). This linker emulates the gnu, darwin, or /// windows linker based on the argv[0] or -flavor option. static int lldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly = true) { + + CommonLinkingContext *ctx = nullptr; + std::vector args(argv, argv + argc); - switch (parseFlavor(args)) { - case Gnu: - if (isPETarget(args)) - return !mingw::link(args, exitEarly, stdoutOS, stderrOS); - return !elf::link(args, exitEarly, stdoutOS, stderrOS); - case WinLink: - return !coff::link(args, exitEarly, stdoutOS, stderrOS); - case Darwin: - return !macho::link(args, exitEarly, stdoutOS, stderrOS); - case DarwinOld: - return !mach_o::link(args, exitEarly, stdoutOS, stderrOS); - case Wasm: - return !lld::wasm::link(args, exitEarly, stdoutOS, stderrOS); - default: + Flavor f = parseFlavor(args); + bool isMinGW = f == Gnu ? isPETarget(args) : false; + + if (f == Invalid) die("lld is a generic driver.\n" "Invoke ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-ld" " (WebAssembly) instead"); - } + + // Create the global context for the corresponding driver. Currently most + // drivers use the "base" common context, but this will change as we + // progressively move global state to their driver-specific context. + if (f == DarwinOld) + ctx = new MachOLinkingContext; + else + ctx = new CommonLinkingContext; + + setStdPipes(stdoutOS, stderrOS); + errorHandler().exitEarly = exitEarly; + errorHandler().disableOutput = inTestOutputDisabled; + stderrOS.enable_colors(stderrOS.has_colors()); + + // FIXME: Temporary, bind all TaskGroup threads with the current LLD context. + llvm::parallel::detail::TaskGroup::wrapThreadState( + [&ctx](std::function &f) { + attachContext(*ctx); + f(); + detachContext(); + }); + + bool r; // The driver will be returning false if an error occurs. + + // Run the driver. + if (isMinGW) + r = mingw::link(args); + else if (f == Gnu) + r = elf::link(args); + else if (f == WinLink) + r = coff::link(args); + else if (f == Darwin) + r = macho::link(args); + else if (f == DarwinOld) + r = mach_o::link(args); + else if (f == Wasm) + r = lld::wasm::link(args); + + // Call exit() if we can to avoid calling destructors. + if (exitEarly) + exitLld(!r ? 1 : 0); + + // Delete global state & detach from current thread. + delete ctx; + + return !r ? 1 : 0; } // Similar to lldMain except that exceptions are caught. @@ -180,7 +224,7 @@ // Cleanup memory and reset everything back in pristine condition. This path // is only taken when LLD is in test, or when it is used as a library. llvm::CrashRecoveryContext crc; - if (!crc.RunSafely([&]() { errorHandler().reset(); })) { + if (!crc.RunSafely([&]() { CommonLinkingContext::shutdown(); })) { // The memory is corrupted beyond any possible recovery. return {r, /*canRunAgain=*/false}; } @@ -210,8 +254,7 @@ for (unsigned i = inTestVerbosity(); i > 0; --i) { // Disable stdout/stderr for all iterations but the last one. - if (i != 1) - errorHandler().disableOutput = true; + inTestOutputDisabled = (i != 1); // Execute one iteration. auto r = safeLldMain(argc, argv, llvm::outs(), llvm::errs()); diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -81,18 +81,11 @@ }; } // anonymous namespace -bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, - raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; - - errorHandler().cleanupCallback = []() { freeArena(); }; - +bool link(ArrayRef args) { errorHandler().logName = args::getFilenameWithoutExe(args[0]); errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now (use " "-error-limit=0 to see all errors)"; - stderrOS.enable_colors(stderrOS.has_colors()); config = make(); symtab = make(); @@ -100,13 +93,7 @@ initLLVM(); LinkerDriver().linkerMain(args); - // Exit immediately if we don't need to return to the caller. - // This saves time because the overhead of calling destructors - // for all globally-allocated objects is not negligible. - if (canExitEarly) - exitLld(errorCount() ? 1 : 0); - - return !errorCount(); + return errorCount() == 0; } // Create prefix string literals used in Options.td @@ -189,7 +176,7 @@ // Expand response files (arguments in the form of @) // and then parse the argument again. - cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec); + cl::ExpandResponseFiles(saver(), getQuotingStyle(args), vec); args = this->ParseArgs(vec, missingIndex, missingCount); handleColorDiagnostics(args); @@ -760,8 +747,8 @@ if (!sym) continue; - Symbol *real = addUndefined(saver.save("__real_" + name)); - Symbol *wrap = addUndefined(saver.save("__wrap_" + name)); + Symbol *real = addUndefined(saver().save("__real_" + name)); + Symbol *wrap = addUndefined(saver().save("__wrap_" + name)); v.push_back({sym, real, wrap}); // We want to tell LTO not to inline symbols to be overwritten diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -720,7 +720,7 @@ static Symbol *createBitcodeSymbol(const std::vector &keptComdats, const lto::InputFile::Symbol &objSym, BitcodeFile &f) { - StringRef name = saver.save(objSym.getName()); + StringRef name = saver().save(objSym.getName()); uint32_t flags = objSym.isWeak() ? WASM_SYMBOL_BINDING_WEAK : 0; flags |= mapVisibility(objSym.getVisibility()); @@ -750,7 +750,7 @@ } obj = check(lto::InputFile::create(MemoryBufferRef( - mb.getBuffer(), saver.save(archiveName + mb.getBufferIdentifier())))); + mb.getBuffer(), saver().save(archiveName + mb.getBufferIdentifier())))); Triple t(obj->getTargetTriple()); if (!t.isWasm()) { error(toString(this) + ": machine type must be wasm32 or wasm64"); diff --git a/lld/wasm/LTO.cpp b/lld/wasm/LTO.cpp --- a/lld/wasm/LTO.cpp +++ b/lld/wasm/LTO.cpp @@ -135,12 +135,14 @@ files[task] = std::move(mb); })); + CommonLinkingContext &ctx = context(); + checkError(ltoObj->run( [&](size_t task) { return std::make_unique( std::make_unique(buf[task])); }, - cache)); + cache, [&ctx] { attachContext(ctx); })); if (!config->thinLTOCacheDir.empty()) pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy); diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -825,7 +825,7 @@ void SymbolTable::replaceWithUndefined(Symbol *sym) { // Add a synthetic dummy for weak undefined functions. These dummies will // be GC'd if not used as the target of any "call" instructions. - StringRef debugName = saver.save("undefined_weak:" + toString(*sym)); + StringRef debugName = saver().save("undefined_weak:" + toString(*sym)); replaceWithUnreachable(sym, *sym->getSignature(), debugName); // Hide our dummy to prevent export. sym->setHidden(true); @@ -923,7 +923,8 @@ if (symbol != defined) { auto *f = cast(symbol); reportFunctionSignatureMismatch(symName, f, defined, false); - StringRef debugName = saver.save("signature_mismatch:" + toString(*f)); + StringRef debugName = + saver().save("signature_mismatch:" + toString(*f)); replaceWithUnreachable(f, *f->signature, debugName); } } diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -185,7 +185,7 @@ else if (sec->type == WASM_SEC_CODE) name = "reloc.CODE"; else if (sec->type == WASM_SEC_CUSTOM) - name = saver.save("reloc." + sec->name); + name = saver().save("reloc." + sec->name); else llvm_unreachable( "relocations only supported for code, data, or custom sections"); @@ -384,8 +384,8 @@ LLVM_DEBUG(dbgs() << "addStartStopSymbols: " << name << "\n"); uint64_t start = seg->startVA; uint64_t stop = start + seg->size; - symtab->addOptionalDataSymbol(saver.save("__start_" + name), start); - symtab->addOptionalDataSymbol(saver.save("__stop_" + name), stop); + symtab->addOptionalDataSymbol(saver().save("__start_" + name), start); + symtab->addOptionalDataSymbol(saver().save("__stop_" + name), stop); } void Writer::addSections() { @@ -945,7 +945,7 @@ writeUleb128(os, bodyContent.size(), "function size"); os << bodyContent; } - ArrayRef body = arrayRefFromStringRef(saver.save(functionBody)); + ArrayRef body = arrayRefFromStringRef(saver().save(functionBody)); cast(func->function)->setBody(body); } diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h --- a/llvm/include/llvm/LTO/LTO.h +++ b/llvm/include/llvm/LTO/LTO.h @@ -220,13 +220,18 @@ using NativeObjectCache = std::function; +/// This lets callers insert a behavior whenever a LTO thread starts. This is +/// typically used to bind thread_local pointers to an application-wide global +/// state that lives on the stack. +using ThreadInitFn = std::function; + /// A ThinBackend defines what happens after the thin-link phase during ThinLTO. /// The details of this type definition aren't important; clients can only /// create a ThinBackend using one of the create*ThinBackend() functions below. using ThinBackend = std::function( const Config &C, ModuleSummaryIndex &CombinedIndex, StringMap &ModuleToDefinedGVSummaries, - AddStreamFn AddStream, NativeObjectCache Cache)>; + AddStreamFn AddStream, NativeObjectCache Cache, ThreadInitFn ThreadInit)>; /// This ThinBackend runs the individual backend jobs in-process. /// The default value means to use one job per hardware core (not hyper-thread). @@ -299,7 +304,8 @@ /// /// The client will receive at most one callback (via either AddStream or /// Cache) for each task identifier. - Error run(AddStreamFn AddStream, NativeObjectCache Cache = nullptr); + Error run(AddStreamFn AddStream, NativeObjectCache Cache = nullptr, + ThreadInitFn ThreadInit = nullptr); /// Static method that returns a list of libcall symbols that can be generated /// by LTO but might not be visible from bitcode symbol table. @@ -432,6 +438,7 @@ Error runRegularLTO(AddStreamFn AddStream); Error runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache, + ThreadInitFn ThreadInit, const DenseSet &GUIDPreservedSymbols); Error checkPartiallySplit(); diff --git a/llvm/include/llvm/Support/Parallel.h b/llvm/include/llvm/Support/Parallel.h --- a/llvm/include/llvm/Support/Parallel.h +++ b/llvm/include/llvm/Support/Parallel.h @@ -69,6 +69,10 @@ void spawn(std::function f); + // Provide a hook to execute behavior before & after the spawned functions. + // This is needed to setup thread_local state. + static void wrapThreadState(std::function &)> f); + void sync() const { L.sync(); } }; diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -993,7 +993,8 @@ return Error::success(); } -Error LTO::run(AddStreamFn AddStream, NativeObjectCache Cache) { +Error LTO::run(AddStreamFn AddStream, NativeObjectCache Cache, + ThreadInitFn ThreadInit) { // Compute "dead" symbols, we don't want to import/export these! DenseSet GUIDPreservedSymbols; DenseMap GUIDPrevailingResolutions; @@ -1033,7 +1034,7 @@ Error Result = runRegularLTO(AddStream); if (!Result) - Result = runThinLTO(AddStream, Cache, GUIDPreservedSymbols); + Result = runThinLTO(AddStream, Cache, ThreadInit, GUIDPreservedSymbols); if (StatsFile) PrintStatisticsJSON(StatsFile->os()); @@ -1179,6 +1180,7 @@ ThreadPool BackendThreadPool; AddStreamFn AddStream; NativeObjectCache Cache; + ThreadInitFn ThreadInit; std::set CfiFunctionDefs; std::set CfiFunctionDecls; @@ -1190,10 +1192,10 @@ const Config &Conf, ModuleSummaryIndex &CombinedIndex, ThreadPoolStrategy ThinLTOParallelism, const StringMap &ModuleToDefinedGVSummaries, - AddStreamFn AddStream, NativeObjectCache Cache) + AddStreamFn AddStream, NativeObjectCache Cache, ThreadInitFn ThreadInit) : ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries), BackendThreadPool(ThinLTOParallelism), AddStream(std::move(AddStream)), - Cache(std::move(Cache)) { + Cache(std::move(Cache)), ThreadInit(std::move(ThreadInit)) { for (auto &Name : CombinedIndex.cfiFunctionDefs()) CfiFunctionDefs.insert( GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name))); @@ -1203,13 +1205,18 @@ } Error runThinLTOBackendThread( - AddStreamFn AddStream, NativeObjectCache Cache, unsigned Task, - BitcodeModule BM, ModuleSummaryIndex &CombinedIndex, + AddStreamFn AddStream, NativeObjectCache Cache, ThreadInitFn ThreadInit, + unsigned Task, BitcodeModule BM, ModuleSummaryIndex &CombinedIndex, const FunctionImporter::ImportMapTy &ImportList, const FunctionImporter::ExportSetTy &ExportList, const std::map &ResolvedODR, const GVSummaryMapTy &DefinedGlobals, MapVector &ModuleMap) { + + // Ensure the client thread init function is called before anything else. + if (ThreadInit) + ThreadInit(); + auto RunThinBackend = [&](AddStreamFn AddStream) { LTOLLVMContext BackendContext(Conf); Expected> MOrErr = BM.parseModule(BackendContext); @@ -1262,8 +1269,8 @@ timeTraceProfilerInitialize(Conf.TimeTraceGranularity, "thin backend"); Error E = runThinLTOBackendThread( - AddStream, Cache, Task, BM, CombinedIndex, ImportList, ExportList, - ResolvedODR, DefinedGlobals, ModuleMap); + AddStream, Cache, ThreadInit, Task, BM, CombinedIndex, ImportList, + ExportList, ResolvedODR, DefinedGlobals, ModuleMap); if (E) { std::unique_lock L(ErrMu); if (Err) @@ -1296,10 +1303,11 @@ ThinBackend lto::createInProcessThinBackend(ThreadPoolStrategy Parallelism) { return [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, const StringMap &ModuleToDefinedGVSummaries, - AddStreamFn AddStream, NativeObjectCache Cache) { + AddStreamFn AddStream, NativeObjectCache Cache, + ThreadInitFn ThreadInit) { return std::make_unique( Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, AddStream, - Cache); + Cache, ThreadInit); }; } @@ -1390,7 +1398,8 @@ raw_fd_ostream *LinkedObjectsFile, IndexWriteCallback OnWrite) { return [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, const StringMap &ModuleToDefinedGVSummaries, - AddStreamFn AddStream, NativeObjectCache Cache) { + AddStreamFn AddStream, NativeObjectCache Cache, + ThreadInitFn ThreadInit) { return std::make_unique( Conf, CombinedIndex, ModuleToDefinedGVSummaries, OldPrefix, NewPrefix, ShouldEmitImportsFiles, LinkedObjectsFile, OnWrite); @@ -1398,6 +1407,7 @@ } Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache, + ThreadInitFn ThreadInit, const DenseSet &GUIDPreservedSymbols) { timeTraceProfilerBegin("ThinLink", StringRef("")); auto TimeTraceScopeExit = llvm::make_scope_exit([]() { @@ -1525,7 +1535,7 @@ std::unique_ptr BackendProc = ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, - AddStream, Cache); + AddStream, Cache, ThreadInit); auto &ModuleMap = ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap; diff --git a/llvm/lib/Support/Parallel.cpp b/llvm/lib/Support/Parallel.cpp --- a/llvm/lib/Support/Parallel.cpp +++ b/llvm/lib/Support/Parallel.cpp @@ -27,18 +27,8 @@ namespace { -/// An abstract class that takes closures and runs them asynchronously. -class Executor { -public: - virtual ~Executor() = default; - virtual void add(std::function func) = 0; - - static Executor *getDefaultExecutor(); -}; - -/// An implementation of an Executor that runs closures on a thread pool -/// in filo order. -class ThreadPoolExecutor : public Executor { +/// Takes closures and runs them asynchronously on a thread pool in FILO order. +class ThreadPoolExecutor final { public: explicit ThreadPoolExecutor(ThreadPoolStrategy S = hardware_concurrency()) { unsigned ThreadCount = S.compute_thread_count(); @@ -69,7 +59,7 @@ ThreadsCreated.get_future().wait(); } - ~ThreadPoolExecutor() override { + ~ThreadPoolExecutor() { stop(); std::thread::id CurrentThreadId = std::this_thread::get_id(); for (std::thread &T : Threads) @@ -86,7 +76,7 @@ static void call(void *Ptr) { ((ThreadPoolExecutor *)Ptr)->stop(); } }; - void add(std::function F) override { + void add(std::function F) { { std::lock_guard Lock(Mutex); WorkStack.push(F); @@ -94,6 +84,8 @@ Cond.notify_one(); } + std::function &)> ThreadStateFn; + private: void work(ThreadPoolStrategy S, unsigned ThreadID) { S.apply_thread_strategy(ThreadID); @@ -105,7 +97,7 @@ auto Task = WorkStack.top(); WorkStack.pop(); Lock.unlock(); - Task(); + ThreadStateFn ? ThreadStateFn(Task) : Task(); } } @@ -117,7 +109,7 @@ std::vector Threads; }; -Executor *Executor::getDefaultExecutor() { +static ThreadPoolExecutor *getDefaultExecutor() { // The ManagedStatic enables the ThreadPoolExecutor to be stopped via // llvm_shutdown() which allows a "clean" fast exit, e.g. via _exit(). This // stops the thread pool and waits for any worker thread creation to complete @@ -156,7 +148,7 @@ void TaskGroup::spawn(std::function F) { if (Parallel) { L.inc(); - Executor::getDefaultExecutor()->add([&, F] { + getDefaultExecutor()->add([&, F] { F(); L.dec(); }); @@ -165,6 +157,11 @@ } } +void TaskGroup::wrapThreadState( + std::function &)> F) { + getDefaultExecutor()->ThreadStateFn = F; +} + } // namespace detail } // namespace parallel } // namespace llvm