Skip to content

Commit e2e19dc

Browse files
committed
Add Compilation "Wave" Assertion in +Asserts Builds
The "wave" of a compilation job describes the number of indirections through other compile jobs the driver required to reach the decision to schedule a job. In incremental mode, it should always be the case that it takes no more than two complete waves to arrive at a fixpoint in the build. This is a natural consequence of the structure of the dependencies emitted by the Swift frontend - namely we rely on transitivity in dependency arcs. A quick proof sketch: Suppose an arbitrary perturbation of the inputs to an incremental compilation session are made. In the first wave, dependency edges from the prior build's state (the "zeroeth wave") are loaded and the files corresponding to invalidated edges are scheduled into the first wave. Supposing the second wave is not the null set - the trivial case - there are additional arcs that were invalidated. Now suppose that there were a third wave. Take an arbitrary arc invalidated by this third wave. It must be the case that the file containing the use is not new - else it would be scheduled. Further it must be the case that its def was not invalidated by the zeroeth or first waves of compilation otherwise we would have scheduled it into the first or second waves. Finally, it must have a use that was discovered in the second wave. But in order for that use to have been included in the second wave, there must have been an invalidated arc created by the first wave. By transitivity of dependency arcs, there must therefore be a dependency arc from a definition invalidated in the first wave to our third wave job, which implies that the file would be scheduled into the second wave! [Insert contradiction pig image here]
1 parent c3881d9 commit e2e19dc

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

include/swift/Driver/Job.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,23 @@ class Job {
308308
/// The modification time of the main input file, if any.
309309
llvm::sys::TimePoint<> InputModTime = llvm::sys::TimePoint<>::max();
310310

311+
#ifndef NDEBUG
312+
/// The "wave" of incremental jobs that this \c Job was scheduled into.
313+
///
314+
/// The first "wave" of jobs is computed by the driver from the set of inputs
315+
/// and external files that have been mutated by the user. From there, as
316+
/// jobs from the first wave finish executing, we reload their \c swiftdeps
317+
/// files and re-integrate them into the dependency graph to discover
318+
/// the jobs for the second "wave".
319+
///
320+
/// In +asserts builds, we ensure that no more than two "waves" occur for
321+
/// any given incremental compilation session. This is a consequence of
322+
/// 1) transitivity in dependency arcs
323+
/// 2) dependency tracing from uses that affect a def's interfaces to that
324+
/// def's uses.
325+
mutable unsigned Wave = 1;
326+
#endif
327+
311328
public:
312329
Job(const JobAction &Source, SmallVectorImpl<const Job *> &&Inputs,
313330
std::unique_ptr<CommandOutput> Output, const char *Executable,
@@ -399,6 +416,11 @@ class Job {
399416
/// Assumes that, if a compile job, has one primary swift input
400417
/// May return empty if none.
401418
StringRef getFirstSwiftPrimaryInput() const;
419+
420+
#ifndef NDEBUG
421+
unsigned getWave() const { return Wave; }
422+
void setWave(unsigned WaveNum) const { Wave = WaveNum; }
423+
#endif
402424
};
403425

404426
/// A BatchJob comprises a _set_ of jobs, each of which is sufficiently similar

lib/Driver/Compilation.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,17 @@ namespace driver {
323323
return;
324324
}
325325

326+
#ifndef NDEBUG
327+
// If we can, assert that no compile jobs are scheduled beyond the second
328+
// wave. If this assertion fails, it indicates one of:
329+
// 1) A failure of the driver's job tracing machinery to follow a
330+
// dependency arc.
331+
// 2) A failure of the frontend to emit a dependency arc.
332+
if (isa<CompileJobAction>(Cmd->getSource()) && Cmd->getWave() > 2) {
333+
llvm_unreachable("Scheduled a command into a third wave!");
334+
}
335+
#endif
336+
326337
// Adding to scheduled means we've committed to its completion (not
327338
// distinguished from skipping). We never remove it once inserted.
328339
ScheduledCommands.insert(Cmd);
@@ -694,8 +705,16 @@ namespace driver {
694705
"because of dependencies discovered later");
695706

696707
scheduleCommandsInSortedOrder(DependentsInEffect);
697-
for (const Job *Cmd : DependentsInEffect)
698-
DeferredCommands.erase(Cmd);
708+
for (const Job *Cmd : DependentsInEffect) {
709+
if (DeferredCommands.erase(Cmd)) {
710+
#ifndef NDEBUG
711+
if (isa<CompileJobAction>(FinishedCmd->getSource()))
712+
Cmd->setWave(FinishedCmd->getWave() + 1);
713+
#else
714+
continue;
715+
#endif
716+
}
717+
}
699718
return TaskFinishedResponse::ContinueExecution;
700719
}
701720

0 commit comments

Comments
 (0)