27
27
#include " swift/Driver/ToolChain.h"
28
28
#include " llvm/ADT/DenseSet.h"
29
29
#include " llvm/ADT/MapVector.h"
30
+ #include " llvm/ADT/SetVector.h"
30
31
#include " llvm/ADT/StringExtras.h"
31
32
#include " llvm/ADT/TinyPtrVector.h"
32
33
#include " llvm/Option/Arg.h"
@@ -136,6 +137,17 @@ namespace driver {
136
137
// / don't need to run.
137
138
CommandSet ScheduledCommands;
138
139
140
+ // / A temporary buffer to hold commands that were scheduled but haven't been
141
+ // / added to the Task Queue yet, because we might try batching them together
142
+ // / first.
143
+ CommandSet PendingExecution;
144
+
145
+ // / Set of synthetic BatchJobs that serve to cluster subsets of jobs waiting
146
+ // / in PendingExecution. Also used to identify (then unpack) BatchJobs back
147
+ // / to their underlying non-Batch Jobs, when running a callback from
148
+ // / TaskQueue.
149
+ CommandSet BatchJobs;
150
+
139
151
// / All jobs which have finished execution or which have been determined
140
152
// / that they don't need to run.
141
153
CommandSet FinishedCommands;
@@ -222,14 +234,24 @@ namespace driver {
222
234
return ;
223
235
}
224
236
237
+ // Adding to scheduled means we've committed to its completion (not
238
+ // distinguished from skipping). We never remove it once inserted.
239
+ ScheduledCommands.insert (Cmd);
240
+
241
+ // Adding to pending means it should be in the next round of additions to
242
+ // the task queue (either batched or singularly); we remove Jobs from
243
+ // PendingExecution once we hand them over to the TaskQueue.
244
+ PendingExecution.insert (Cmd);
245
+ }
246
+
247
+ void addPendingJobToTaskQueue (const Job *Cmd) {
225
248
// FIXME: Failing here should not take down the whole process.
226
249
bool success = writeFilelistIfNecessary (Cmd, Comp.Diags );
227
250
assert (success && " failed to write filelist" );
228
251
(void )success;
229
252
230
253
assert (Cmd->getExtraEnvironment ().empty () &&
231
254
" not implemented for compilations with multiple jobs" );
232
- ScheduledCommands.insert (Cmd);
233
255
if (Comp.ShowJobLifecycle )
234
256
llvm::outs () << " Added to TaskQueue: " << LogJob (Cmd) << " \n " ;
235
257
TQ->addTask (Cmd->getExecutable (), Cmd->getArguments (), llvm::None,
@@ -289,6 +311,8 @@ namespace driver {
289
311
parseable_output::emitBeganMessage (llvm::errs (), *BeganCmd, Pid);
290
312
}
291
313
314
+ // / Note that a .swiftdeps file failed to load and take corrective actions:
315
+ // / disable incremental logic and schedule all existing deferred commands.
292
316
void
293
317
dependencyLoadFailed (StringRef DependenciesFile, bool Warn=true ) {
294
318
if (Warn && Comp.ShowIncrementalBuildDecisions )
@@ -301,9 +325,10 @@ namespace driver {
301
325
DeferredCommands.clear ();
302
326
}
303
327
304
- // / Helper that reloads a job's .swiftdeps file after the job exits, and
305
- // / re-runs transitive marking to ensure everything is properly invalidated
306
- // / by any new dependency edges introduced by it.
328
+ // / Helper that attmepts to reload a job's .swiftdeps file after the job
329
+ // / exits, and re-run transitive marking to ensure everything is properly
330
+ // / invalidated by any new dependency edges introduced by it. If reloading
331
+ // / fails, this can cause deferred jobs to be immediately scheduled.
307
332
template <unsigned N>
308
333
void reloadAndRemarkDeps (const Job *FinishedCmd,
309
334
int ReturnCode,
@@ -381,6 +406,28 @@ namespace driver {
381
406
}
382
407
}
383
408
409
+ // / Unpack a \c BatchJob that has finished into its constituent \c Job
410
+ // / members, and call \c taskFinished on each, propagating any \c
411
+ // / TaskFinishedResponse other than \c
412
+ // / TaskFinishedResponse::ContinueExecution from any of the constituent
413
+ // / calls.
414
+ TaskFinishedResponse
415
+ unpackAndFinishBatch (ProcessId Pid, int ReturnCode, StringRef Output,
416
+ StringRef Errors, const BatchJob *B) {
417
+ if (Comp.ShowJobLifecycle )
418
+ llvm::outs () << " Batch job finished: " << LogJob (B) << " \n " ;
419
+ auto res = TaskFinishedResponse::ContinueExecution;
420
+ for (const Job *J : B->getCombinedJobs ()) {
421
+ if (Comp.ShowJobLifecycle )
422
+ llvm::outs () << " ==> Unpacked batch constituent finished: "
423
+ << LogJob (J) << " \n " ;
424
+ auto r = taskFinished (Pid, ReturnCode, Output, Errors, (void *)J);
425
+ if (r != TaskFinishedResponse::ContinueExecution)
426
+ res = r;
427
+ }
428
+ return res;
429
+ }
430
+
384
431
// / Callback which will be called immediately after a task has finished
385
432
// / execution. Determines if execution should continue, and also schedule
386
433
// / any additional Jobs which we now know we need to run.
@@ -393,6 +440,11 @@ namespace driver {
393
440
DriverTimers[FinishedCmd]->stopTimer ();
394
441
}
395
442
443
+ if (BatchJobs.count (FinishedCmd) != 0 ) {
444
+ return unpackAndFinishBatch (Pid, ReturnCode, Output, Errors,
445
+ static_cast <const BatchJob *>(FinishedCmd));
446
+ }
447
+
396
448
if (Comp.Level == OutputLevel::Parseable) {
397
449
// Parseable output was requested.
398
450
parseable_output::emitFinishedMessage (llvm::errs (), *FinishedCmd, Pid,
@@ -446,6 +498,30 @@ namespace driver {
446
498
return TaskFinishedResponse::ContinueExecution;
447
499
}
448
500
501
+ // / Unpack a \c BatchJob that has finished into its constituent \c Job
502
+ // / members, and call \c taskSignalled on each, propagating any \c
503
+ // / TaskFinishedResponse other than \c
504
+ // / TaskFinishedResponse::ContinueExecution from any of the constituent
505
+ // / calls.
506
+ TaskFinishedResponse
507
+ unpackAndSignalBatch (ProcessId Pid, StringRef ErrorMsg, StringRef Output,
508
+ StringRef Errors, const BatchJob *B,
509
+ Optional<int > Signal) {
510
+ if (Comp.ShowJobLifecycle )
511
+ llvm::outs () << " Batch job signalled: " << LogJob (B) << " \n " ;
512
+ auto res = TaskFinishedResponse::ContinueExecution;
513
+ for (const Job *J : B->getCombinedJobs ()) {
514
+ if (Comp.ShowJobLifecycle )
515
+ llvm::outs () << " ==> Unpacked batch constituent signalled: "
516
+ << LogJob (J) << " \n " ;
517
+ auto r = taskSignalled (Pid, ErrorMsg, Output, Errors,
518
+ (void *)J, Signal);
519
+ if (r != TaskFinishedResponse::ContinueExecution)
520
+ res = r;
521
+ }
522
+ return res;
523
+ }
524
+
449
525
TaskFinishedResponse
450
526
taskSignalled (ProcessId Pid, StringRef ErrorMsg, StringRef Output,
451
527
StringRef Errors, void *Context, Optional<int > Signal) {
@@ -455,6 +531,12 @@ namespace driver {
455
531
DriverTimers[SignalledCmd]->stopTimer ();
456
532
}
457
533
534
+ if (BatchJobs.count (SignalledCmd) != 0 ) {
535
+ return unpackAndSignalBatch (Pid, ErrorMsg, Output, Errors,
536
+ static_cast <const BatchJob *>(SignalledCmd),
537
+ Signal);
538
+ }
539
+
458
540
if (Comp.Level == OutputLevel::Parseable) {
459
541
// Parseable output was requested.
460
542
parseable_output::emitSignalledMessage (llvm::errs (), *SignalledCmd,
@@ -596,6 +678,126 @@ namespace driver {
596
678
}
597
679
}
598
680
681
+ // / Insert all jobs in \p Cmds (of descriptive name \p Kind) to the \c
682
+ // / TaskQueue, and clear \p Cmds.
683
+ void transferJobsToTaskQueue (CommandSet &Cmds, StringRef Kind) {
684
+ for (const Job *Cmd : Cmds) {
685
+ if (Comp.ShowJobLifecycle )
686
+ llvm::outs () << " Adding " << Kind
687
+ << " job to task queue: "
688
+ << LogJob (Cmd) << " \n " ;
689
+ addPendingJobToTaskQueue (Cmd);
690
+ }
691
+ Cmds.clear ();
692
+ }
693
+
694
+ // / Partition the jobs in \c PendingExecution into those that are \p
695
+ // / Batchable and those that are \p NonBatchable, clearing \p
696
+ // / PendingExecution.
697
+ void getPendingBatchableJobs (CommandSet &Batchable,
698
+ CommandSet &NonBatchable) {
699
+ for (const Job *Cmd : PendingExecution) {
700
+ if (Comp.getToolChain ().jobIsBatchable (Comp, Cmd)) {
701
+ if (Comp.ShowJobLifecycle )
702
+ llvm::outs () << " Batchable: " << LogJob (Cmd) << " \n " ;
703
+ Batchable.insert (Cmd);
704
+ } else {
705
+ if (Comp.ShowJobLifecycle )
706
+ llvm::outs () << " Not batchable: " << LogJob (Cmd) << " \n " ;
707
+ NonBatchable.insert (Cmd);
708
+ }
709
+ }
710
+ PendingExecution.clear ();
711
+ }
712
+
713
+ // / If \p CurrentBatch is nonempty, construct a new \c BatchJob from its
714
+ // / contents by calling \p ToolChain::constructBatchJob, then insert the
715
+ // / new \c BatchJob into \p Batches and clear \p CurrentBatch.
716
+ void
717
+ formBatchJobFromCurrentBatch (CommandSet &Batches,
718
+ llvm::SetVector<const Job *> &CurrentBatch) {
719
+ if (CurrentBatch.empty ())
720
+ return ;
721
+ if (Comp.ShowJobLifecycle )
722
+ llvm::outs () << " Forming batch job from "
723
+ << CurrentBatch.size () << " constituents\n " ;
724
+ auto const &TC = Comp.getToolChain ();
725
+ auto J = TC.constructBatchJob (CurrentBatch.getArrayRef (), Comp);
726
+ if (J)
727
+ Batches.insert (Comp.addJob (std::move (J)));
728
+ CurrentBatch.clear ();
729
+ }
730
+
731
+ // / Return true iff \p Cmd can be expanded by \p CurrentBatch, meaning
732
+ // / that \p CurrentBatch is smaller than \p TargetBatchSize and \p Cmd
733
+ // / is batch-combinable with the equivalence class of \p CurrentBatch
734
+ // / (as represented by element 0 of \p CurrentBatch).
735
+ bool canExpandBatch (const Job *Cmd,
736
+ llvm::SetVector<const Job *> &CurrentBatch,
737
+ size_t TargetBatchSize) {
738
+ auto const &TC = Comp.getToolChain ();
739
+ return (CurrentBatch.empty () ||
740
+ (TC.jobsAreBatchCombinable (Comp, Cmd, CurrentBatch[0 ]) &&
741
+ CurrentBatch.size () < TargetBatchSize));
742
+ }
743
+
744
+ // / If \p CurrentBatch can't be expanded with \p Cmd, form a new \c BatchJob
745
+ // / from \p CurrentBatch, add it to \p Batches, and reset\p CurrentBatch;
746
+ // / then in either case, insert \p Cmd into \p CurrentBatch.
747
+ void expandBatch (const Job *Cmd,
748
+ CommandSet &Batches,
749
+ llvm::SetVector<const Job *> &CurrentBatch,
750
+ size_t TargetBatchSize) {
751
+ if (!canExpandBatch (Cmd, CurrentBatch, TargetBatchSize)) {
752
+ formBatchJobFromCurrentBatch (Batches, CurrentBatch);
753
+ }
754
+ llvm::outs () << " Adding to batch: " << LogJob (Cmd) << " \n " ;
755
+ CurrentBatch.insert (Cmd);
756
+ }
757
+
758
+ // / Select jobs that are batch-combinable from \c PendingExecution, combine
759
+ // / them together into \p BatchJob instances (also inserted into \p
760
+ // / BatchJobs), and enqueue all \c PendingExecution jobs (whether batched or
761
+ // / not) into the \c TaskQueue for execution.
762
+ void formBatchJobsAndAddPendingJobsToTaskQueue () {
763
+
764
+ // If batch mode is not enabled, just transfer the set of pending jobs to
765
+ // the task queue, as-is.
766
+ if (!Comp.getBatchModeEnabled ()) {
767
+ transferJobsToTaskQueue (PendingExecution, " standard" );
768
+ return ;
769
+ }
770
+
771
+ // Partition the pending jobs.
772
+ CommandSet Batchable, NonBatchable, Batches;
773
+ getPendingBatchableJobs (Batchable, NonBatchable);
774
+ size_t TargetBatchSize = Batchable.size () / Comp.NumberOfParallelCommands ;
775
+
776
+ if (Comp.ShowJobLifecycle ) {
777
+ llvm::outs () << " Found " << Batchable.size () << " batchable jobs\n " ;
778
+ llvm::outs () << " Aiming for batch size " << TargetBatchSize << ' \n ' ;
779
+ }
780
+
781
+ // Batch the batchable jobs.
782
+ llvm::SetVector<const Job *> CurrentBatch;
783
+ for (const Job *Cmd : Batchable) {
784
+ expandBatch (Cmd, Batches, CurrentBatch, TargetBatchSize);
785
+ }
786
+
787
+ // Form a residual incomplete batch if any jobs remain.
788
+ if (!CurrentBatch.empty ()) {
789
+ formBatchJobFromCurrentBatch (Batches, CurrentBatch);
790
+ }
791
+
792
+ // Save batches so we can locate and decompose them on task-exit.
793
+ for (const Job *Cmd : Batches)
794
+ BatchJobs.insert (Cmd);
795
+
796
+ // Enqueue the resulting jobs, batched and non-batched alike.
797
+ transferJobsToTaskQueue (Batches, " batch" );
798
+ transferJobsToTaskQueue (NonBatchable, " non-batch" );
799
+ }
800
+
599
801
void runTaskQueueToCompletion () {
600
802
do {
601
803
using namespace std ::placeholders;
@@ -607,20 +809,39 @@ namespace driver {
607
809
std::bind (&PerformJobsState::taskSignalled, this ,
608
810
_1, _2, _3, _4, _5, _6));
609
811
610
- // Mark all remaining deferred commands as skipped.
812
+ // Returning from TaskQueue::execute should mean either an empty
813
+ // TaskQueue or a failed subprocess.
814
+ assert (!(Result == 0 && TQ->hasRemainingTasks ()));
815
+
816
+ // Task-exit callbacks from TaskQueue::execute may have unblocked jobs,
817
+ // which means there might be PendingExecution jobs to enqueue here. If
818
+ // there are, we need to continue trying to make progress on the
819
+ // TaskQueue before we start marking deferred jobs as skipped, below.
820
+ if (!PendingExecution.empty () && Result == 0 ) {
821
+ formBatchJobsAndAddPendingJobsToTaskQueue ();
822
+ continue ;
823
+ }
824
+
825
+ // If we got here, all the queued and pending work we know about is
826
+ // done; mark anything still in deferred state as skipped.
611
827
for (const Job *Cmd : DeferredCommands) {
612
828
if (Comp.Level == OutputLevel::Parseable) {
613
- // Provide output indicating this command was skipped if parseable output
614
- // was requested.
829
+ // Provide output indicating this command was skipped if parseable
830
+ // output was requested.
615
831
parseable_output::emitSkippedMessage (llvm::errs (), *Cmd);
616
832
}
617
-
618
833
ScheduledCommands.insert (Cmd);
619
834
markFinished (Cmd, /* Skipped=*/ true );
620
835
}
621
836
DeferredCommands.clear ();
622
837
623
- // ...which may allow us to go on and do later tasks.
838
+ // It's possible that by marking some jobs as skipped, we unblocked
839
+ // some jobs and thus have entries in PendingExecution again; push
840
+ // those through to the TaskQueue.
841
+ formBatchJobsAndAddPendingJobsToTaskQueue ();
842
+
843
+ // If we added jobs to the TaskQueue, and we are not in an error state,
844
+ // we want to give the TaskQueue another run.
624
845
} while (Result == 0 && TQ->hasRemainingTasks ());
625
846
}
626
847
@@ -848,6 +1069,7 @@ int Compilation::performJobsImpl(bool &abnormalExit) {
848
1069
849
1070
State.scheduleInitialJobs ();
850
1071
State.scheduleAdditionalJobs ();
1072
+ State.formBatchJobsAndAddPendingJobsToTaskQueue ();
851
1073
State.runTaskQueueToCompletion ();
852
1074
State.checkUnfinishedJobs ();
853
1075
0 commit comments