@@ -28,6 +28,7 @@ shared bool verbose; // output verbose logging
28
28
shared bool force; // always build everything (ignores timestamp checking)
29
29
shared bool dryRun; // / dont execute targets, just print command to be executed
30
30
__gshared int jobs; // Number of jobs to run in parallel
31
+ __gshared bool usePGO = false ;
31
32
32
33
__gshared string [string ] env;
33
34
__gshared string [][string ] flags;
@@ -37,6 +38,7 @@ __gshared TaskPool taskPool;
37
38
// / Array of build rules through which all other build rules can be reached
38
39
immutable rootRules = [
39
40
&dmdDefault,
41
+ &dmdPGO,
40
42
&autoTesterBuild,
41
43
&runDmdUnittest,
42
44
&clean,
@@ -96,6 +98,7 @@ Examples
96
98
./build.d unittest # runs internal unittests
97
99
./build.d clean # remove all generated files
98
100
./build.d generated/linux/release/64/dmd.conf
101
+ ./build.d dmd-pgo # builds dmd with PGO data, currently only LDC is supported
99
102
100
103
Important variables:
101
104
--------------------
@@ -114,6 +117,7 @@ ENABLE_RELEASE: Optimized release build
114
117
ENABLE_DEBUG: Add debug instructions and symbols (set if ENABLE_RELEASE isn't set)
115
118
ENABLE_ASSERTS: Don't use -release if ENABLE_RELEASE is set
116
119
ENABLE_LTO: Enable link-time optimizations
120
+ ENABLE_PGO: Makes the default build dmd-pgo, you do not need to combine it with other optimization flags
117
121
ENABLE_UNITTEST: Build dmd with unittests (sets ENABLE_COVERAGE=1)
118
122
ENABLE_PROFILE: Build dmd with a profiling recorder (D)
119
123
ENABLE_COVERAGE Build dmd with coverage counting
@@ -163,10 +167,13 @@ Command-line parameters
163
167
if (! flags[" DFLAGS" ].canFind(" -color=off" ) &&
164
168
[env[" HOST_DMD_RUN" ], " -color=on" , " -h" ].tryRun().status == 0 )
165
169
flags[" DFLAGS" ] ~= " -color=on" ;
166
-
170
+ string defaultRule ()
171
+ {
172
+ return usePGO ? " dmd-pgo" : " dmd" ;
173
+ }
167
174
// default target
168
175
if (! args.length)
169
- args = [" dmd " ];
176
+ args = [defaultRule() ];
170
177
171
178
auto targets = predefinedTargets(args); // preprocess
172
179
@@ -475,17 +482,134 @@ alias dmdDefault = makeRule!((builder, rule) => builder
475
482
.description(" Build dmd" )
476
483
.deps([dmdExe(null , null , null ), dmdConf])
477
484
);
485
+ struct PGOState
486
+ {
487
+ // Does the host compiler actually support PGO, if not print a message
488
+ static bool checkPGO (string x)
489
+ {
490
+ switch (env[" HOST_DMD_KIND" ])
491
+ {
492
+ case " dmd" :
493
+ abortBuild(` DMD does not support PGO!` );
494
+ break ;
495
+ case " ldc" :
496
+ return true ;
497
+ break ;
498
+ case " gdc" :
499
+ abortBuild(` PGO (or AutoFDO) builds are not yet supported for gdc` );
500
+ break ;
501
+ default :
502
+ assert (false , " Unknown host compiler kind: " ~ env[" HOST_DMD_KIND" ]);
503
+ }
504
+ assert (0 );
505
+ }
506
+ this (string set)
507
+ {
508
+ hostKind = set;
509
+ profDirPath = buildPath(env[" G" ], " dmd_profdata" );
510
+ mkdirRecurse(profDirPath);
511
+ }
512
+ string profDirPath;
513
+ string hostKind;
514
+ string [] pgoGenerateFlags () const
515
+ {
516
+ switch (hostKind)
517
+ {
518
+ case " ldc" :
519
+ return [" -fprofile-instr-generate=" ~ pgoDataPath ~ " /data.%p.raw" ];
520
+ default :
521
+ return [" " ];
522
+ }
523
+ }
524
+ string [] pgoUseFlags () const
525
+ {
526
+ switch (hostKind)
527
+ {
528
+ case " ldc" :
529
+ return [" -fprofile-instr-use=" ~ buildPath(pgoDataPath(), " merged.data" )];
530
+ default :
531
+ return [" " ];
532
+ }
533
+ }
534
+ string pgoDataPath () const
535
+ {
536
+ return profDirPath;
537
+ }
538
+ }
539
+ // Compiles the test runner
540
+ alias testRunner = methodInit! (BuildRule, (rundBuilder, rundRule) => rundBuilder
541
+ .msg(" (DC) RUN.D" )
542
+ .sources([ testDir.buildPath( " run.d" ) ])
543
+ .target(env[" GENERATED" ].buildPath(" run" .exeName))
544
+ .command([ env[" HOST_DMD_RUN" ], " -of=" ~ rundRule.target, " -i" , " -I" ~ testDir] ~ rundRule.sources));
545
+
546
+ // __gshared PGOState pgoState;
547
+ alias dmdPGO = makeRule! ((builder, rule) {
548
+ const dmdKind = env[" HOST_DMD_KIND" ];
549
+ PGOState pgoState = PGOState(dmdKind);
550
+
551
+ alias buildInstrumentedDmd = methodInit! (BuildRule, (rundBuilder, rundRule) => rundBuilder
552
+ .msg(" Built dmd with PGO instrumentation" )
553
+ .deps([dmdExe(null , pgoState.pgoGenerateFlags(), pgoState.pgoGenerateFlags()), dmdConf]));
554
+
555
+ alias genData = methodInit! (BuildRule, (rundBuilder, rundRule) => rundBuilder
556
+ .msg(" Compiling dmd testsuite to generate PGO data" )
557
+ .sources([ testDir.buildPath( " run.d" ) ])
558
+ .deps([buildInstrumentedDmd, testRunner])
559
+ .commandFunction({
560
+ // Run dmd test suite to get data
561
+ const scope cmd = [ testRunner.targets[0 ], " compilable" , " -j" ~ jobs.to! string ];
562
+ log(" %-(%s %)" , cmd);
563
+ if (spawnProcess(cmd, null , Config.init, testDir).wait())
564
+ stderr.writeln(" dmd tests failed! This will not end the PGO build because some data may have been gathered" );
565
+ }));
566
+ alias genPhobosData = methodInit! (BuildRule, (rundBuilder, rundRule) => rundBuilder
567
+ .msg(" Compiling phobos testsuite to generate PGO data" )
568
+ .deps([buildInstrumentedDmd])
569
+ .commandFunction({
570
+ // Run phobos unittests
571
+ // TODO makefiles
572
+ // generated/linux/release/64/unittest/test_runner builds the unittests without running them.
573
+ const scope cmd = [" make" , " -C" , " ../phobos" , " -j" ~ jobs.to! string , " -fposix.mak" , " generated/linux/release/64/unittest/test_runner" , " DMD_DIR=" ~ dmdRepo];
574
+ log(" %-(%s %)" , cmd);
575
+ if (spawnProcess(cmd, null , Config.init).wait())
576
+ stderr.writeln(" Phobos Tests failed! This will not end the PGO build because some data may have been gathered" );
577
+ }));
578
+ alias finalDataMerge = methodInit! (BuildRule, (rundBuilder, rundRule) => rundBuilder
579
+ .msg(" Merging PGO data" )
580
+ .deps([/* genData */ genData])
581
+ .commandFunction({
582
+ // Run dmd test suite to get data
583
+ scope cmd = [" ldc-profdata" , " merge" , " --output=merged.data" ];
584
+ import std.file : dirEntries;
585
+ auto files = dirEntries(pgoState.pgoDataPath, " *.raw" , SpanMode.shallow).array;
586
+ files.each! (f => cmd ~= f);
587
+ log(" %-(%s %)" , cmd);
588
+ if (spawnProcess(cmd, null , Config.init, pgoState.pgoDataPath).wait())
589
+ abortBuild(" Merge failed" );
590
+ files.each! (f => remove(f));
591
+ }));
592
+ builder
593
+ .name(" dmd-pgo" )
594
+ .description(" Build dmd with PGO data collected from the dmd and phobos testsuites" )
595
+ .msg(" Build with collected PGO data" )
596
+ .condition(() => PGOState.checkPGO(dmdKind))
597
+ .deps([finalDataMerge])
598
+ .commandFunction({
599
+ auto addArgs = pgoState.pgoUseFlags ~ " -wi" ;
600
+ auto cmd = [env[" HOST_DMD_RUN" ], " -run" , " src/build.d" , " ENABLE_RELEASE=1" ,
601
+ " ENABLE_LTO=1" ,
602
+ " DFLAGS=" ~ joiner(addArgs, " " ).to! string , " --force" , " -j" ~ jobs.to! string ];
603
+ log(" %-(%s %)" , cmd);
604
+ if (spawnProcess(cmd, null , Config.init).wait())
605
+ abortBuild(" PGO Compilation failed" );
606
+ });
607
+ }
608
+ );
478
609
479
610
// / Run's the test suite (unittests & `run.d`)
480
611
alias runTests = makeRule! ((testBuilder, testRule)
481
612
{
482
- // Precompiles the test runner
483
- alias runner = methodInit! (BuildRule, (rundBuilder, rundRule) => rundBuilder
484
- .msg(" (DC) RUN.D" )
485
- .sources([ testDir.buildPath( " run.d" ) ])
486
- .target(env[" GENERATED" ].buildPath(" run" .exeName))
487
- .command([ env[" HOST_DMD_RUN" ], " -of=" ~ rundRule.target, " -i" , " -I" ~ testDir] ~ rundRule.sources));
488
-
489
613
// Reference header assumes Linux64
490
614
auto headerCheck = env[" OS" ] == " linux" && env[" MODEL" ] == " 64"
491
615
? [ runCxxHeadersTest ] : null ;
@@ -494,10 +618,10 @@ alias runTests = makeRule!((testBuilder, testRule)
494
618
.name(" test" )
495
619
.description(" Run the test suite using test/run.d" )
496
620
.msg(" (RUN) TEST" )
497
- .deps([dmdDefault, runDmdUnittest, runner ] ~ headerCheck)
621
+ .deps([dmdDefault, runDmdUnittest, testRunner ] ~ headerCheck)
498
622
.commandFunction({
499
623
// Use spawnProcess to avoid output redirection for `command`s
500
- const scope cmd = [ runner .targets[0 ], " -j" ~ jobs.to! string ];
624
+ const scope cmd = [ testRunner .targets[0 ], " -j" ~ jobs.to! string ];
501
625
log(" %-(%s %)" , cmd);
502
626
if (spawnProcess(cmd, null , Config.init, testDir).wait())
503
627
abortBuild(" Tests failed!" );
@@ -1261,6 +1385,23 @@ void processEnvironment()
1261
1385
assert (false , " Unknown host compiler kind: " ~ env[" HOST_DMD_KIND" ]);
1262
1386
}
1263
1387
}
1388
+ if (env.getNumberedBool(" ENABLE_PGO" ))
1389
+ {
1390
+ switch (env[" HOST_DMD_KIND" ])
1391
+ {
1392
+ case " dmd" :
1393
+ stderr.writeln(` DMD does not support PGO! Ignoring ENABLE_PGO flag` );
1394
+ break ;
1395
+ case " ldc" :
1396
+ usePGO = true ;
1397
+ break ;
1398
+ case " gdc" :
1399
+ stderr.writeln(` PGO (or AutoFDO) builds are not yet supported for gdc. Ignoring ENABLE_PGO flag` );
1400
+ break ;
1401
+ default :
1402
+ assert (false , " Unknown host compiler kind: " ~ env[" HOST_DMD_KIND" ]);
1403
+ }
1404
+ }
1264
1405
if (env.getNumberedBool(" ENABLE_UNITTEST" ))
1265
1406
{
1266
1407
dflags ~= [" -unittest" ];
0 commit comments