Skip to content

Commit cecb7c8

Browse files
committed
Schedule MergeModules Incrementally
Plumb the logic necessary to schedule merge-modules incrementally. This means that if any inputs jobs to merge-modules run, merge-modules is run. But, if they are all skipped, merge-modules will be skipped as well. This requires some light special-casing of the legacy driver's incremental job handling because it assumes in a few places it can always extract a swiftdeps file. This invariant will be further broken when the precompile step for bridging headers is skipped as well. rdar://65893400
1 parent a81cf93 commit cecb7c8

File tree

5 files changed

+85
-18
lines changed

5 files changed

+85
-18
lines changed

lib/Driver/Compilation.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ namespace driver {
446446

447447
std::vector<const Job*>
448448
reloadAndRemarkDeps(const Job *FinishedCmd, int ReturnCode,
449-
const bool forRanges) {
449+
const bool forRanges) {
450450
const CommandOutput &Output = FinishedCmd->getOutput();
451451
StringRef DependenciesFile =
452452
Output.getAdditionalOutputForType(file_types::TY_SwiftDeps);
@@ -458,7 +458,8 @@ namespace driver {
458458
// coarse dependencies that always affect downstream nodes), but we're
459459
// not using either of those right now, and this logic should probably
460460
// be revisited when we are.
461-
assert(FinishedCmd->getCondition() == Job::Condition::Always);
461+
assert(isa<MergeModuleJobAction>(FinishedCmd->getSource()) ||
462+
FinishedCmd->getCondition() == Job::Condition::Always);
462463
return {};
463464
}
464465
const bool compileExitedNormally =
@@ -907,6 +908,7 @@ namespace driver {
907908
return everyIncrementalJob;
908909
};
909910

911+
const Job *mergeModulesJob = nullptr;
910912
CommandSet jobsToSchedule;
911913
CommandSet initialCascadingCommands;
912914
for (const Job *cmd : Comp.getJobs()) {
@@ -915,6 +917,11 @@ namespace driver {
915917
continue;
916918
}
917919

920+
if (isa<MergeModuleJobAction>(cmd->getSource())) {
921+
assert(!mergeModulesJob && "multiple scheduled merge-modules jobs?");
922+
mergeModulesJob = cmd;
923+
}
924+
918925
const Optional<std::pair<bool, bool>> shouldSchedAndIsCascading =
919926
computeShouldInitiallyScheduleJobAndDependendents(cmd, forRanges);
920927
if (!shouldSchedAndIsCascading)
@@ -936,6 +943,15 @@ namespace driver {
936943
collectIncrementalExternallyDependentJobsFromDependencyGraph(
937944
forRanges))
938945
jobsToSchedule.insert(cmd);
946+
947+
// The merge-modules job is special: it *must* be scheduled if any other
948+
// job has been scheduled because any other job can influence the
949+
// structure of the resulting module. Additionally, the initial scheduling
950+
// predicate above is only aware of intra-module changes. External
951+
// dependencies changing *must* cause merge-modules to be scheduled.
952+
if (!jobsToSchedule.empty() && mergeModulesJob) {
953+
jobsToSchedule.insert(mergeModulesJob);
954+
}
939955
return jobsToSchedule;
940956
}
941957

@@ -1031,6 +1047,13 @@ namespace driver {
10311047
/// But returns None if there was a dependency read error.
10321048
Optional<std::pair<Job::Condition, bool>>
10331049
loadDependenciesAndComputeCondition(const Job *const Cmd, bool forRanges) {
1050+
// merge-modules Jobs do not have .swiftdeps files associated with them,
1051+
// however, their compilation condition is computed as a function of their
1052+
// inputs, so their condition can be used as normal.
1053+
if (isa<MergeModuleJobAction>(Cmd->getSource())) {
1054+
return std::make_pair(Cmd->getCondition(), true);
1055+
}
1056+
10341057
// Try to load the dependencies file for this job. If there isn't one, we
10351058
// always have to run the job, but it doesn't affect any other jobs. If
10361059
// there should be one but it's not present or can't be loaded, we have to

lib/Driver/Driver.cpp

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2749,24 +2749,30 @@ static void addDiagFileOutputForPersistentPCHAction(
27492749

27502750
/// If the file at \p input has not been modified since the last build (i.e. its
27512751
/// mtime has not changed), adjust the Job's condition accordingly.
2752-
static void
2753-
handleCompileJobCondition(Job *J, CompileJobAction::InputInfo inputInfo,
2754-
StringRef input, bool alwaysRebuildDependents) {
2755-
if (inputInfo.status == CompileJobAction::InputInfo::NewlyAdded) {
2752+
static void handleCompileJobCondition(Job *J,
2753+
CompileJobAction::InputInfo inputInfo,
2754+
Optional<StringRef> input,
2755+
bool alwaysRebuildDependents) {
27562756
using InputStatus = CompileJobAction::InputInfo::Status;
2757+
2758+
if (inputInfo.status == InputStatus::NewlyAdded) {
27572759
J->setCondition(Job::Condition::NewlyAdded);
27582760
return;
27592761
}
27602762

2763+
auto output = J->getOutput().getPrimaryOutputFilename();
27612764
bool hasValidModTime = false;
27622765
llvm::sys::fs::file_status inputStatus;
2763-
if (!llvm::sys::fs::status(input, inputStatus)) {
2766+
if (input.hasValue() && !llvm::sys::fs::status(*input, inputStatus)) {
2767+
J->setInputModTime(inputStatus.getLastModificationTime());
2768+
hasValidModTime = J->getInputModTime() == inputInfo.previousModTime;
2769+
} else if (!llvm::sys::fs::status(output, inputStatus)) {
27642770
J->setInputModTime(inputStatus.getLastModificationTime());
27652771
hasValidModTime = true;
27662772
}
27672773

27682774
Job::Condition condition;
2769-
if (hasValidModTime && J->getInputModTime() == inputInfo.previousModTime) {
2775+
if (hasValidModTime) {
27702776
switch (inputInfo.status) {
27712777
case InputStatus::UpToDate:
27722778
if (llvm::sys::fs::exists(output))
@@ -2942,14 +2948,18 @@ Job *Driver::buildJobsForAction(Compilation &C, const JobAction *JA,
29422948
Job *J = C.addJob(std::move(ownedJob));
29432949

29442950
// If we track dependencies for this job, we may be able to avoid running it.
2945-
if (!J->getOutput()
2946-
.getAdditionalOutputForType(file_types::TY_SwiftDeps)
2947-
.empty()) {
2948-
if (InputActions.size() == 1) {
2949-
auto compileJob = cast<CompileJobAction>(JA);
2950-
bool alwaysRebuildDependents =
2951-
C.getArgs().hasArg(options::OPT_driver_always_rebuild_dependents);
2952-
handleCompileJobCondition(J, compileJob->getInputInfo(), BaseInput,
2951+
if (auto incrementalJob = dyn_cast<IncrementalJobAction>(JA)) {
2952+
const bool alwaysRebuildDependents =
2953+
C.getArgs().hasArg(options::OPT_driver_always_rebuild_dependents);
2954+
if (!J->getOutput()
2955+
.getAdditionalOutputForType(file_types::TY_SwiftDeps)
2956+
.empty()) {
2957+
if (InputActions.size() == 1) {
2958+
handleCompileJobCondition(J, incrementalJob->getInputInfo(), BaseInput,
2959+
alwaysRebuildDependents);
2960+
}
2961+
} else if (isa<MergeModuleJobAction>(JA)) {
2962+
handleCompileJobCondition(J, incrementalJob->getInputInfo(), None,
29532963
alwaysRebuildDependents);
29542964
}
29552965
}

test/Driver/Dependencies/one-way-merge-module-fine.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515

1616
// CHECK-SECOND-NOT: warning
1717
// CHECK-SECOND-NOT: Handled
18-
// CHECK-SECOND: Produced master.swiftmodule
18+
// CHECK-SECOND-NOT: Produced master.swiftmodule

test/Incremental/CrossModule/linear.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,21 @@
3838
// MODULE-B: Job finished: {merge-module: B.swiftmodule <= B.o}
3939

4040
// MODULE-A: Job skipped: {compile: A.o <= A.swift}
41-
// MODULE-A: Job finished: {merge-module: A.swiftmodule <= A.o}
41+
// MODULE-A: Job skipped: {merge-module: A.swiftmodule <= A.o}
42+
43+
//
44+
// And ensure that the null build really is null.
45+
//
46+
47+
// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s
48+
// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s
49+
// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s
50+
51+
// MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift}
52+
// MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o}
53+
54+
// MODULE-B-NULL: Job skipped: {compile: B.o <= B.swift}
55+
// MODULE-B-NULL: Job skipped: {merge-module: B.swiftmodule <= B.o}
56+
57+
// MODULE-A-NULL: Job skipped: {compile: A.o <= A.swift}
58+
// MODULE-A-NULL: Job skipped: {merge-module: A.swiftmodule <= A.o}

test/Incremental/CrossModule/transitive.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,20 @@
4242
// MODULE-A: Queuing because of incremental external dependencies: {compile: A.o <= A.swift}
4343
// MODULE-A: Job finished: {compile: A.o <= A.swift}
4444
// MODULE-A: Job finished: {merge-module: A.swiftmodule <= A.o}
45+
46+
//
47+
// And ensure that the null build really is null.
48+
//
49+
50+
// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/C.swiftmodule -enable-experimental-cross-module-incremental-build -module-name C -I %t -output-file-map %t/C.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC -DNEW %t/C.swift 2>&1 | %FileCheck -check-prefix MODULE-C-NULL %s
51+
// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/B.swiftmodule -enable-experimental-cross-module-incremental-build -module-name B -I %t -output-file-map %t/B.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/B.swift 2>&1 | %FileCheck -check-prefix MODULE-B-NULL %s
52+
// RUN: cd %t && %swiftc_driver -c -incremental -emit-dependencies -emit-module -emit-module-path %t/A.swiftmodule -enable-experimental-cross-module-incremental-build -module-name A -I %t -output-file-map %t/A.json -working-directory %t -driver-show-incremental -driver-show-job-lifecycle -DUSEC %t/A.swift 2>&1 | %FileCheck -check-prefix MODULE-A-NULL %s
53+
54+
// MODULE-C-NULL: Job skipped: {compile: C.o <= C.swift}
55+
// MODULE-C-NULL: Job skipped: {merge-module: C.swiftmodule <= C.o}
56+
57+
// MODULE-B-NULL: Job skipped: {compile: B.o <= B.swift}
58+
// MODULE-B-NULL: Job skipped: {merge-module: B.swiftmodule <= B.o}
59+
60+
// MODULE-A-NULL: Job skipped: {compile: A.o <= A.swift}
61+
// MODULE-A-NULL: Job skipped: {merge-module: A.swiftmodule <= A.o}

0 commit comments

Comments
 (0)