44
44
#include " llvm/Support/FileSystem.h"
45
45
#include " llvm/Support/Parallel.h"
46
46
#include " llvm/Support/Path.h"
47
+ #include " llvm/Support/Process.h"
47
48
#include " llvm/Support/TarWriter.h"
48
49
#include " llvm/Support/TargetSelect.h"
49
50
#include " llvm/Support/TimeProfiler.h"
@@ -282,11 +283,11 @@ static void saveThinArchiveToRepro(ArchiveFile const *file) {
282
283
" : Archive::children failed: " + toString (std::move (e)));
283
284
}
284
285
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 ) {
290
291
if (!buffer)
291
292
return nullptr ;
292
293
MemoryBufferRef mbref = *buffer;
@@ -441,6 +442,14 @@ static InputFile *addFile(StringRef path, LoadType loadType,
441
442
return newFile;
442
443
}
443
444
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
+
444
453
static std::vector<StringRef> missingAutolinkWarnings;
445
454
static void addLibrary (StringRef name, bool isNeeded, bool isWeak,
446
455
bool isReexport, bool isHidden, bool isExplicit,
@@ -564,13 +573,24 @@ void macho::resolveLCLinkerOptions() {
564
573
}
565
574
}
566
575
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,
583
+ int readThreads) {
568
584
std::optional<MemoryBufferRef> buffer = readFile (path);
569
585
if (!buffer)
570
586
return ;
571
587
MemoryBufferRef mbref = *buffer;
572
588
for (StringRef path : args::getLines (mbref))
573
- addFile (rerootPath (path), LoadType::CommandLine, isLazy);
589
+ if (readThreads) {
590
+ StringRef rrpath = rerootPath (path);
591
+ deferredFiles.push_back ({rrpath, readFile (rrpath)});
592
+ } else
593
+ addFile (rerootPath (path), LoadType::CommandLine, isLazy);
574
594
}
575
595
576
596
// We expect sub-library names of the form "libfoo", which will match a dylib
@@ -1215,20 +1235,81 @@ static void handleSymbolPatterns(InputArgList &args,
1215
1235
parseSymbolPatternsFile (arg, symbolPatterns);
1216
1236
}
1217
1237
1218
- static void createFiles (const InputArgList &args) {
1238
+ // Most input files have been mapped but not yet paged in.
1239
+ // This code forces the page-ins on multiple threads so
1240
+ // the process is not stalled waiting on disk buffer i/o.
1241
+ void multiThreadedPageIn (std::vector<DeferredFile> &deferred, int nthreads) {
1242
+ #ifndef _WIN32
1243
+ typedef struct {
1244
+ std::vector<DeferredFile> &deferred;
1245
+ size_t counter, total, pageSize;
1246
+ pthread_mutex_t mutex;
1247
+ } PageInState;
1248
+ PageInState state = {deferred, 0 , 0 ,
1249
+ llvm::sys::Process::getPageSizeEstimate (),
1250
+ pthread_mutex_t ()};
1251
+ pthread_mutex_init (&state.mutex , NULL );
1252
+
1253
+ pthread_t running[200 ];
1254
+ int maxthreads = sizeof running / sizeof running[0 ];
1255
+ if (nthreads > maxthreads)
1256
+ nthreads = maxthreads;
1257
+
1258
+ for (int t = 0 ; t < nthreads; t++)
1259
+ pthread_create (
1260
+ &running[t], nullptr ,
1261
+ [](void *ptr) -> void * {
1262
+ PageInState &state = *(PageInState *)ptr;
1263
+ static int total = 0 ;
1264
+ while (true ) {
1265
+ pthread_mutex_lock (&state.mutex );
1266
+ if (state.counter >= state.deferred .size ()) {
1267
+ pthread_mutex_unlock (&state.mutex );
1268
+ return nullptr ;
1269
+ }
1270
+ DeferredFile &add = state.deferred [state.counter ];
1271
+ state.counter += 1 ;
1272
+ pthread_mutex_unlock (&state.mutex );
1273
+
1274
+ int t = 0 ; // Reference each page to load it into memory.
1275
+ for (const char *start = add.buffer ->getBuffer ().data (),
1276
+ *page = start;
1277
+ page < start + add.buffer ->getBuffer ().size ();
1278
+ page += state.pageSize )
1279
+ t += *page;
1280
+ state.total += t; // Avoids whole section being optimised out.
1281
+ }
1282
+ },
1283
+ &state);
1284
+
1285
+ for (int t = 0 ; t < nthreads; t++)
1286
+ pthread_join (running[t], nullptr );
1287
+
1288
+ pthread_mutex_destroy (&state.mutex );
1289
+ #endif
1290
+ }
1291
+
1292
+ void createFiles (const InputArgList &args, int readThreads) {
1219
1293
TimeTraceScope timeScope (" Load input files" );
1220
1294
// This loop should be reserved for options whose exact ordering matters.
1221
1295
// Other options should be handled via filtered() and/or getLastArg().
1222
1296
bool isLazy = false ;
1223
1297
// If we've processed an opening --start-lib, without a matching --end-lib
1224
1298
bool inLib = false ;
1299
+ std::vector<DeferredFile> deferredFiles;
1300
+
1225
1301
for (const Arg *arg : args) {
1226
1302
const Option &opt = arg->getOption ();
1227
1303
warnIfDeprecatedOption (opt);
1228
1304
warnIfUnimplementedOption (opt);
1229
1305
1230
1306
switch (opt.getID ()) {
1231
1307
case OPT_INPUT:
1308
+ if (readThreads) {
1309
+ StringRef rrpath = rerootPath (arg->getValue ());
1310
+ deferredFiles.push_back ({rrpath, readFile (rrpath)});
1311
+ break ;
1312
+ }
1232
1313
addFile (rerootPath (arg->getValue ()), LoadType::CommandLine, isLazy);
1233
1314
break ;
1234
1315
case OPT_needed_library:
@@ -1249,7 +1330,7 @@ static void createFiles(const InputArgList &args) {
1249
1330
dylibFile->forceWeakImport = true ;
1250
1331
break ;
1251
1332
case OPT_filelist:
1252
- addFileList (arg->getValue (), isLazy);
1333
+ addFileList (arg->getValue (), isLazy, deferredFiles, readThreads );
1253
1334
break ;
1254
1335
case OPT_force_load:
1255
1336
addFile (rerootPath (arg->getValue ()), LoadType::CommandLineForce);
@@ -1295,6 +1376,12 @@ static void createFiles(const InputArgList &args) {
1295
1376
break ;
1296
1377
}
1297
1378
}
1379
+
1380
+ if (readThreads) {
1381
+ multiThreadedPageIn (deferredFiles, readThreads);
1382
+ for (auto &add : deferredFiles)
1383
+ deferredAddFile (add.buffer , add.path , LoadType::CommandLine, isLazy);
1384
+ }
1298
1385
}
1299
1386
1300
1387
static void gatherInputSections () {
@@ -1687,6 +1774,14 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
1687
1774
}
1688
1775
}
1689
1776
1777
+ if (auto *arg = args.getLastArg (OPT_read_threads)) {
1778
+ StringRef v (arg->getValue ());
1779
+ unsigned threads = 0 ;
1780
+ if (!llvm::to_integer (v, threads, 0 ) || threads < 0 )
1781
+ error (arg->getSpelling () + " : expected a positive integer, but got '" +
1782
+ arg->getValue () + " '" );
1783
+ config->readThreads = threads;
1784
+ }
1690
1785
if (auto *arg = args.getLastArg (OPT_threads_eq)) {
1691
1786
StringRef v (arg->getValue ());
1692
1787
unsigned threads = 0 ;
@@ -2107,7 +2202,7 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
2107
2202
TimeTraceScope timeScope (" ExecuteLinker" );
2108
2203
2109
2204
initLLVM (); // must be run before any call to addFile()
2110
- createFiles (args);
2205
+ createFiles (args, config-> readThreads );
2111
2206
2112
2207
// Now that all dylibs have been loaded, search for those that should be
2113
2208
// re-exported.
0 commit comments