Skip to content

Commit 60e63d5

Browse files
authored
Merge pull request #23 from apple/wmo
Whole module optimization mode
2 parents 672de52 + cfa181b commit 60e63d5

File tree

10 files changed

+315
-79
lines changed

10 files changed

+315
-79
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ The goal of the new Swift driver is to provide a drop-in replacement for the exi
122122
* [x] Implement proper tokenization for response files
123123
* Compilation modes
124124
* [x] Batch mode
125-
* [ ] Whole-module-optimization mode
125+
* [x] Whole-module-optimization mode
126126
* [ ] REPL mode
127127
* [ ] Immediate mode
128128
* Features
@@ -132,6 +132,7 @@ The goal of the new Swift driver is to provide a drop-in replacement for the exi
132132
* [x] Parseable output, as used by SwiftPM
133133
* [x] Response files
134134
* [ ] Input and primary input file lists
135+
* [ ] Complete `OutputFileMap` implementation to handle all file types uniformly
135136
* Testing
136137
* [ ] Build stuff with SwiftPM or Xcode or your favorite build system, using `swift-driver`. Were the results identical? What changed?
137138
* [x] Shim in `swift-driver` so it can run the Swift repository's [driver test suite](https://github.com/apple/swift/tree/master/test/Driver).

Sources/SwiftDriver/Driver/CompilerMode.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ extension CompilerMode {
4747
return true
4848
}
4949
}
50+
51+
/// Whether this compilation mode compiles the whole target in one job.
52+
public var isSingleCompilation: Bool {
53+
switch self {
54+
case .immediate, .repl, .standardCompile, .batchCompile:
55+
return false
56+
57+
case .singleCompile:
58+
return true
59+
}
60+
}
5061
}
5162

5263
extension CompilerMode: CustomStringConvertible {

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -284,16 +284,70 @@ public struct Driver {
284284
self.enabledSanitizers = try Self.parseSanitizerArgValues(&parsedOptions, diagnosticEngine: diagnosticEngine, toolchain: toolchain, targetTriple: targetTriple)
285285

286286
// Supplemental outputs.
287-
self.dependenciesFilePath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .dependencies, isOutput: .emitDependencies, outputPath: .emitDependenciesPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
288-
self.referenceDependenciesFilePath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .swiftDeps, isOutput: .emitReferenceDependencies, outputPath: .emitReferenceDependenciesPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
289-
self.serializedDiagnosticsFilePath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .diagnostics, isOutput: .serializeDiagnostics, outputPath: .serializeDiagnosticsPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
287+
self.dependenciesFilePath = try Self.computeSupplementaryOutputPath(
288+
&parsedOptions, type: .dependencies, isOutput: .emitDependencies,
289+
outputPath: .emitDependenciesPath,
290+
compilerOutputType: compilerOutputType,
291+
compilerMode: compilerMode,
292+
outputFileMap: self.outputFileMap,
293+
moduleName: moduleName)
294+
self.referenceDependenciesFilePath = try Self.computeSupplementaryOutputPath(
295+
&parsedOptions, type: .swiftDeps, isOutput: .emitReferenceDependencies,
296+
outputPath: .emitReferenceDependenciesPath,
297+
compilerOutputType: compilerOutputType,
298+
compilerMode: compilerMode,
299+
outputFileMap: self.outputFileMap,
300+
moduleName: moduleName)
301+
self.serializedDiagnosticsFilePath = try Self.computeSupplementaryOutputPath(
302+
&parsedOptions, type: .diagnostics, isOutput: .serializeDiagnostics,
303+
outputPath: .serializeDiagnosticsPath,
304+
compilerOutputType: compilerOutputType,
305+
compilerMode: compilerMode,
306+
outputFileMap: self.outputFileMap,
307+
moduleName: moduleName)
290308
// FIXME: -fixits-output-path
291-
self.objcGeneratedHeaderPath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .objcHeader, isOutput: .emitObjcHeader, outputPath: .emitObjcHeaderPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
292-
self.loadedModuleTracePath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .moduleTrace, isOutput: .emitLoadedModuleTrace, outputPath: .emitLoadedModuleTracePath, compilerOutputType: compilerOutputType, moduleName: moduleName)
293-
self.tbdPath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .tbd, isOutput: .emitTbd, outputPath: .emitTbdPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
294-
self.moduleDocOutputPath = try Self.computeModuleDocOutputPath(&parsedOptions, moduleOutputPath: self.moduleOutput?.outputPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
295-
self.swiftInterfacePath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .swiftInterface, isOutput: .emitModuleInterface, outputPath: .emitModuleInterfacePath, compilerOutputType: compilerOutputType, moduleName: moduleName)
296-
self.optimizationRecordPath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .optimizationRecord, isOutput: .saveOptimizationRecord, outputPath: .saveOptimizationRecordPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
309+
self.objcGeneratedHeaderPath = try Self.computeSupplementaryOutputPath(
310+
&parsedOptions, type: .objcHeader, isOutput: .emitObjcHeader,
311+
outputPath: .emitObjcHeaderPath,
312+
compilerOutputType: compilerOutputType,
313+
compilerMode: compilerMode,
314+
outputFileMap: self.outputFileMap,
315+
moduleName: moduleName)
316+
self.loadedModuleTracePath = try Self.computeSupplementaryOutputPath(
317+
&parsedOptions, type: .moduleTrace, isOutput: .emitLoadedModuleTrace,
318+
outputPath: .emitLoadedModuleTracePath,
319+
compilerOutputType: compilerOutputType,
320+
compilerMode: compilerMode,
321+
outputFileMap: self.outputFileMap,
322+
moduleName: moduleName)
323+
self.tbdPath = try Self.computeSupplementaryOutputPath(
324+
&parsedOptions, type: .tbd, isOutput: .emitTbd,
325+
outputPath: .emitTbdPath,
326+
compilerOutputType: compilerOutputType,
327+
compilerMode: compilerMode,
328+
outputFileMap: self.outputFileMap,
329+
moduleName: moduleName)
330+
self.moduleDocOutputPath = try Self.computeModuleDocOutputPath(
331+
&parsedOptions, moduleOutputPath: self.moduleOutput?.outputPath,
332+
compilerOutputType: compilerOutputType,
333+
compilerMode: compilerMode,
334+
outputFileMap: self.outputFileMap,
335+
moduleName: moduleName)
336+
self.swiftInterfacePath = try Self.computeSupplementaryOutputPath(
337+
&parsedOptions, type: .swiftInterface, isOutput: .emitModuleInterface,
338+
outputPath: .emitModuleInterfacePath,
339+
compilerOutputType: compilerOutputType,
340+
compilerMode: compilerMode,
341+
outputFileMap: self.outputFileMap,
342+
moduleName: moduleName)
343+
self.optimizationRecordPath = try Self.computeSupplementaryOutputPath(
344+
&parsedOptions, type: .optimizationRecord,
345+
isOutput: .saveOptimizationRecord,
346+
outputPath: .saveOptimizationRecordPath,
347+
compilerOutputType: compilerOutputType,
348+
compilerMode: compilerMode,
349+
outputFileMap: self.outputFileMap,
350+
moduleName: moduleName)
297351
}
298352
}
299353

@@ -1249,11 +1303,11 @@ extension Driver {
12491303
isOutput: Option?,
12501304
outputPath: Option,
12511305
compilerOutputType: FileType?,
1306+
compilerMode: CompilerMode,
1307+
outputFileMap: OutputFileMap?,
12521308
moduleName: String,
12531309
patternOutputFile: VirtualPath? = nil
12541310
) throws -> VirtualPath? {
1255-
// FIXME: Do we need to check the output file map?
1256-
12571311
// If there is an explicit argument for the output path, use that
12581312
if let outputPathArg = parsedOptions.getLastArgument(outputPath) {
12591313
// Consume the isOutput argument
@@ -1268,6 +1322,14 @@ extension Driver {
12681322
return nil
12691323
}
12701324

1325+
// If this is a single-file compile and there is an entry in the
1326+
// output file map, use that.
1327+
if compilerMode.isSingleCompilation,
1328+
let singleOutputPath = outputFileMap?.existingOutputForSingleInput(
1329+
outputType: type) {
1330+
return singleOutputPath
1331+
}
1332+
12711333
// If there is an output argument, derive the name from there.
12721334
if let outputPathArg = parsedOptions.getLastArgument(.o) {
12731335
let path = try VirtualPath(path: outputPathArg.asSingle)
@@ -1288,10 +1350,10 @@ extension Driver {
12881350
_ parsedOptions: inout ParsedOptions,
12891351
moduleOutputPath: VirtualPath?,
12901352
compilerOutputType: FileType?,
1353+
compilerMode: CompilerMode,
1354+
outputFileMap: OutputFileMap?,
12911355
moduleName: String
12921356
) throws -> VirtualPath? {
1293-
// FIXME: Do we need to check the output file map?
1294-
12951357
// If there is an explicit argument for the output path, use that
12961358
if let outputPathArg = parsedOptions.getLastArgument(.emitModuleDocPath) {
12971359
// Consume -emit-module-doc if it's there.
@@ -1300,6 +1362,14 @@ extension Driver {
13001362
return try VirtualPath(path: outputPathArg.asSingle)
13011363
}
13021364

1365+
// If this is a single-file compile and there is an entry in the
1366+
// output file map, use that.
1367+
if compilerMode.isSingleCompilation,
1368+
let singleOutputPath = outputFileMap?.existingOutputForSingleInput(
1369+
outputType: .swiftDocumentation) {
1370+
return singleOutputPath
1371+
}
1372+
13031373
// If there's a known module output path, put the .swiftdoc file next
13041374
// to it.
13051375
if let moduleOutputPath = moduleOutputPath {

Sources/SwiftDriver/Driver/OutputFileMap.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,17 @@ fileprivate struct OutputFileMapJSON: Codable {
113113
case dependencies
114114
case object
115115
case swiftmodule
116+
case swiftinterface
116117
case swiftDependencies = "swift-dependencies"
118+
case diagnostics
117119
}
118120

119121
let dependencies: String?
120122
let object: String?
121123
let swiftmodule: String?
124+
let swiftinterface: String?
122125
let swiftDependencies: String?
126+
let diagnostics: String?
123127
}
124128

125129
/// The parsed entires
@@ -146,7 +150,9 @@ fileprivate struct OutputFileMapJSON: Codable {
146150
map[.dependencies] = entry.dependencies
147151
map[.object] = entry.object
148152
map[.swiftModule] = entry.swiftmodule
153+
map[.swiftInterface] = entry.swiftinterface
149154
map[.swiftDeps] = entry.swiftDependencies
155+
map[.diagnostics] = entry.diagnostics
150156

151157
result[input] = try map.mapValues(VirtualPath.init(path:))
152158
}
@@ -168,7 +174,9 @@ fileprivate struct OutputFileMapJSON: Codable {
168174
dependencies: outputs[.dependencies]?.name,
169175
object: outputs[.object]?.name,
170176
swiftmodule: outputs[.swiftModule]?.name,
171-
swiftDependencies: outputs[.swiftDeps]?.name)
177+
swiftinterface: outputs[.swiftInterface]?.name,
178+
swiftDependencies: outputs[.swiftDeps]?.name,
179+
diagnostics: outputs[.diagnostics]?.name)
172180
}
173181
return Self(entries: Dictionary(uniqueKeysWithValues: entries.map(convert(entry:))))
174182
}

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: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,37 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212
extension Driver {
13+
/// Add options that are common to command lines that emit modules, e.g.,
14+
/// options for the paths of various module files.
15+
mutating func addCommonModuleOptions(
16+
commandLine: inout [Job.ArgTemplate],
17+
outputs: inout [TypedVirtualPath]
18+
) throws {
19+
// Add supplemental outputs.
20+
func addSupplementalOutput(path: VirtualPath?, flag: String, type: FileType) {
21+
guard let path = path else { return }
22+
23+
commandLine.appendFlag(flag)
24+
commandLine.appendPath(path)
25+
outputs.append(.init(file: path, type: type))
26+
}
27+
28+
addSupplementalOutput(path: moduleDocOutputPath, flag: "-emit-module-doc-path", type: .swiftDocumentation)
29+
addSupplementalOutput(path: swiftInterfacePath, flag: "-emit-module-interface-path", type: .swiftInterface)
30+
addSupplementalOutput(path: serializedDiagnosticsFilePath, flag: "-serialize-diagnostics-path", type: .diagnostics)
31+
addSupplementalOutput(path: objcGeneratedHeaderPath, flag: "-emit-objc-header-path", type: .objcHeader)
32+
addSupplementalOutput(path: tbdPath, flag: "-emit-tbd-path", type: .tbd)
33+
34+
if let dependenciesFilePath = dependenciesFilePath {
35+
var path = dependenciesFilePath
36+
// FIXME: Hack to workaround the fact that SwiftPM/Xcode don't pass this path right now.
37+
if parsedOptions.getLastArgument(.emitDependenciesPath) == nil {
38+
path = try moduleOutput!.outputPath.replacingExtension(with: .dependencies)
39+
}
40+
addSupplementalOutput(path: path, flag: "-emit-dependencies-path", type: .dependencies)
41+
}
42+
}
43+
1344
/// Form a job that emits a single module
1445
mutating func emitModuleJob() throws -> Job {
1546
let moduleOutputPath = moduleOutput!.outputPath
@@ -32,29 +63,7 @@ extension Driver {
3263
try addCommonFrontendOptions(commandLine: &commandLine)
3364
// FIXME: Add MSVC runtime library flags
3465

35-
// Add suppplementable outputs.
36-
func addSupplementalOutput(path: VirtualPath?, flag: String, type: FileType) {
37-
guard let path = path else { return }
38-
39-
commandLine.appendFlag(flag)
40-
commandLine.appendPath(path)
41-
outputs.append(.init(file: path, type: type))
42-
}
43-
44-
addSupplementalOutput(path: moduleDocOutputPath, flag: "-emit-module-doc-path", type: .swiftDocumentation)
45-
addSupplementalOutput(path: swiftInterfacePath, flag: "-emit-module-interface-path", type: .swiftInterface)
46-
addSupplementalOutput(path: serializedDiagnosticsFilePath, flag: "-serialize-diagnostics-path", type: .diagnostics)
47-
addSupplementalOutput(path: objcGeneratedHeaderPath, flag: "-emit-objc-header-path", type: .objcHeader)
48-
addSupplementalOutput(path: tbdPath, flag: "-emit-tbd-path", type: .tbd)
49-
50-
if let dependenciesFilePath = dependenciesFilePath {
51-
var path = dependenciesFilePath
52-
// FIXME: Hack to workaround the fact that SwiftPM/Xcode don't pass this path right now.
53-
if parsedOptions.getLastArgument(.emitDependenciesPath) == nil {
54-
path = try moduleOutputPath.replacingExtension(with: .dependencies)
55-
}
56-
addSupplementalOutput(path: path, flag: "-emit-dependencies-path", type: .dependencies)
57-
}
66+
try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs)
5867

5968
commandLine.appendFlag(.o)
6069
commandLine.appendPath(moduleOutputPath)

0 commit comments

Comments
 (0)