Skip to content

Commit 613e5d5

Browse files
committed
[WIP] Whole module optimization mode
1 parent c393e30 commit 613e5d5

File tree

5 files changed

+118
-27
lines changed

5 files changed

+118
-27
lines changed

Sources/SwiftDriver/Jobs/CompileJob.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ extension Driver {
5454
}
5555
commandLine.append(.path(input.file))
5656

57-
// If there is a primary output, add it.
58-
if isPrimary, let compilerOutputType = compilerOutputType {
57+
// If there is a primary output or we are doing multithreaded compiles,
58+
// add an output for the input.
59+
if isPrimary || numThreads > 0,
60+
let compilerOutputType = compilerOutputType {
5961
let output = (outputFileMap ?? OutputFileMap()).getOutput(
6062
inputFile: input.file,
6163
outputType: compilerOutputType
@@ -64,6 +66,15 @@ extension Driver {
6466
}
6567
}
6668

69+
// When not using primary file inputs or multithreading, add a single output.
70+
if !usesPrimaryFileInputs && numThreads == 0,
71+
let outputType = compilerOutputType {
72+
let existingOutputPath = outputFileMap?.existingOutputForSingleInput(
73+
outputType: outputType)
74+
let output = existingOutputPath ?? VirtualPath.temporary(.init(moduleName.appendingFileTypeExtension(outputType)))
75+
primaryOutputs.append(TypedVirtualPath(file: output, type: outputType))
76+
}
77+
6778
return primaryOutputs
6879
}
6980

Sources/SwiftDriver/Jobs/EmitModuleJob.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extension Driver {
3232
try addCommonFrontendOptions(commandLine: &commandLine)
3333
// FIXME: Add MSVC runtime library flags
3434

35-
// Add suppplementable outputs.
35+
// Add supplemental outputs.
3636
func addSupplementalOutput(path: VirtualPath?, flag: String, type: FileType) {
3737
guard let path = path else { return }
3838

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -164,40 +164,75 @@ extension Driver {
164164
mutating func addFrontendSupplementaryOutputArguments(commandLine: inout [Job.ArgTemplate], primaryInputs: [TypedVirtualPath]) throws -> [TypedVirtualPath] {
165165
var outputs: [TypedVirtualPath] = []
166166

167-
func addOutputsOfType(outputType: FileType, input: VirtualPath, flag: String) {
167+
/// Add output of a particular type, if needed.
168+
func addOutputOfType(
169+
outputType: FileType, finalOutputPath: VirtualPath?,
170+
input: VirtualPath?, flag: String
171+
) {
172+
// If there is no final output, there's nothing to do.
173+
guard let finalOutputPath = finalOutputPath else { return }
174+
175+
// If the whole of the compiler output is this type, there's nothing to
176+
// do.
168177
if outputType == compilerOutputType { return }
169178

179+
// Add the appropriate flag.
170180
commandLine.appendFlag(flag)
171181

172-
let path = (outputFileMap ?? OutputFileMap())
173-
.getOutput(inputFile: input, outputType: outputType)
174-
commandLine.append(.path(path))
175-
outputs.append(TypedVirtualPath(file: path, type: outputType))
176-
}
177-
178-
for input in primaryInputs {
179-
if moduleOutput != nil && !forceEmitModuleInSingleInvocation {
180-
addOutputsOfType(outputType: .swiftModule, input: input.file, flag: "-emit-module-path")
182+
// Compute the output path based on the input path (if there is one), or
183+
// use the final output.
184+
let outputPath: VirtualPath
185+
if let input = input {
186+
outputPath = (outputFileMap ?? OutputFileMap())
187+
.getOutput(inputFile: input, outputType: outputType)
188+
} else {
189+
outputPath = finalOutputPath
181190
}
182191

183-
if moduleDocOutputPath != nil && !forceEmitModuleInSingleInvocation {
184-
addOutputsOfType(outputType: .swiftDocumentation, input: input.file, flag: "-emit-module-doc-path")
185-
}
192+
commandLine.append(.path(outputPath))
193+
outputs.append(TypedVirtualPath(file: outputPath, type: outputType))
194+
}
186195

187-
if dependenciesFilePath != nil && !forceEmitModuleInSingleInvocation {
188-
addOutputsOfType(outputType: .dependencies, input: input.file, flag: "-emit-dependencies-path")
196+
/// Add all of the outputs needed for a given input.
197+
func addAllOutputsFor(input: VirtualPath?) {
198+
if !forceEmitModuleInSingleInvocation {
199+
addOutputOfType(
200+
outputType: .swiftModule,
201+
finalOutputPath: moduleOutput?.outputPath,
202+
input: input,
203+
flag: "-emit-module-path")
204+
addOutputOfType(
205+
outputType: .swiftDocumentation,
206+
finalOutputPath: moduleDocOutputPath,
207+
input: input,
208+
flag: "-emit-module-doc-path")
209+
addOutputOfType(
210+
outputType: .dependencies,
211+
finalOutputPath: dependenciesFilePath,
212+
input: input,
213+
flag: "-emit-dependencies-path")
189214
}
190215

191-
if optimizationRecordPath != nil {
192-
addOutputsOfType(outputType: .optimizationRecord, input: input.file, flag: "-save-optimization-record-path")
193-
}
216+
addOutputOfType(
217+
outputType: .optimizationRecord,
218+
finalOutputPath: optimizationRecordPath,
219+
input: input,
220+
flag: "-save-optimization-record-path")
194221

195222
#if false
196223
// FIXME: handle -update-code
197-
addOutputsOfType(outputType: .remap, input: input.file, flag: "-emit-remap-file-path")
224+
addOutputOfType(outputType: .remap, input: input.file, flag: "-emit-remap-file-path")
198225
#endif
199226
}
200227

228+
if compilerMode.usesPrimaryFileInputs {
229+
for input in primaryInputs {
230+
addAllOutputsFor(input: input.file)
231+
}
232+
} else {
233+
addAllOutputsFor(input: nil)
234+
}
235+
201236
return outputs
202237
}
203238
}

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,33 @@ extension Driver {
4040
}
4141

4242
let partitions: BatchPartitions?
43-
if case let .batchCompile(batchInfo) = compilerMode {
43+
switch compilerMode {
44+
case .batchCompile(let batchInfo):
4445
partitions = batchPartitions(batchInfo)
45-
} else {
46+
47+
case .immediate, .repl:
48+
fatalError("immediate and REPL modes are currently unsupported")
49+
50+
case .singleCompile:
51+
// Create a single compile job for all of the files, none of which
52+
// are primary.
53+
var jobOutputs: [TypedVirtualPath] = []
54+
let job = try compileJob(primaryInputs: [], outputType: compilerOutputType, allOutputs: &jobOutputs)
55+
jobs.append(job)
56+
addJobOutputs(jobOutputs)
57+
58+
partitions = nil
59+
60+
case .standardCompile:
4661
partitions = nil
4762
}
4863

4964
for input in inputFiles {
5065
switch input.type {
5166
case .swift, .sil, .sib:
67+
// Generate a compile job for primary inputs here.
68+
guard compilerMode.usesPrimaryFileInputs else { break }
69+
5270
var primaryInputs: [TypedVirtualPath]
5371
if let partitions = partitions, let partitionIdx = partitions.assignment[input] {
5472
// We have a partitioning for batch mode. If this input file isn't the first
@@ -94,7 +112,7 @@ extension Driver {
94112
}
95113

96114
// Plan the merge-module job, if there are module inputs.
97-
if moduleOutput != nil && !moduleInputs.isEmpty {
115+
if moduleOutput != nil && !moduleInputs.isEmpty && compilerMode.usesPrimaryFileInputs {
98116
jobs.append(try mergeModuleJob(inputs: moduleInputs))
99117
}
100118

@@ -126,10 +144,10 @@ extension Driver {
126144
public mutating func planBuild() throws -> [Job] {
127145
// Plan the build.
128146
switch compilerMode {
129-
case .immediate, .repl, .singleCompile:
147+
case .immediate, .repl:
130148
fatalError("Not yet supported")
131149

132-
case .standardCompile, .batchCompile:
150+
case .standardCompile, .batchCompile, .singleCompile:
133151
return try planStandardCompile()
134152
}
135153
}

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,33 @@ final class SwiftDriverTests: XCTestCase {
721721
XCTAssertEqual(plannedJobs[3].outputs.first!.file, VirtualPath.relative(RelativePath("Test")))
722722
}
723723

724+
func testSingleThreadedWholeModuleOptimizationCompiles() throws {
725+
var driver1 = try Driver(args: ["swiftc", "-whole-module-optimization", "foo.swift", "bar.swift", "-module-name", "Test", "-target", "x86_64-apple-macosx10.15"])
726+
let plannedJobs = try driver1.planBuild()
727+
XCTAssertEqual(plannedJobs.count, 2)
728+
XCTAssertEqual(plannedJobs[0].kind, .compile)
729+
XCTAssertEqual(plannedJobs[0].outputs.count, 1)
730+
XCTAssertEqual(plannedJobs[0].outputs.first!.file, VirtualPath.temporary(RelativePath("Test.o")))
731+
XCTAssert(!plannedJobs[0].commandLine.contains(.flag("-primary-file")))
732+
733+
XCTAssertEqual(plannedJobs[1].kind, .link)
734+
}
735+
736+
func testMultiThreadedWholeModuleOptimizationCompiles() throws {
737+
var driver1 = try Driver(args: ["swiftc", "-whole-module-optimization", "foo.swift", "bar.swift", "wibble.swift", "-module-name", "Test",
738+
"-num-threads", "4", "-target", "x86_64-apple-macosx10.15"])
739+
let plannedJobs = try driver1.planBuild()
740+
XCTAssertEqual(plannedJobs.count, 2)
741+
XCTAssertEqual(plannedJobs[0].kind, .compile)
742+
XCTAssertEqual(plannedJobs[0].outputs.count, 3)
743+
XCTAssertEqual(plannedJobs[0].outputs[0].file, VirtualPath.temporary(RelativePath("foo.o")))
744+
XCTAssertEqual(plannedJobs[0].outputs[1].file, VirtualPath.temporary(RelativePath("bar.o")))
745+
XCTAssertEqual(plannedJobs[0].outputs[2].file, VirtualPath.temporary(RelativePath("wibble.o")))
746+
XCTAssert(!plannedJobs[0].commandLine.contains(.flag("-primary-file")))
747+
748+
XCTAssertEqual(plannedJobs[1].kind, .link)
749+
}
750+
724751
func testMergeModulesOnly() throws {
725752
do {
726753
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module", "-import-objc-header", "TestInputHeader.h", "-emit-dependencies", "-emit-module-doc-path", "/foo/bar/Test.swiftdoc"])

0 commit comments

Comments
 (0)