Skip to content

Commit 92c8136

Browse files
authored
Merge pull request #1019 from eeckstein/fix-cmo-builds
fix cross-module-optimization builds and add the option `-disable-cmo`
2 parents 4f3d37f + 5b4f5dd commit 92c8136

File tree

5 files changed

+62
-14
lines changed

5 files changed

+62
-14
lines changed

Sources/SwiftDriver/Jobs/CompileJob.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ extension Driver {
282282
try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs)
283283
// FIXME: MSVC runtime flags
284284

285+
addDisableCMOOption(commandLine: &commandLine)
286+
285287
if parsedOptions.hasArgument(.parseAsLibrary, .emitLibrary) {
286288
commandLine.appendFlag(.parseAsLibrary)
287289
}

Sources/SwiftDriver/Jobs/EmitModuleJob.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ extension Driver {
9797

9898
addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs, isMergeModule: false)
9999

100+
addDisableCMOOption(commandLine: &commandLine)
101+
100102
try commandLine.appendLast(.emitSymbolGraph, from: &parsedOptions)
101103
try commandLine.appendLast(.emitSymbolGraphDir, from: &parsedOptions)
102104
try commandLine.appendLast(.includeSpiSymbols, from: &parsedOptions)
@@ -143,6 +145,13 @@ extension Driver {
143145
default: true)
144146

145147
case .singleCompile:
148+
// Non library-evolution builds require a single job, because cross-module-optimization is enabled by default.
149+
if !parsedOptions.hasArgument(.enableLibraryEvolution),
150+
!parsedOptions.hasArgument(.disableCrossModuleOptimization),
151+
let opt = parsedOptions.getLast(in: .O), opt.option != .Onone {
152+
return false
153+
}
154+
146155
return parsedOptions.hasFlag(positive: .emitModuleSeparatelyWMO,
147156
negative: .noEmitModuleSeparatelyWMO,
148157
default: true) &&

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,14 @@ extension Driver {
300300
driver: self)
301301
}
302302

303+
/// Add options to disable cross-module-optimization.
304+
mutating func addDisableCMOOption(commandLine: inout [Job.ArgTemplate]) {
305+
if parsedOptions.hasArgument(.disableCrossModuleOptimization) {
306+
commandLine.appendFlag(.Xllvm)
307+
commandLine.appendFlag("-sil-disable-pass=cmo")
308+
}
309+
}
310+
303311
mutating func addFrontendSupplementaryOutputArguments(commandLine: inout [Job.ArgTemplate],
304312
primaryInputs: [TypedVirtualPath],
305313
inputsGeneratingCodeCount: Int,

Sources/SwiftOptions/Options.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extension Option {
6161
public static let continueBuildingAfterErrors: Option = Option("-continue-building-after-errors", .flag, attributes: [.frontend, .doesNotAffectIncrementalBuild], helpText: "Continue building, even after errors are encountered")
6262
public static let coveragePrefixMap: Option = Option("-coverage-prefix-map", .separate, attributes: [.frontend], metaVar: "<prefix=replacement>", helpText: "Remap source paths in coverage info")
6363
public static let CrossModuleOptimization: Option = Option("-cross-module-optimization", .flag, attributes: [.helpHidden, .frontend], helpText: "Perform cross-module optimization")
64+
public static let disableCrossModuleOptimization: Option = Option("-disable-cmo", .flag, attributes: [.helpHidden, .frontend], helpText: "Disable cross-module optimization")
6465
public static let crosscheckUnqualifiedLookup: Option = Option("-crosscheck-unqualified-lookup", .flag, attributes: [.frontend, .noDriver], helpText: "Compare legacy DeclContext- to ASTScope-based unqualified name lookup (for debugging)")
6566
public static let c: Option = Option("-c", .flag, alias: Option.emitObject, attributes: [.frontend, .noInteractive], group: .modes)
6667
public static let debugAssertAfterParse: Option = Option("-debug-assert-after-parse", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Force an assertion failure after parsing", group: .debugCrash)
@@ -704,6 +705,7 @@ extension Option {
704705
Option.continueBuildingAfterErrors,
705706
Option.coveragePrefixMap,
706707
Option.CrossModuleOptimization,
708+
Option.disableCrossModuleOptimization,
707709
Option.crosscheckUnqualifiedLookup,
708710
Option.c,
709711
Option.debugAssertAfterParse,

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2691,12 +2691,11 @@ final class SwiftDriverTests: XCTestCase {
26912691
func testEmitModuleSeparatelyWMO() throws {
26922692
var envVars = ProcessEnv.vars
26932693
envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false)
2694+
let root = try localFileSystem.currentWorkingDirectory.map { AbsolutePath("/foo/bar", relativeTo: $0) }
2695+
?? AbsolutePath(validating: "/foo/bar")
26942696

26952697
do {
2696-
let symbolGraphDir =
2697-
try localFileSystem.currentWorkingDirectory.map { AbsolutePath("/foo/bar", relativeTo: $0) }
2698-
?? AbsolutePath(validating: "/Foo/Bar")
2699-
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", rebase("Test.swiftmodule", at: symbolGraphDir), "-emit-symbol-graph", "-emit-symbol-graph-dir", symbolGraphDir.pathString, "-emit-library", "-target", "x86_64-apple-macosx10.15", "-wmo", "-emit-module-separately-wmo"],
2698+
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", rebase("Test.swiftmodule", at: root), "-emit-symbol-graph", "-emit-symbol-graph-dir", root.pathString, "-emit-library", "-target", "x86_64-apple-macosx10.15", "-wmo", "-emit-module-separately-wmo"],
27002699
env: envVars)
27012700

27022701
let abiFileCount = (driver.isFeatureSupported(.emit_abi_descriptor) && driver.targetTriple.isDarwin) ? 1 : 0
@@ -2715,21 +2714,18 @@ final class SwiftDriverTests: XCTestCase {
27152714
let emitModuleJob = plannedJobs.first(where: {$0.kind == .emitModule})!
27162715
XCTAssertTrue(emitModuleJob.tool.name.contains("swift"))
27172716
XCTAssertEqual(emitModuleJob.outputs.count, 3 + abiFileCount)
2718-
XCTAssertEqual(1, emitModuleJob.outputs.filter({$0.file == .absolute(AbsolutePath(rebase("Test.swiftmodule", at: symbolGraphDir)))}).count)
2719-
XCTAssertEqual(1, emitModuleJob.outputs.filter({$0.file == .absolute(AbsolutePath(rebase("Test.swiftdoc", at: symbolGraphDir)))}).count)
2720-
XCTAssertEqual(1, emitModuleJob.outputs.filter({$0.file == .absolute(AbsolutePath(rebase("Test.swiftsourceinfo", at: symbolGraphDir)))}).count)
2717+
XCTAssertEqual(1, emitModuleJob.outputs.filter({$0.file == .absolute(AbsolutePath(rebase("Test.swiftmodule", at: root)))}).count)
2718+
XCTAssertEqual(1, emitModuleJob.outputs.filter({$0.file == .absolute(AbsolutePath(rebase("Test.swiftdoc", at: root)))}).count)
2719+
XCTAssertEqual(1, emitModuleJob.outputs.filter({$0.file == .absolute(AbsolutePath(rebase("Test.swiftsourceinfo", at: root)))}).count)
27212720
if abiFileCount == 1 {
2722-
XCTAssertEqual(abiFileCount, emitModuleJob.outputs.filter({$0.file == .absolute(AbsolutePath(rebase("Test.abi.json", at: symbolGraphDir)))}).count)
2721+
XCTAssertEqual(abiFileCount, emitModuleJob.outputs.filter({$0.file == .absolute(AbsolutePath(rebase("Test.abi.json", at: root)))}).count)
27232722
}
27242723

27252724
// We don't know the output file of the symbol graph, just make sure the flag is passed along.
27262725
XCTAssertTrue(emitModuleJob.commandLine.contains(.flag("-emit-symbol-graph-dir")))
27272726
}
27282727

27292728
do {
2730-
let root = try localFileSystem.currentWorkingDirectory.map { AbsolutePath("/foo/bar", relativeTo: $0) }
2731-
?? AbsolutePath(validating: "/foo/bar")
2732-
27332729
// Ignore the `-emit-module-separately-wmo` flag when building only the module files to avoid duplicating outputs.
27342730
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", rebase("Test.swiftmodule", at: root), "-wmo", "-emit-module-separately-wmo"])
27352731
let abiFileCount = (driver.isFeatureSupported(.emit_abi_descriptor) && driver.targetTriple.isDarwin) ? 1 : 0
@@ -2750,9 +2746,6 @@ final class SwiftDriverTests: XCTestCase {
27502746
}
27512747

27522748
do {
2753-
let root = try localFileSystem.currentWorkingDirectory.map { AbsolutePath("/foo/bar", relativeTo: $0) }
2754-
?? AbsolutePath(validating: "/foo/bar")
2755-
27562749
// Specifying -no-emit-module-separately-wmo doesn't schedule the separate emit-module job.
27572750
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", rebase("Test.swiftmodule", at: root), "-emit-library", "-wmo", "-emit-module-separately-wmo", "-no-emit-module-separately-wmo" ])
27582751
let abiFileCount = (driver.isFeatureSupported(.emit_abi_descriptor) && driver.targetTriple.isDarwin) ? 1 : 0
@@ -2777,6 +2770,40 @@ final class SwiftDriverTests: XCTestCase {
27772770
}
27782771
}
27792772

2773+
#if os(Linux) || os(Android)
2774+
let autoLinkExtractJob = 1
2775+
#else
2776+
let autoLinkExtractJob = 0
2777+
#endif
2778+
2779+
do {
2780+
// non library-evolution builds require a single job, because cross-module-optimization is enabled by default.
2781+
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", rebase("Test.swiftmodule", at: root), "-c", "-o", rebase("test.o", at: root), "-wmo", "-O" ])
2782+
let plannedJobs = try driver.planBuild()
2783+
XCTAssertEqual(plannedJobs.count, 1 + autoLinkExtractJob)
2784+
}
2785+
2786+
do {
2787+
// library-evolution builds can emit the module in a separate job.
2788+
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", rebase("Test.swiftmodule", at: root), "-c", "-o", rebase("test.o", at: root), "-wmo", "-O", "-enable-library-evolution" ])
2789+
let plannedJobs = try driver.planBuild()
2790+
XCTAssertEqual(plannedJobs.count, 2 + autoLinkExtractJob)
2791+
}
2792+
2793+
do {
2794+
// When disabling cross-module-optimization, the module can be emitted in a separate job.
2795+
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", rebase("Test.swiftmodule", at: root), "-c", "-o", rebase("test.o", at: root), "-wmo", "-O", "-disable-cmo" ])
2796+
let plannedJobs = try driver.planBuild()
2797+
XCTAssertEqual(plannedJobs.count, 2 + autoLinkExtractJob)
2798+
}
2799+
2800+
do {
2801+
// non optimized builds can emit the module in a separate job.
2802+
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "-module-name", "Test", "-emit-module-path", rebase("Test.swiftmodule", at: root), "-c", "-o", rebase("test.o", at: root), "-wmo" ])
2803+
let plannedJobs = try driver.planBuild()
2804+
XCTAssertEqual(plannedJobs.count, 2 + autoLinkExtractJob)
2805+
}
2806+
27802807
do {
27812808
// Don't use emit-module-separetely as a linker.
27822809
var driver = try Driver(args: ["swiftc", "foo.sil", "bar.sil", "-module-name", "Test", "-emit-module-path", "/foo/bar/Test.swiftmodule", "-emit-library", "-target", "x86_64-apple-macosx10.15", "-wmo", "-emit-module-separately-wmo"],

0 commit comments

Comments
 (0)