4444#include " llvm/Support/FileSystem.h"
4545#include " llvm/Support/Parallel.h"
4646#include " llvm/Support/Path.h"
47+ #include " llvm/Support/Process.h"
4748#include " llvm/Support/TarWriter.h"
4849#include " llvm/Support/TargetSelect.h"
4950#include " llvm/Support/TimeProfiler.h"
@@ -282,11 +283,11 @@ static void saveThinArchiveToRepro(ArchiveFile const *file) {
282283 " : Archive::children failed: " + toString (std::move (e)));
283284}
284285
285- static InputFile *addFile (StringRef path, LoadType loadType ,
286- bool isLazy = false , bool isExplicit = true ,
287- bool isBundleLoader = false ,
288- bool isForceHidden = false ) {
289- std::optional<MemoryBufferRef> buffer = readFile (path);
286+ static InputFile *deferredAddFile (std::optional<MemoryBufferRef> buffer ,
287+ StringRef path, LoadType loadType ,
288+ bool isLazy = false , bool isExplicit = true ,
289+ bool isBundleLoader = false ,
290+ bool isForceHidden = false ) {
290291 if (!buffer)
291292 return nullptr ;
292293 MemoryBufferRef mbref = *buffer;
@@ -441,6 +442,14 @@ static InputFile *addFile(StringRef path, LoadType loadType,
441442 return newFile;
442443}
443444
445+ static InputFile *addFile (StringRef path, LoadType loadType,
446+ bool isLazy = false , bool isExplicit = true ,
447+ bool isBundleLoader = false ,
448+ bool isForceHidden = false ) {
449+ return deferredAddFile (readFile (path), path, loadType, isLazy, isExplicit,
450+ isBundleLoader, isForceHidden);
451+ }
452+
444453static std::vector<StringRef> missingAutolinkWarnings;
445454static void addLibrary (StringRef name, bool isNeeded, bool isWeak,
446455 bool isReexport, bool isHidden, bool isExplicit,
@@ -564,13 +573,23 @@ void macho::resolveLCLinkerOptions() {
564573 }
565574}
566575
567- static void addFileList (StringRef path, bool isLazy) {
576+ typedef struct {
577+ StringRef path;
578+ std::optional<MemoryBufferRef> buffer;
579+ } DeferredFile;
580+
581+ static void addFileList (StringRef path, bool isLazy,
582+ std::vector<DeferredFile> &deferredFiles) {
568583 std::optional<MemoryBufferRef> buffer = readFile (path);
569584 if (!buffer)
570585 return ;
571586 MemoryBufferRef mbref = *buffer;
572587 for (StringRef path : args::getLines (mbref))
573- addFile (rerootPath (path), LoadType::CommandLine, isLazy);
588+ if (config->readThreads ) {
589+ StringRef rrpath = rerootPath (path);
590+ deferredFiles.push_back ({rrpath, readFile (rrpath)});
591+ } else
592+ addFile (rerootPath (path), LoadType::CommandLine, isLazy);
574593}
575594
576595// We expect sub-library names of the form "libfoo", which will match a dylib
@@ -1215,20 +1234,80 @@ static void handleSymbolPatterns(InputArgList &args,
12151234 parseSymbolPatternsFile (arg, symbolPatterns);
12161235}
12171236
1218- static void createFiles (const InputArgList &args) {
1237+ // Most input files have been mapped but not yet paged in.
1238+ // This code forces the page-ins on multiple threads so
1239+ // the process is not stalled waiting on disk buffer i/o.
1240+ void multiThreadedPageIn (std::vector<DeferredFile> &deferred, int nthreads) {
1241+ #ifndef _WIN32
1242+ typedef struct {
1243+ std::vector<DeferredFile> &deferred;
1244+ size_t counter, total, pageSize;
1245+ pthread_mutex_t mutex;
1246+ } PageInState;
1247+ PageInState state = {deferred, 0 , 0 ,
1248+ llvm::sys::Process::getPageSizeEstimate (),
1249+ pthread_mutex_t ()};
1250+ pthread_mutex_init (&state.mutex , NULL );
1251+
1252+ pthread_t running[200 ];
1253+ int maxthreads = sizeof running / sizeof running[0 ];
1254+ if (nthreads > maxthreads)
1255+ nthreads = maxthreads;
1256+
1257+ for (int t = 0 ; t < nthreads; t++)
1258+ pthread_create (
1259+ &running[t], nullptr ,
1260+ [](void *ptr) -> void * {
1261+ PageInState &state = *(PageInState *)ptr;
1262+ static int total = 0 ;
1263+ while (true ) {
1264+ pthread_mutex_lock (&state.mutex );
1265+ if (state.counter >= state.deferred .size ()) {
1266+ pthread_mutex_unlock (&state.mutex );
1267+ return nullptr ;
1268+ }
1269+ DeferredFile &add = state.deferred [state.counter ];
1270+ state.counter += 1 ;
1271+ pthread_mutex_unlock (&state.mutex );
1272+
1273+ int t = 0 ; // Reference each page to load it into memory.
1274+ for (const char *page = add.buffer ->getBuffer ().data (),
1275+ *end = page + add.buffer ->getBuffer ().size ();
1276+ page < end; page += state.pageSize )
1277+ t += *page;
1278+ state.total += t; // Avoids whole section being optimised out.
1279+ }
1280+ },
1281+ &state);
1282+
1283+ for (int t = 0 ; t < nthreads; t++)
1284+ pthread_join (running[t], nullptr );
1285+
1286+ pthread_mutex_destroy (&state.mutex );
1287+ #endif
1288+ }
1289+
1290+ void createFiles (const InputArgList &args) {
12191291 TimeTraceScope timeScope (" Load input files" );
12201292 // This loop should be reserved for options whose exact ordering matters.
12211293 // Other options should be handled via filtered() and/or getLastArg().
12221294 bool isLazy = false ;
12231295 // If we've processed an opening --start-lib, without a matching --end-lib
12241296 bool inLib = false ;
1297+ std::vector<DeferredFile> deferredFiles;
1298+
12251299 for (const Arg *arg : args) {
12261300 const Option &opt = arg->getOption ();
12271301 warnIfDeprecatedOption (opt);
12281302 warnIfUnimplementedOption (opt);
12291303
12301304 switch (opt.getID ()) {
12311305 case OPT_INPUT:
1306+ if (config->readThreads ) {
1307+ StringRef rrpath = rerootPath (arg->getValue ());
1308+ deferredFiles.push_back ({rrpath, readFile (rrpath)});
1309+ break ;
1310+ }
12321311 addFile (rerootPath (arg->getValue ()), LoadType::CommandLine, isLazy);
12331312 break ;
12341313 case OPT_needed_library:
@@ -1249,7 +1328,7 @@ static void createFiles(const InputArgList &args) {
12491328 dylibFile->forceWeakImport = true ;
12501329 break ;
12511330 case OPT_filelist:
1252- addFileList (arg->getValue (), isLazy);
1331+ addFileList (arg->getValue (), isLazy, deferredFiles );
12531332 break ;
12541333 case OPT_force_load:
12551334 addFile (rerootPath (arg->getValue ()), LoadType::CommandLineForce);
@@ -1295,6 +1374,12 @@ static void createFiles(const InputArgList &args) {
12951374 break ;
12961375 }
12971376 }
1377+
1378+ if (config->readThreads ) {
1379+ multiThreadedPageIn (deferredFiles, config->readThreads );
1380+ for (auto &add : deferredFiles)
1381+ deferredAddFile (add.buffer , add.path , LoadType::CommandLine, isLazy);
1382+ }
12981383}
12991384
13001385static void gatherInputSections () {
@@ -1687,6 +1772,14 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
16871772 }
16881773 }
16891774
1775+ if (auto *arg = args.getLastArg (OPT_read_threads)) {
1776+ StringRef v (arg->getValue ());
1777+ unsigned threads = 0 ;
1778+ if (!llvm::to_integer (v, threads, 0 ) || threads < 0 )
1779+ error (arg->getSpelling () + " : expected a positive integer, but got '" +
1780+ arg->getValue () + " '" );
1781+ config->readThreads = threads;
1782+ }
16901783 if (auto *arg = args.getLastArg (OPT_threads_eq)) {
16911784 StringRef v (arg->getValue ());
16921785 unsigned threads = 0 ;
0 commit comments