Skip to content

Commit 813f483

Browse files
committed
Update parallel compilation to handle optimization correctly.
Explicitly disable cross-module optimization in `exec` configs so that Swift tools can be built in parallel to speed up critical build paths. Cherry-pic: d1ecc8a
1 parent df0d18b commit 813f483

File tree

9 files changed

+146
-42
lines changed

9 files changed

+146
-42
lines changed

swift/internal/compiling.bzl

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@ load(
4848
"SWIFT_FEATURE_EMIT_SWIFTDOC",
4949
"SWIFT_FEATURE_EMIT_SWIFTINTERFACE",
5050
"SWIFT_FEATURE_FULL_LTO",
51+
"SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION",
5152
"SWIFT_FEATURE_HEADERS_ALWAYS_ACTION_INPUTS",
5253
"SWIFT_FEATURE_INDEX_WHILE_BUILDING",
5354
"SWIFT_FEATURE_MODULAR_INDEXING",
5455
"SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD",
5556
"SWIFT_FEATURE_NO_GENERATED_MODULE_MAP",
5657
"SWIFT_FEATURE_OPT",
58+
"SWIFT_FEATURE_OPT_USES_CMO",
5759
"SWIFT_FEATURE_OPT_USES_WMO",
5860
"SWIFT_FEATURE_PROPAGATE_GENERATED_MODULE_MAP",
5961
"SWIFT_FEATURE_SPLIT_DERIVED_FILES_GENERATION",
@@ -765,25 +767,64 @@ def _should_plan_parallel_compilation(
765767
feature_configuration,
766768
user_compile_flags):
767769
"""Returns `True` if the compilation should be done in parallel."""
768-
parallel_requested = is_feature_enabled(
770+
if not is_feature_enabled(
769771
feature_configuration = feature_configuration,
770772
feature_name = SWIFT_FEATURE_COMPILE_IN_PARALLEL,
771-
)
773+
):
774+
return False
772775

773776
# TODO: are we able to support split derived file generation in parallel, this feature is not in upstream.
774777
# For now, force non-parallel compilation when split derived file generation is enabled.
775-
split_derived_file_generation = is_feature_enabled(
778+
if is_feature_enabled(
776779
feature_configuration = feature_configuration,
777780
feature_name = SWIFT_FEATURE_SPLIT_DERIVED_FILES_GENERATION,
778-
)
779-
780-
# The Swift driver will not emit separate jobs to compile the module and to
781-
# perform codegen if optimization is requested. See
781+
):
782+
return False
783+
784+
# When the Swift driver plans a compilation, the default behavior is to emit
785+
# separate frontend jobs to emit the module and to perform codegen. However,
786+
# this will *not* happen if cross-module optimization is possible; in that
787+
# case, the driver emits a single frontend job to compile everything. If any
788+
# of the following conditions is true, then cross-module optimization is not
789+
# possible and we can plan parallel compilation:
790+
#
791+
# - Whole-module optimization is not enabled.
792+
# - Library evolution is enabled.
793+
# - Cross-module optimization has been explicitly disabled.
794+
# - Optimization (via the `-O` flag group) has not been requested.
795+
#
796+
# This logic mirrors that defined in
782797
# https://github.com/swiftlang/swift-driver/blob/c647e91574122f2b104d294ab1ec5baadaa1aa95/Sources/SwiftDriver/Jobs/EmitModuleJob.swift#L156-L181.
783-
opt_requested = is_optimization_manually_requested(
784-
user_compile_flags = user_compile_flags,
798+
if not (
799+
is_wmo_manually_requested(
800+
user_compile_flags = user_compile_flags,
801+
) or are_all_features_enabled(
802+
feature_configuration = feature_configuration,
803+
feature_names = [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_WMO],
804+
)
805+
):
806+
return True
807+
808+
if is_feature_enabled(
809+
feature_configuration = feature_configuration,
810+
feature_name = SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION,
811+
):
812+
return True
813+
814+
if not is_feature_enabled(
815+
feature_configuration = feature_configuration,
816+
feature_name = SWIFT_FEATURE_OPT_USES_CMO,
817+
):
818+
return True
819+
820+
return (
821+
not is_optimization_manually_requested(
822+
user_compile_flags = user_compile_flags,
823+
) and not is_feature_enabled(
824+
feature_configuration = feature_configuration,
825+
feature_name = SWIFT_FEATURE_OPT,
826+
)
785827
)
786-
return parallel_requested and not opt_requested and not split_derived_file_generation
787828

788829
def _execute_compile_plan(
789830
actions,

swift/internal/feature_names.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@ SWIFT_FEATURE_OPT_USES_WMO = "swift.opt_uses_wmo"
173173
# the `-Osize` flag instead of `-O`.
174174
SWIFT_FEATURE_OPT_USES_OSIZE = "swift.opt_uses_osize"
175175

176+
# If enabled, compilations that are using whole-module optimization and also
177+
# request optimizations via the `-O` flag group will have cross-module
178+
# optimization performed. This is the default behavior for the Swift compiler
179+
# and for these build rules, with one exception: because CMO prevents
180+
# parallelized compilation, we unconditionally disable CMO for targets built in
181+
# an `exec` configuration under the presumption that build speed is more
182+
# important than runtime performance for build tools.
183+
SWIFT_FEATURE_OPT_USES_CMO = "swift.opt_uses_cmo"
184+
176185
# If enabled, and if the toolchain specifies a generated header rewriting tool,
177186
# that tool will be invoked after compilation to rewrite the generated header in
178187
# place.

swift/internal/features.bzl

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ load(
2020
":feature_names.bzl",
2121
"SWIFT_FEATURE_CACHEABLE_SWIFTMODULES",
2222
"SWIFT_FEATURE_CHECKED_EXCLUSIVITY",
23-
"SWIFT_FEATURE_COMPILE_IN_PARALLEL",
2423
"SWIFT_FEATURE_COVERAGE",
2524
"SWIFT_FEATURE_COVERAGE_PREFIX_MAP",
2625
"SWIFT_FEATURE_DEBUG_PREFIX_MAP",
@@ -40,6 +39,7 @@ load(
4039
"SWIFT_FEATURE_OPT_USES_WMO",
4140
"SWIFT_FEATURE_REMAP_XCODE_PATH",
4241
"SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE",
42+
"SWIFT_FEATURE_OPT_USES_CMO",
4343
"SWIFT_FEATURE__SUPPORTS_V6",
4444
)
4545
load(":package_specs.bzl", "label_matches_package_specs")
@@ -137,6 +137,19 @@ def configure_features(
137137
unsupported_features = unsupported_features,
138138
)
139139

140+
# Disable CMO for builds in `exec` configurations so that we can use
141+
# parallelized compilation for build tools written in Swift. The speedup
142+
# from parallelized compilation is significantly higher than the performance
143+
# loss from disabling CMO.
144+
#
145+
# HACK: There is no supported API yet to detect whether a build is in an
146+
# `exec` configuration. https://github.com/bazelbuild/bazel/issues/14444.
147+
if "-exec" in ctx.bin_dir.path or "/host/" in ctx.bin_dir.path:
148+
unsupported_features.append(SWIFT_FEATURE_OPT_USES_CMO)
149+
else:
150+
requested_features = list(requested_features)
151+
requested_features.append(SWIFT_FEATURE_OPT_USES_CMO)
152+
140153
all_requestable_features, all_unsupported_features = _compute_features(
141154
label = ctx.label,
142155
requested_features = requested_features,
@@ -150,6 +163,7 @@ def configure_features(
150163
requested_features = all_requestable_features,
151164
unsupported_features = all_unsupported_features,
152165
)
166+
153167
return struct(
154168
_cc_feature_configuration = cc_feature_configuration,
155169
_enabled_features = all_requestable_features,
@@ -193,12 +207,6 @@ def features_for_build_modes(ctx, cpp_fragment = None):
193207
requested_features.append("swift.{}".format(compilation_mode))
194208
if compilation_mode in ("dbg", "fastbuild"):
195209
requested_features.append(SWIFT_FEATURE_ENABLE_TESTING)
196-
elif compilation_mode == "opt":
197-
# Disable parallel compilation early if we know we're going to be doing
198-
# an optimized compile, because the driver will not emit separate jobs
199-
# unless we also explicitly disable cross-module optimization. See
200-
# https://github.com/swiftlang/swift-driver/blob/c647e91574122f2b104d294ab1ec5baadaa1aa95/Sources/SwiftDriver/Jobs/EmitModuleJob.swift#L156-L181.
201-
unsupported_features.append(SWIFT_FEATURE_COMPILE_IN_PARALLEL)
202210

203211
if ctx.configuration.coverage_enabled:
204212
requested_features.append(SWIFT_FEATURE_COVERAGE)

swift/internal/optimization.bzl

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
load(
1818
":feature_names.bzl",
19-
"SWIFT_FEATURE_COMPILE_IN_PARALLEL",
2019
"SWIFT_FEATURE__NUM_THREADS_0_IN_SWIFTCOPTS",
2120
"SWIFT_FEATURE__WMO_IN_SWIFTCOPTS",
2221
)
@@ -113,13 +112,6 @@ def optimization_features_from_swiftcopts(swiftcopts):
113112
requested_features = []
114113
unsupported_features = []
115114

116-
if is_optimization_manually_requested(user_compile_flags = swiftcopts):
117-
# Disable parallel compilation early if we know we're going to be doing
118-
# an optimized compile, because the driver will not emit separate jobs
119-
# unless we also explicitly disable cross-module optimization. See
120-
# https://github.com/swiftlang/swift-driver/blob/c647e91574122f2b104d294ab1ec5baadaa1aa95/Sources/SwiftDriver/Jobs/EmitModuleJob.swift#L156-L181.
121-
unsupported_features.append(SWIFT_FEATURE_COMPILE_IN_PARALLEL)
122-
123115
if is_wmo_manually_requested(user_compile_flags = swiftcopts):
124116
requested_features.append(SWIFT_FEATURE__WMO_IN_SWIFTCOPTS)
125117
if find_num_threads_flag_value(user_compile_flags = swiftcopts) == 1:

swift/toolchains/config/compile_config.bzl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ load(
7272
"SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD",
7373
"SWIFT_FEATURE_NO_ASAN_VERSION_CHECK",
7474
"SWIFT_FEATURE_OPT",
75+
"SWIFT_FEATURE_OPT_USES_CMO",
7576
"SWIFT_FEATURE_OPT_USES_OSIZE",
7677
"SWIFT_FEATURE_OPT_USES_WMO",
7778
"SWIFT_FEATURE_REWRITE_GENERATED_HEADER",
@@ -366,6 +367,11 @@ def compile_action_configs(
366367
configurators = [add_arg("-Osize")],
367368
features = [SWIFT_FEATURE_OPT, SWIFT_FEATURE_OPT_USES_OSIZE],
368369
),
370+
ActionConfigInfo(
371+
actions = all_compile_action_names(),
372+
configurators = [add_arg("-disable-cmo")],
373+
not_features = [SWIFT_FEATURE_OPT_USES_CMO],
374+
),
369375

370376
# If the `swift.opt_uses_wmo` feature is enabled, opt builds should also
371377
# automatically imply whole-module optimization.

test/debug_settings_tests.bzl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,6 @@ def debug_settings_test_suite(name, tags = []):
238238
"-g",
239239
"-gline-tables-only",
240240
],
241-
# In optimized mode, the driver still uses a single invocation for both
242-
# the module and for codegen.
243241
mnemonic = "SwiftCompile",
244242
tags = all_tags,
245243
target_under_test = "//test/fixtures/debug_settings:simple",

test/features_tests.bzl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,7 @@ def features_test_suite(name, tags = []):
213213
tags = all_tags,
214214
expected_argv = ["-emit-object", "-O"],
215215
not_expected_argv = ["-whole-module-optimization"],
216-
# In optimized mode, the driver still uses a single invocation for both
217-
# the module and for codegen.
218-
mnemonic = "SwiftCompile",
216+
mnemonic = "SwiftCompileModule",
219217
target_under_test = "//test/fixtures/debug_settings:simple",
220218
)
221219

test/fixtures/parallel_compilation/BUILD

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,34 @@ swift_library(
3434
)
3535

3636
swift_library(
37-
name = "with_opt_with_wmo",
37+
name = "with_opt_with_wmo_no_cmo",
3838
srcs = ["Empty.swift"],
3939
copts = [
4040
"-O",
4141
"-wmo",
4242
],
43+
features = ["-swift.opt_uses_cmo"],
44+
tags = FIXTURE_TAGS,
45+
)
46+
47+
swift_library(
48+
name = "with_opt_with_wmo_with_cmo",
49+
srcs = ["Empty.swift"],
50+
copts = [
51+
"-O",
52+
"-wmo",
53+
],
54+
tags = FIXTURE_TAGS,
55+
)
56+
57+
swift_library(
58+
name = "with_opt_with_wmo_with_library_evolution",
59+
srcs = ["Empty.swift"],
60+
copts = [
61+
"-O",
62+
"-wmo",
63+
],
64+
library_evolution = True,
4365
tags = FIXTURE_TAGS,
4466
)
4567

test/parallel_compilation_tests.bzl

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,17 @@ load("@bazel_skylib//rules:build_test.bzl", "build_test")
1818
load(
1919
"@build_bazel_rules_swift//test/rules:actions_created_test.bzl",
2020
"actions_created_test",
21+
"make_actions_created_test_rule",
2122
)
2223

2324
visibility("private")
2425

26+
opt_actions_create_test = make_actions_created_test_rule(
27+
config_settings = {
28+
"//command_line_option:compilation_mode": "opt",
29+
},
30+
)
31+
2532
def parallel_compilation_test_suite(name, tags = []):
2633
"""Test suite for parallel compilation.
2734
@@ -47,25 +54,46 @@ def parallel_compilation_test_suite(name, tags = []):
4754
target_under_test = "@build_bazel_rules_swift//test/fixtures/parallel_compilation:no_opt_with_wmo",
4855
)
4956

50-
# Optimized, non-WMO cannot be compiled in parallel.
51-
# TODO: b/351801556 - This is actually incorrect based on further driver
52-
# testing; update the rules to allow compiling these in parallel.
57+
# Optimized, non-WMO can be compiled in parallel.
5358
actions_created_test(
5459
name = "{}_with_opt_no_wmo".format(name),
55-
mnemonics = ["-SwiftCompileModule", "-SwiftCompileCodegen", "SwiftCompile"],
60+
mnemonics = ["SwiftCompileModule", "SwiftCompileCodegen", "-SwiftCompile"],
5661
tags = all_tags,
5762
target_under_test = "@build_bazel_rules_swift//test/fixtures/parallel_compilation:with_opt_no_wmo",
5863
)
5964

60-
# Optimized, with-WMO cannot be compiled in parallel.
61-
# TODO: b/351801556 - This should be allowed if cross-module-optimization is
62-
# disabled. Update the rules to allow this and add a new version of this
63-
# target that disables CMO so we can test both situtations.
65+
# Optimized, with-WMO can be compiled in parallel if CMO is also disabled.
66+
actions_created_test(
67+
name = "{}_with_opt_with_wmo_no_cmo".format(name),
68+
mnemonics = ["SwiftCompileModule", "SwiftCompileCodegen", "-SwiftCompile"],
69+
tags = all_tags,
70+
target_under_test = "@build_bazel_rules_swift//test/fixtures/parallel_compilation:with_opt_with_wmo_no_cmo",
71+
)
72+
73+
# Optimized, with-WMO cannot be compiled in parallel if CMO is enabled.
6474
actions_created_test(
65-
name = "{}_with_opt_with_wmo".format(name),
75+
name = "{}_with_opt_with_wmo_with_cmo".format(name),
6676
mnemonics = ["-SwiftCompileModule", "-SwiftCompileCodegen", "SwiftCompile"],
6777
tags = all_tags,
68-
target_under_test = "@build_bazel_rules_swift//test/fixtures/parallel_compilation:with_opt_with_wmo",
78+
target_under_test = "@build_bazel_rules_swift//test/fixtures/parallel_compilation:with_opt_with_wmo_with_cmo",
79+
)
80+
81+
# Force `-c opt` on a non-optimized, with-WMO target and make sure we don't
82+
# plan parallel compilation there.
83+
opt_actions_create_test(
84+
name = "{}_no_opt_with_wmo_but_compilation_mode_opt".format(name),
85+
mnemonics = ["-SwiftCompileModule", "-SwiftCompileCodegen", "SwiftCompile"],
86+
tags = all_tags,
87+
target_under_test = "@build_bazel_rules_swift//test/fixtures/parallel_compilation:no_opt_with_wmo",
88+
)
89+
90+
# Optimized, with-WMO can be compiled in parallel if library evolution is
91+
# enabled (which implicitly disables CMO).
92+
actions_created_test(
93+
name = "{}_with_opt_with_wmo_with_library_evolution".format(name),
94+
mnemonics = ["SwiftCompileModule", "SwiftCompileCodegen", "-SwiftCompile"],
95+
tags = all_tags,
96+
target_under_test = "@build_bazel_rules_swift//test/fixtures/parallel_compilation:with_opt_with_wmo_with_library_evolution",
6997
)
7098

7199
# Make sure that when we look for optimizer flags, we don't treat `-Onone`
@@ -85,7 +113,9 @@ def parallel_compilation_test_suite(name, tags = []):
85113
"@build_bazel_rules_swift//test/fixtures/parallel_compilation:no_opt_no_wmo",
86114
"@build_bazel_rules_swift//test/fixtures/parallel_compilation:no_opt_with_wmo",
87115
"@build_bazel_rules_swift//test/fixtures/parallel_compilation:with_opt_no_wmo",
88-
"@build_bazel_rules_swift//test/fixtures/parallel_compilation:with_opt_with_wmo",
116+
"@build_bazel_rules_swift//test/fixtures/parallel_compilation:with_opt_with_wmo_no_cmo",
117+
"@build_bazel_rules_swift//test/fixtures/parallel_compilation:with_opt_with_wmo_with_cmo",
118+
"@build_bazel_rules_swift//test/fixtures/parallel_compilation:with_opt_with_wmo_with_library_evolution",
89119
"@build_bazel_rules_swift//test/fixtures/parallel_compilation:onone_with_wmo",
90120
],
91121
tags = all_tags,

0 commit comments

Comments
 (0)