Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion include/swift/Driver/DependencyGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class DependencyGraphImpl {
llvm::StringMap<std::pair<std::vector<DependencyEntryTy>, DependencyMaskTy>> Dependencies;

/// The set of marked nodes.

llvm::SmallPtrSet<const void *, 16> Marked;

/// A list of all external dependencies that cannot be resolved from just this
Expand Down Expand Up @@ -153,6 +154,8 @@ class DependencyGraphImpl {
(void)newlyInserted;
}

/// See DependencyGraph::markTransitive.

void markTransitive(SmallVectorImpl<const void *> &visited,
const void *node, MarkTracerImpl *tracer = nullptr);
bool markIntransitive(const void *node) {
Expand Down Expand Up @@ -254,7 +257,7 @@ class DependencyGraph : public DependencyGraphImpl {
/// Marks \p node and all nodes that depend on \p node, and places any nodes
/// that get transitively marked into \p visited.
///
/// Nodes that have been previously marked are not included in \p newlyMarked,
/// Nodes that have been previously marked are not included in \p visited,
/// nor are their successors traversed, <em>even if their "provides" set has
/// been updated since it was marked.</em> (However, nodes that depend on the
/// given \p node are always traversed.)
Expand All @@ -264,6 +267,14 @@ class DependencyGraph : public DependencyGraphImpl {
///
/// If you want to see how each node gets added to \p visited, pass a local
/// MarkTracer instance to \p tracer.
///
/// Conservatively assumes that there exists a "cascading" edge into the
/// starting node. Therefore, mark the start. For each visited node, add it to
/// \p visited, and mark it if some incoming edge cascades. The start node is
/// NOT added to \p visited.
///
/// The traversal routines use
/// \p visited to avoid endless recursion.
template <unsigned N>
void markTransitive(SmallVector<T, N> &visited, T node,
MarkTracer *tracer = nullptr) {
Expand Down
8 changes: 8 additions & 0 deletions include/swift/Driver/Job.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,17 @@ class CommandOutput {
class Job {
public:
enum class Condition {
// There was no information about the previous build (i.e., an input map),
// or the map marked this Job as dirty or needing a cascading build.
// Be maximally conservative with dependencies.
Always,
// The input changed, or this job was scheduled as non-cascading in the last
// build but didn't get to run.
RunWithoutCascading,
// The best case: input didn't change, output exists.
// Only run if it depends on some other thing that changed.
CheckDependencies,
// Run no matter what (but may or may not cascade).
NewlyAdded
};

Expand Down
26 changes: 21 additions & 5 deletions lib/Driver/Compilation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,10 @@ namespace driver {
/// Jobs that incremental-mode has decided it can skip.
CommandSet DeferredCommands;

/// Jobs in the initial set with Condition::Always, or lacking existing
/// Jobs in the initial set with Condition::Always, and having an existing
/// .swiftdeps files.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems incorrect. NewlyAdded files would also show up here (at least they should).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, maybe not. Maybe the variable name just needs to change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the code from Compilation.cpp:

       case Job::Condition::Always:
          if (Comp.getIncrementalBuildEnabled() && !DependenciesFile.empty()) {
            // Ensure dependents will get recompiled.
            InitialOutOfDateCommands.push_back(Cmd);
            // Mark this job as cascading.
            DepGraph.markIntransitive(Cmd);
          }
          LLVM_FALLTHROUGH;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look at the code above and see if the code is wrong, or I'm reading it wrong, or what? Am I missing another place where jobs get added?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, didn't see your second thought, messages crossed in progress. Happy to change the name if you like. Let me know.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, given that this is the only place this gets set, I suggest "InitialCascadingCommands" or something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, will do!

SmallVector<const Job *, 16> InitialOutOfDateCommands;
/// Set by scheduleInitialJobs and used only by scheduleAdditionalJobs.
SmallVector<const Job *, 16> InitialCascadingCommands;

/// Dependency graph for deciding which jobs are dirty (need running)
/// or clean (can be skipped).
Expand Down Expand Up @@ -382,7 +383,7 @@ namespace driver {
DeferredCommands.clear();
}

/// Helper that attmepts to reload a job's .swiftdeps file after the job
/// Helper that attempts to reload a job's .swiftdeps file after the job
/// exits, and re-run transitive marking to ensure everything is properly
/// invalidated by any new dependency edges introduced by it. If reloading
/// fails, this can cause deferred jobs to be immediately scheduled.
Expand All @@ -406,6 +407,13 @@ namespace driver {
// If we have a dependency file /and/ the frontend task exited normally,
// we can be discerning about what downstream files to rebuild.
if (ReturnCode == EXIT_SUCCESS || ReturnCode == EXIT_FAILURE) {
// "Marked" means that everything provided by this node (i.e. Job) is
// dirty. Thus any file using any of these provides must be
// recompiled. (Only non-private entities are output as provides.) In
// other words, this Job "cascades"; the need to recompile it causes
// other recompilations. It is possible that the current code marks
// things that do not need to be marked. Unecessary compilation would
// result if that were the case.
bool wasCascading = DepGraph.isMarked(FinishedCmd);

switch (DepGraph.loadFromPath(FinishedCmd, DependenciesFile)) {
Expand Down Expand Up @@ -715,7 +723,15 @@ namespace driver {
switch (Condition) {
case Job::Condition::Always:
if (Comp.getIncrementalBuildEnabled() && !DependenciesFile.empty()) {
InitialOutOfDateCommands.push_back(Cmd);
// Ensure dependents will get recompiled.
InitialCascadingCommands.push_back(Cmd);
// Mark this job as cascading.
//
// It would probably be safe and simpler to markTransitive on the
// start nodes in the "Always" condition from the start instead of
// using markIntransitive and having later functions call
// markTransitive. That way markIntransitive would be an
// implementation detail of DependencyGraph.
DepGraph.markIntransitive(Cmd);
}
LLVM_FALLTHROUGH;
Expand All @@ -740,7 +756,7 @@ namespace driver {
// We scheduled all of the files that have actually changed. Now add the
// files that haven't changed, so that they'll get built in parallel if
// possible and after the first set of files if it's not.
for (auto *Cmd : InitialOutOfDateCommands) {
for (auto *Cmd : InitialCascadingCommands) {
DepGraph.markTransitive(AdditionalOutOfDateCommands, Cmd,
IncrementalTracer);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/Driver/CompilationRecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ namespace swift {
namespace driver {
namespace compilation_record {

/// Compilation record files (.swiftdeps files) are YAML files composed of these
/// top-level keys.
/// Compilation record files (-master.swiftdeps files) are YAML files composed
/// of these top-level keys.
enum class TopLevelKey {
/// The key for the Swift compiler version used to produce the compilation
/// record.
Expand Down