--no-threads is a name copied from gold.
gold has --no-thread, --thread-count and several other --thread-count-*.
There are needs to customize the number of threads (running several lld
processes concurrently or customizing the number of LTO threads).
Having a single --threads=N is a straightforward replacement of gold's
--no-threads + --thread-count.
--no-threads is used rarely. So just delete --no-threads instead of
keeping it for compatibility for a while.
If --threads= is specified (ELF,wasm; COFF /threads: is similar),
--thinlto-jobs= defaults to --threads=,
otherwise all available hardware threads are used.
There is currently no way to override a --threads={1,2,...}. It is still
a debate whether we should use --threads=all.
Reviewed By: rnk, aganea
Differential Revision: https://reviews.llvm.org/D76885
return;
}
- lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true);
+ // /threads: takes a positive integer and provides the default value for
+ // /opt:lldltojobs=.
+ if (auto *arg = args.getLastArg(OPT_threads)) {
+ StringRef v(arg->getValue());
+ unsigned threads = 0;
+ if (!llvm::to_integer(v, threads, 0) || threads == 0)
+ error(arg->getSpelling() + ": expected a positive integer, but got '" +
+ arg->getValue() + "'");
+ parallel::strategy = hardware_concurrency(threads);
+ config->thinLTOJobs = v.str();
+ }
if (args.hasArg(OPT_show_timing))
config->showTiming = true;
"output native object for merged LTO unit to this path">;
def dash_dash_version : Flag<["--"], "version">,
HelpText<"Print version information">;
-defm threads: B<"threads",
- "Run the linker multi-threaded (default)",
- "Do not run the linker multi-threaded">;
+def threads
+ : P<"threads", "Number of threads. '1' disables multi-threading. By "
+ "default all available hardware threads are used">;
// Flags for debugging
def lldmap : F<"lldmap">;
Reproduce.cpp
Strings.cpp
TargetOptionsCommandFlags.cpp
- Threads.cpp
Timer.cpp
VCSVersion.inc
Version.cpp
#if defined(_WIN32)
sys::fs::remove(path);
#else
- if (!threadsEnabled || !sys::fs::exists(path) ||
+ if (parallel::strategy.ThreadsRequested == 1 || !sys::fs::exists(path) ||
!sys::fs::is_regular_file(path))
return;
+++ /dev/null
-//===- Threads.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/Threads.h"
-
-bool lld::threadsEnabled = true;
args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
errorHandler().vsDiagnostics =
args.hasArg(OPT_visual_studio_diagnostics_format, false);
- threadsEnabled = args.hasFlag(OPT_threads, OPT_no_threads, true);
config->allowMultipleDefinition =
args.hasFlag(OPT_allow_multiple_definition,
config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
args.hasArg(OPT_thinlto_index_only_eq);
config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
- config->thinLTOJobs = args.getLastArgValue(OPT_thinlto_jobs);
config->thinLTOObjectSuffixReplace =
getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq);
config->thinLTOPrefixReplace =
for (auto *arg : args.filtered(OPT_mllvm))
parseClangOption(arg->getValue(), arg->getSpelling());
+ // --threads= takes a positive integer and provides the default value for
+ // --thinlto-jobs=.
+ if (auto *arg = args.getLastArg(OPT_threads)) {
+ StringRef v(arg->getValue());
+ unsigned threads = 0;
+ if (!llvm::to_integer(v, threads, 0) || threads == 0)
+ error(arg->getSpelling() + ": expected a positive integer, but got '" +
+ arg->getValue() + "'");
+ parallel::strategy = hardware_concurrency(threads);
+ config->thinLTOJobs = v;
+ }
+ if (auto *arg = args.getLastArg(OPT_thinlto_jobs))
+ config->thinLTOJobs = arg->getValue();
+
if (config->ltoo > 3)
error("invalid optimization level for LTO: " + Twine(config->ltoo));
if (config->ltoPartitions == 0)
void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> fn) {
// If threading is disabled or the number of sections are
// too small to use threading, call Fn sequentially.
- if (!threadsEnabled || sections.size() < 1024) {
+ if (parallel::strategy.ThreadsRequested == 1 || sections.size() < 1024) {
forEachClassRange(0, sections.size(), fn);
++cnt;
return;
Eq<"target2", "Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">,
MetaVarName<"<type>">;
-defm threads: B<"threads",
- "Run the linker multi-threaded (default)",
- "Do not run the linker multi-threaded">;
+defm threads
+ : Eq<"threads",
+ "Number of threads. '1' disables multi-threading. By default all "
+ "available hardware threads are used">;
def time_trace: F<"time-trace">, HelpText<"Record time trace">;
def time_trace_file_eq: J<"time-trace-file=">, HelpText<"Specify time trace output file">;
def thinlto_emit_imports_files: F<"thinlto-emit-imports-files">;
def thinlto_index_only: F<"thinlto-index-only">;
def thinlto_index_only_eq: J<"thinlto-index-only=">;
-def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
+def thinlto_jobs: J<"thinlto-jobs=">,
+ HelpText<"Number of ThinLTO jobs. Default to --threads=">;
def thinlto_object_suffix_replace_eq: J<"thinlto-object-suffix-replace=">;
def thinlto_prefix_replace_eq: J<"thinlto-prefix-replace=">;
// The number of symbols we will handle in this function is of the order
// of millions for very large executables, so we use multi-threading to
// speed it up.
- size_t numShards = 32;
- size_t concurrency = 1;
- if (threadsEnabled)
- concurrency = std::min<size_t>(
- PowerOf2Floor(hardware_concurrency().compute_thread_count()),
- numShards);
+ constexpr size_t numShards = 32;
+ size_t concurrency = PowerOf2Floor(
+ std::min<size_t>(hardware_concurrency(parallel::strategy.ThreadsRequested)
+ .compute_thread_count(),
+ numShards));
// A sharded map to uniquify symbols by name.
std::vector<DenseMap<CachedHashStringRef, size_t>> map(numShards);
// Concurrency level. Must be a power of 2 to avoid expensive modulo
// operations in the following tight loop.
- size_t concurrency = 1;
- if (threadsEnabled)
- concurrency = std::min<size_t>(
- PowerOf2Floor(hardware_concurrency().compute_thread_count()),
- numShards);
+ size_t concurrency = PowerOf2Floor(
+ std::min<size_t>(hardware_concurrency(parallel::strategy.ThreadsRequested)
+ .compute_thread_count(),
+ numShards));
// Add section pieces to the builders.
parallelForEachN(0, concurrency, [&](size_t threadId) {
Do not set the text data sections to be writable, page align sections.
.It Fl -no-rosegment
Do not put read-only non-executable sections in their own segment.
-.It Fl -no-threads
-Do not run the linker multi-threaded.
.It Fl -no-undefined-version
Report version scripts that refer undefined symbols.
.It Fl -no-undefined
Pruning policy for the ThinLTO cache.
.It Fl -thinlto-jobs Ns = Ns Ar value
Number of ThinLTO jobs.
-.It Fl -threads
-Run the linker multi-threaded.
-This option is enabled by default.
+.It Fl -threads Ns = Ns Ar N
+Number of threads.
+.Cm all
+(default) means all of concurrent threads supported.
+.Cm 1
+disables multi-threading.
.It Fl -trace
Print the names of the input files.
.It Fl -trace-symbol Ns = Ns Ar symbol , Fl y Ar symbol
namespace lld {
-extern bool threadsEnabled;
-
template <typename R, class FuncTy> void parallelForEach(R &&range, FuncTy fn) {
- if (threadsEnabled)
+ if (llvm::parallel::strategy.ThreadsRequested != 1)
for_each(llvm::parallel::par, std::begin(range), std::end(range), fn);
else
for_each(llvm::parallel::seq, std::begin(range), std::end(range), fn);
inline void parallelForEachN(size_t begin, size_t end,
llvm::function_ref<void(size_t)> fn) {
- if (threadsEnabled)
+ if (llvm::parallel::strategy.ThreadsRequested != 1)
for_each_n(llvm::parallel::par, begin, end, fn);
else
for_each_n(llvm::parallel::seq, begin, end, fn);
}
template <typename R, class FuncTy> void parallelSort(R &&range, FuncTy fn) {
- if (threadsEnabled)
+ if (llvm::parallel::strategy.ThreadsRequested != 1)
sort(llvm::parallel::par, std::begin(range), std::end(range), fn);
else
sort(llvm::parallel::seq, std::begin(range), std::end(range), fn);
RUN: lld-link /debug /nodefaultlib /entry:main /out:%t.exe /pdb:%t.pdb %t.obj
RUN: llvm-pdbutil dump -symbols -globals %t.pdb | FileCheck %s
-RUN: lld-link /debug /nodefaultlib /entry:main /out:%t.exe /pdb:%t.pdb %t.obj /threads
+RUN: lld-link /debug /nodefaultlib /entry:main /out:%t.exe /pdb:%t.pdb %t.obj /threads:1
RUN: llvm-pdbutil dump -symbols -globals %t.pdb | FileCheck %s
-RUN: lld-link /debug /nodefaultlib /entry:main /out:%t.exe /pdb:%t.pdb %t.obj /threads:no
+RUN: lld-link /debug /nodefaultlib /entry:main /out:%t.exe /pdb:%t.pdb %t.obj /threads:2
RUN: llvm-pdbutil dump -symbols -globals %t.pdb | FileCheck %s
# Test that we correctly distribute symbols between the globals and module
# RUN: ld.lld --build-id %t -o %t2
# RUN: llvm-readobj -S %t2 | FileCheck -check-prefix=ALIGN %s
-# RUN: ld.lld --build-id %t -o %t2 -threads
+# RUN: ld.lld --build-id %t -o %t2
# RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=DEFAULT %s
-# RUN: ld.lld --build-id=fast %t -o %t2 -threads
+# RUN: ld.lld --build-id=fast %t -o %t2
# RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=DEFAULT %s
-# RUN: ld.lld --build-id %t -o %t2 -no-threads
+# RUN: ld.lld --build-id %t -o %t2 --threads=1
# RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=DEFAULT %s
-# RUN: ld.lld --build-id=md5 %t -o %t2 -threads
+# RUN: ld.lld --build-id=md5 %t -o %t2
# RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=MD5 %s
-# RUN: ld.lld --build-id=md5 %t -o %t2 -no-threads
+# RUN: ld.lld --build-id=md5 %t -o %t2 --threads=1
# RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=MD5 %s
-# RUN: ld.lld --build-id=sha1 %t -o %t2 -threads
+# RUN: ld.lld --build-id=sha1 %t -o %t2
# RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=SHA1 %s
-# RUN: ld.lld --build-id=sha1 %t -o %t2 -no-threads
+# RUN: ld.lld --build-id=sha1 %t -o %t2 --threads=1
# RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=SHA1 %s
-# RUN: ld.lld --build-id=tree %t -o %t2 -threads
+# RUN: ld.lld --build-id=tree %t -o %t2
# RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=SHA1 %s
-# RUN: ld.lld --build-id=tree %t -o %t2 -no-threads
+# RUN: ld.lld --build-id=tree %t -o %t2 --threads=1
# RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=SHA1 %s
# RUN: ld.lld --build-id=uuid %t -o %t2
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
+;; --thinlto-jobs= defaults to --threads=.
+; RUN: rm -f %t31.lto.o %t32.lto.o
+; RUN: ld.lld -save-temps --threads=2 -shared %t1.o %t2.o -o %t3
+; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
+; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
+
+;; --thinlto-jobs= overrides --threads=.
+; RUN: rm -f %t31.lto.o %t32.lto.o
+; RUN: ld.lld -save-temps --threads=1 --plugin-opt=jobs=2 -shared %t1.o %t2.o -o %t3
+; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
+; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
+
; Test with all threads, on all cores, on all CPU sockets
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: ld.lld -save-temps --thinlto-jobs=all -shared %t1.o %t2.o -o %t3
--- /dev/null
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+
+## A positive integer is allowed.
+# RUN: ld.lld --threads=1 %t.o -o /dev/null
+# RUN: ld.lld --threads=2 %t.o -o /dev/null
+
+# RUN: not ld.lld --threads=all %t.o -o /dev/null 2>&1 | FileCheck %s -DN=all
+# RUN: not ld.lld --threads=0 %t.o -o /dev/null 2>&1 | FileCheck %s -DN=0
+# RUN: not ld.lld --threads=-1 %t.o -o /dev/null 2>&1 | FileCheck %s -DN=-1
+
+# CHECK: error: --threads: expected a positive integer, but got '[[N]]'
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
+;; --thinlto-jobs= defaults to --threads=.
+; RUN: rm -f %t31.lto.o %t32.lto.o
+; RUN: wasm-ld -r -save-temps --threads=2 %t1.o %t2.o -o %t3
+; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
+; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
+
+;; --thinlto-jobs= overrides --threads=.
+; RUN: rm -f %t31.lto.o %t32.lto.o
+; RUN: wasm-ld -r -save-temps --threads=1 --thinlto-jobs=2 %t1.o %t2.o -o %t3
+; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
+; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
+
; Test with all threads, on all cores, on all CPU sockets
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=all %t1.o %t2.o -o %t3
--- /dev/null
+# RUN: llvm-mc -filetype=obj -triple=wasm32 %s -o %t.o
+
+## A positive integer is allowed.
+# RUN: wasm-ld --no-entry %t.o -o /dev/null
+# RUN: wasm-ld --no-entry --threads=1 %t.o -o /dev/null
+# RUN: wasm-ld --no-entry --threads=2 %t.o -o /dev/null
+
+# RUN: not wasm-ld --threads=all %t.o -o /dev/null 2>&1 | FileCheck %s -DN=all
+# RUN: not wasm-ld --threads=0 %t.o -o /dev/null 2>&1 | FileCheck %s -DN=0
+# RUN: not wasm-ld --threads=-1 %t.o -o /dev/null 2>&1 | FileCheck %s -DN=-1
+
+# CHECK: error: --threads: expected a positive integer, but got '[[N]]'
config->thinLTOCachePolicy = CHECK(
parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy");
- config->thinLTOJobs = args.getLastArgValue(OPT_thinlto_jobs);
errorHandler().verbose = args.hasArg(OPT_verbose);
LLVM_DEBUG(errorHandler().verbose = true);
- threadsEnabled = args.hasFlag(OPT_threads, OPT_no_threads, true);
config->initialMemory = args::getInteger(args, OPT_initial_memory, 0);
config->globalBase = args::getInteger(args, OPT_global_base, 1024);
config->exportDynamic =
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, config->shared);
+ // --threads= takes a positive integer and provides the default value for
+ // --thinlto-jobs=.
+ if (auto *arg = args.getLastArg(OPT_threads)) {
+ StringRef v(arg->getValue());
+ unsigned threads = 0;
+ if (!llvm::to_integer(v, threads, 0) || threads == 0)
+ error(arg->getSpelling() + ": expected a positive integer, but got '" +
+ arg->getValue() + "'");
+ parallel::strategy = hardware_concurrency(threads);
+ config->thinLTOJobs = v;
+ }
+ if (auto *arg = args.getLastArg(OPT_thinlto_jobs))
+ config->thinLTOJobs = arg->getValue();
+
if (auto *arg = args.getLastArg(OPT_features)) {
config->features =
llvm::Optional<std::vector<std::string>>(std::vector<std::string>());
def mllvm: S<"mllvm">, HelpText<"Options to pass to LLVM">;
-def no_threads: F<"no-threads">,
- HelpText<"Do not run the linker multi-threaded">;
-
def no_color_diagnostics: F<"no-color-diagnostics">,
HelpText<"Do not use colors in diagnostics">;
def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
-def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
+defm threads
+ : Eq<"threads", "Number of threads. '1' disables multi-threading. By "
+ "default all available hardware threads are used">;
def trace: F<"trace">, HelpText<"Print the names of the input files">;
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
HelpText<"Path to ThinLTO cached object file directory">;
defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
-def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
+def thinlto_jobs: J<"thinlto-jobs=">,
+ HelpText<"Number of ThinLTO jobs. Default to --threads=">;
#include "llvm/ADT/STLExtras.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Threading.h"
#include <algorithm>
#include <condition_variable>
constexpr sequential_execution_policy seq{};
constexpr parallel_execution_policy par{};
+// Strategy for the default executor used by the parallel routines provided by
+// this file. It defaults to using all hardware threads and should be
+// initialized before the first use of parallel routines.
+extern ThreadPoolStrategy strategy;
+
namespace detail {
#if LLVM_ENABLE_THREADS
#include <thread>
#include <vector>
+llvm::ThreadPoolStrategy llvm::parallel::strategy;
+
namespace llvm {
namespace parallel {
namespace detail {
T.join();
}
+ struct Creator {
+ static void *call() { return new ThreadPoolExecutor(strategy); }
+ };
struct Deleter {
static void call(void *Ptr) { ((ThreadPoolExecutor *)Ptr)->stop(); }
};
// are more frequent with the debug static runtime.
//
// This also prevents intermittent deadlocks on exit with the MinGW runtime.
- static ManagedStatic<ThreadPoolExecutor, object_creator<ThreadPoolExecutor>,
+
+ static ManagedStatic<ThreadPoolExecutor, ThreadPoolExecutor::Creator,
ThreadPoolExecutor::Deleter>
ManagedExec;
static std::unique_ptr<ThreadPoolExecutor> Exec(&(*ManagedExec));
"Reproduce.cpp",
"Strings.cpp",
"TargetOptionsCommandFlags.cpp",
- "Threads.cpp",
"Timer.cpp",
"Version.cpp",
]