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,23 @@ 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) {
568
583
std::optional<MemoryBufferRef> buffer = readFile (path);
569
584
if (!buffer)
570
585
return ;
571
586
MemoryBufferRef mbref = *buffer;
572
587
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);
574
593
}
575
594
576
595
// We expect sub-library names of the form "libfoo", which will match a dylib
@@ -1215,20 +1234,80 @@ static void handleSymbolPatterns(InputArgList &args,
1215
1234
parseSymbolPatternsFile (arg, symbolPatterns);
1216
1235
}
1217
1236
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) {
1219
1291
TimeTraceScope timeScope (" Load input files" );
1220
1292
// This loop should be reserved for options whose exact ordering matters.
1221
1293
// Other options should be handled via filtered() and/or getLastArg().
1222
1294
bool isLazy = false ;
1223
1295
// If we've processed an opening --start-lib, without a matching --end-lib
1224
1296
bool inLib = false ;
1297
+ std::vector<DeferredFile> deferredFiles;
1298
+
1225
1299
for (const Arg *arg : args) {
1226
1300
const Option &opt = arg->getOption ();
1227
1301
warnIfDeprecatedOption (opt);
1228
1302
warnIfUnimplementedOption (opt);
1229
1303
1230
1304
switch (opt.getID ()) {
1231
1305
case OPT_INPUT:
1306
+ if (config->readThreads ) {
1307
+ StringRef rrpath = rerootPath (arg->getValue ());
1308
+ deferredFiles.push_back ({rrpath, readFile (rrpath)});
1309
+ break ;
1310
+ }
1232
1311
addFile (rerootPath (arg->getValue ()), LoadType::CommandLine, isLazy);
1233
1312
break ;
1234
1313
case OPT_needed_library:
@@ -1249,7 +1328,7 @@ static void createFiles(const InputArgList &args) {
1249
1328
dylibFile->forceWeakImport = true ;
1250
1329
break ;
1251
1330
case OPT_filelist:
1252
- addFileList (arg->getValue (), isLazy);
1331
+ addFileList (arg->getValue (), isLazy, deferredFiles );
1253
1332
break ;
1254
1333
case OPT_force_load:
1255
1334
addFile (rerootPath (arg->getValue ()), LoadType::CommandLineForce);
@@ -1295,6 +1374,12 @@ static void createFiles(const InputArgList &args) {
1295
1374
break ;
1296
1375
}
1297
1376
}
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
+ }
1298
1383
}
1299
1384
1300
1385
static void gatherInputSections () {
@@ -1687,6 +1772,14 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
1687
1772
}
1688
1773
}
1689
1774
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
+ }
1690
1783
if (auto *arg = args.getLastArg (OPT_threads_eq)) {
1691
1784
StringRef v (arg->getValue ());
1692
1785
unsigned threads = 0 ;
0 commit comments