Skip to content

Commit

Permalink
[lld][COFF][ELF][WebAssembly] Replace --[no-]threads /threads[:no] wi…
Browse files Browse the repository at this point in the history
…th --threads={1,2,...} /threads:{1,2,...}

--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
  • Loading branch information
MaskRay committed Mar 31, 2020
1 parent f3a7d79 commit eb4663d
Show file tree
Hide file tree
Showing 22 changed files with 144 additions and 65 deletions.
12 changes: 11 additions & 1 deletion lld/COFF/Driver.cpp
Expand Up @@ -1144,7 +1144,17 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
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;
Expand Down
6 changes: 3 additions & 3 deletions lld/COFF/Options.td
Expand Up @@ -219,9 +219,9 @@ def lto_obj_path : P<
"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">;
Expand Down
1 change: 0 additions & 1 deletion lld/Common/CMakeLists.txt
Expand Up @@ -36,7 +36,6 @@ add_lld_library(lldCommon
Reproduce.cpp
Strings.cpp
TargetOptionsCommandFlags.cpp
Threads.cpp
Timer.cpp
VCSVersion.inc
Version.cpp
Expand Down
2 changes: 1 addition & 1 deletion lld/Common/Filesystem.cpp
Expand Up @@ -43,7 +43,7 @@ void lld::unlinkAsync(StringRef path) {
#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;

Expand Down
11 changes: 0 additions & 11 deletions lld/Common/Threads.cpp

This file was deleted.

16 changes: 14 additions & 2 deletions lld/ELF/Driver.cpp
Expand Up @@ -860,7 +860,6 @@ static void readConfigs(opt::InputArgList &args) {
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,
Expand Down Expand Up @@ -974,7 +973,6 @@ static void readConfigs(opt::InputArgList &args) {
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 =
Expand Down Expand Up @@ -1036,6 +1034,20 @@ static void readConfigs(opt::InputArgList &args) {
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)
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/ICF.cpp
Expand Up @@ -400,7 +400,7 @@ template <class ELFT>
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;
Expand Down
10 changes: 6 additions & 4 deletions lld/ELF/Options.td
Expand Up @@ -350,9 +350,10 @@ defm target2:
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">;
Expand Down Expand Up @@ -509,7 +510,8 @@ defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the Th
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=">;

Expand Down
20 changes: 9 additions & 11 deletions lld/ELF/SyntheticSections.cpp
Expand Up @@ -2746,12 +2746,11 @@ createSymbols(ArrayRef<std::vector<GdbIndexSection::NameAttrEntry>> nameAttrs,
// 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);
Expand Down Expand Up @@ -3194,11 +3193,10 @@ void MergeNoTailSection::finalizeContents() {

// 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) {
Expand Down
11 changes: 6 additions & 5 deletions lld/docs/ld.lld.1
Expand Up @@ -302,8 +302,6 @@ Page align sections.
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
Expand Down Expand Up @@ -525,9 +523,12 @@ Path to ThinLTO cached object file directory.
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
Expand Down
8 changes: 3 additions & 5 deletions lld/include/lld/Common/Threads.h
Expand Up @@ -63,25 +63,23 @@

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);
Expand Down
4 changes: 2 additions & 2 deletions lld/test/COFF/pdb-globals.test
Expand Up @@ -2,9 +2,9 @@ RUN: yaml2obj %S/Inputs/pdb-globals.yaml > %t.obj
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
Expand Down
18 changes: 9 additions & 9 deletions lld/test/ELF/build-id.s
Expand Up @@ -5,26 +5,26 @@
# 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
Expand Down
12 changes: 12 additions & 0 deletions lld/test/ELF/lto/thinlto.ll
Expand Up @@ -22,6 +22,18 @@
; 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
Expand Down
11 changes: 11 additions & 0 deletions lld/test/ELF/threads.s
@@ -0,0 +1,11 @@
# 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]]'
12 changes: 12 additions & 0 deletions lld/test/wasm/lto/thinlto.ll
Expand Up @@ -14,6 +14,18 @@
; 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
Expand Down
12 changes: 12 additions & 0 deletions lld/test/wasm/threads.s
@@ -0,0 +1,12 @@
# 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]]'
16 changes: 14 additions & 2 deletions lld/wasm/Driver.cpp
Expand Up @@ -362,10 +362,8 @@ static void readConfigs(opt::InputArgList &args) {
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);
Expand All @@ -377,6 +375,20 @@ static void readConfigs(opt::InputArgList &args) {
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>());
Expand Down
10 changes: 5 additions & 5 deletions lld/wasm/Options.td
Expand Up @@ -64,9 +64,6 @@ def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,

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">;

Expand Down Expand Up @@ -98,7 +95,9 @@ def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;

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">;

Expand Down Expand Up @@ -198,4 +197,5 @@ def save_temps: F<"save-temps">;
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=">;

0 comments on commit eb4663d

Please sign in to comment.