From 29f0ec28a3363fa99f05d04737a699c38fcce02b Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 13 Aug 2024 09:28:25 -0700 Subject: [PATCH] Disable parallel compilation if an optimization flag in the `-O` group is being used. Unless cross-module-optimization is disabled (which we don't really support at this time), the driver in this case will create a single frontend invocation that emits both the module and performs codegen. PiperOrigin-RevId: 662545520 --- swift/internal/BUILD | 19 +++-- swift/internal/compiling.bzl | 28 ++++++- swift/internal/features.bzl | 35 ++++++--- swift/internal/{wmo.bzl => optimization.bzl} | 78 ++++++++++++-------- swift/toolchains/BUILD | 4 +- swift/toolchains/config/BUILD | 1 + swift/toolchains/config/compile_config.bzl | 30 ++------ swift/toolchains/xcode_swift_toolchain.bzl | 33 ++++++--- 8 files changed, 136 insertions(+), 92 deletions(-) rename swift/internal/{wmo.bzl => optimization.bzl} (64%) diff --git a/swift/internal/BUILD b/swift/internal/BUILD index 79ef38ede..1fa1d1b0c 100644 --- a/swift/internal/BUILD +++ b/swift/internal/BUILD @@ -87,9 +87,9 @@ bzl_library( ":feature_names", ":features", ":module_maps", + ":optimization", ":toolchain_utils", ":utils", - ":wmo", "//swift:providers", "@bazel_skylib//lib:paths", "@bazel_skylib//lib:sets", @@ -146,6 +146,14 @@ bzl_library( deps = ["@bazel_skylib//lib:sets"], ) +bzl_library( + name = "optimization", + srcs = ["optimization.bzl"], + deps = [ + ":feature_names", + ], +) + bzl_library( name = "output_groups", srcs = ["output_groups.bzl"], @@ -190,7 +198,6 @@ bzl_library( "//swift:module_name", "//swift:providers", "@bazel_skylib//lib:dicts", - "@bazel_skylib//rules:common_settings", ], ) @@ -238,14 +245,6 @@ bzl_library( ], ) -bzl_library( - name = "wmo", - srcs = ["wmo.bzl"], - deps = [ - ":feature_names", - ], -) - # Consumed by Bazel integration tests. filegroup( name = "for_bazel_tests", diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 78b51ad80..4f247a62e 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -60,6 +60,12 @@ load( "warnings_as_errors_from_features", ) load(":module_maps.bzl", "write_module_map") +load( + ":optimization.bzl", + "find_num_threads_flag_value", + "is_optimization_manually_requested", + "is_wmo_manually_requested", +) load(":toolchain_utils.bzl", "SWIFT_TOOLCHAIN_TYPE") load( ":utils.bzl", @@ -69,7 +75,6 @@ load( "owner_relative_path", "struct_fields", ) -load(":wmo.bzl", "find_num_threads_flag_value", "is_wmo_manually_requested") visibility([ "@build_bazel_rules_swift//swift/...", @@ -493,9 +498,9 @@ def compile( "warnings_as_errors": warnings_as_errors, } | struct_fields(compile_outputs) - if is_feature_enabled( + if _should_plan_parallel_compilation( feature_configuration = feature_configuration, - feature_name = SWIFT_FEATURE_COMPILE_IN_PARALLEL, + user_compile_flags = copts, ): _execute_compile_plan( actions = actions, @@ -577,6 +582,23 @@ def compile( ), ) +def _should_plan_parallel_compilation( + feature_configuration, + user_compile_flags): + """Returns `True` if the compilation should be done in parallel.""" + parallel_requested = is_feature_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_COMPILE_IN_PARALLEL, + ) + + # The Swift driver will not emit separate jobs to compile the module and to + # perform codegen if optimization is requested. See + # https://github.com/swiftlang/swift-driver/blob/c647e91574122f2b104d294ab1ec5baadaa1aa95/Sources/SwiftDriver/Jobs/EmitModuleJob.swift#L156-L181. + opt_requested = is_optimization_manually_requested( + user_compile_flags = user_compile_flags, + ) + return parallel_requested and not opt_requested + def _execute_compile_plan( actions, compile_plan, diff --git a/swift/internal/features.bzl b/swift/internal/features.bzl index e23b2aa18..44ddf5f45 100644 --- a/swift/internal/features.bzl +++ b/swift/internal/features.bzl @@ -18,6 +18,7 @@ load("@bazel_skylib//lib:new_sets.bzl", "sets") load( ":feature_names.bzl", "SWIFT_FEATURE_CHECKED_EXCLUSIVITY", + "SWIFT_FEATURE_COMPILE_IN_PARALLEL", "SWIFT_FEATURE_COVERAGE", "SWIFT_FEATURE_DEBUG_PREFIX_MAP", "SWIFT_FEATURE_DISABLE_CLANG_SPI", @@ -158,7 +159,7 @@ def configure_features( ) def features_for_build_modes(ctx, cpp_fragment = None): - """Returns a list of Swift toolchain features for current build modes. + """Returns features to request and disable for current build modes. This function explicitly breaks the "don't pass `ctx` as an argument" rule-of-thumb because it is internal and only called from the toolchain @@ -166,21 +167,35 @@ def features_for_build_modes(ctx, cpp_fragment = None): Args: ctx: The current rule context. - cpp_fragment: The Cpp configuration fragment, if available. + cpp_fragment: The `cpp` configuration fragment, if available. Returns: - A list of Swift toolchain features to enable. + A tuple containing two lists: + + 1. A list of Swift toolchain features to requested (enable). + 2. A list of Swift toolchain features that are unsupported (disabled). """ + requested_features = [] + unsupported_features = [] + compilation_mode = ctx.var["COMPILATION_MODE"] - features = [] - features.append("swift.{}".format(compilation_mode)) - if ctx.configuration.coverage_enabled: - features.append(SWIFT_FEATURE_COVERAGE) + requested_features.append("swift.{}".format(compilation_mode)) if compilation_mode in ("dbg", "fastbuild"): - features.append(SWIFT_FEATURE_ENABLE_TESTING) + requested_features.append(SWIFT_FEATURE_ENABLE_TESTING) + elif compilation_mode == "opt": + # Disable parallel compilation early if we know we're going to be doing + # an optimized compile, because the driver will not emit separate jobs + # unless we also explicitly disable cross-module optimization. See + # https://github.com/swiftlang/swift-driver/blob/c647e91574122f2b104d294ab1ec5baadaa1aa95/Sources/SwiftDriver/Jobs/EmitModuleJob.swift#L156-L181. + unsupported_features.append(SWIFT_FEATURE_COMPILE_IN_PARALLEL) + + if ctx.configuration.coverage_enabled: + requested_features.append(SWIFT_FEATURE_COVERAGE) + if cpp_fragment and cpp_fragment.apple_generate_dsym: - features.append(SWIFT_FEATURE_FULL_DEBUG_INFO) - return features + requested_features.append(SWIFT_FEATURE_FULL_DEBUG_INFO) + + return requested_features, unsupported_features def get_cc_feature_configuration(feature_configuration): """Returns the C++ feature configuration in a Swift feature configuration. diff --git a/swift/internal/wmo.bzl b/swift/internal/optimization.bzl similarity index 64% rename from swift/internal/wmo.bzl rename to swift/internal/optimization.bzl index e51b5237e..a82c6a5c5 100644 --- a/swift/internal/wmo.bzl +++ b/swift/internal/optimization.bzl @@ -12,10 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Functionality releated to detecting whole-module optimization.""" +"""Detection of various optimization settings.""" load( ":feature_names.bzl", + "SWIFT_FEATURE_COMPILE_IN_PARALLEL", "SWIFT_FEATURE__NUM_THREADS_1_IN_SWIFTCOPTS", "SWIFT_FEATURE__WMO_IN_SWIFTCOPTS", ) @@ -32,29 +33,14 @@ _WMO_FLAGS = { "-force-single-frontend-invocation": True, } -def features_from_swiftcopts(swiftcopts): - """Returns a list of features to enable based on `--swiftcopt` flags. - - Since `--swiftcopt` flags are hooked into the action configuration when the - toolchain is configured, it's not possible for individual actions to query - them easily if those flags may determine the nature of outputs (for example, - single- vs. multi-threaded WMO). The toolchain can call this function to map - those flags to private features that can be queried instead. - - Args: - swiftcopts: The list of command line flags that were passed using - `--swiftcopt`. - - Returns: - A list (possibly empty) of strings denoting feature names that should be - enabled on the toolchain. - """ - features = [] - if is_wmo_manually_requested(user_compile_flags = swiftcopts): - features.append(SWIFT_FEATURE__WMO_IN_SWIFTCOPTS) - if find_num_threads_flag_value(user_compile_flags = swiftcopts) == 1: - features.append(SWIFT_FEATURE__NUM_THREADS_1_IN_SWIFTCOPTS) - return features +# Swift command line flags in the `O` group that enable some kind of +# optimization. This explicitly excludes `-Onone`. +_OPT_FLAGS = { + "-O": True, + "-Oplayground": True, + "-Osize": True, + "-Ounchecked": True, +} def find_num_threads_flag_value(user_compile_flags): """Finds the value of the `-num-threads` flag. @@ -79,6 +65,20 @@ def find_num_threads_flag_value(user_compile_flags): saw_num_threads = True return num_threads +def is_optimization_manually_requested(user_compile_flags): + """Returns `True` if some optimization flag is in the given list of flags. + + Args: + user_compile_flags: A list of compiler flags to scan for optimization. + + Returns: + True if some optimization is enabled in the given list of flags. + """ + for copt in user_compile_flags: + if copt in _OPT_FLAGS: + return True + return False + def is_wmo_manually_requested(user_compile_flags): """Returns `True` if a WMO flag is in the given list of compiler flags. @@ -93,8 +93,8 @@ def is_wmo_manually_requested(user_compile_flags): return True return False -def wmo_features_from_swiftcopts(swiftcopts): - """Returns a list of features to enable based on `--swiftcopt` flags. +def optimization_features_from_swiftcopts(swiftcopts): + """Returns features to enable or disable based on `--swiftcopt` flags. Since `--swiftcopt` flags are hooked into the action configuration when the toolchain is configured, it's not possible for individual actions to query @@ -107,15 +107,29 @@ def wmo_features_from_swiftcopts(swiftcopts): `--swiftcopt`. Returns: - A list (possibly empty) of strings denoting feature names that should be - enabled on the toolchain. + A tuple containing two lists: + + 1. A list (possibly empty) of strings denoting feature names that + should be enabled on the toolchain. + 2. A list (possibly empty) of strings denoting feature names that + should be disabled on the toolchain. """ - features = [] + requested_features = [] + unsupported_features = [] + + if is_optimization_manually_requested(user_compile_flags = swiftcopts): + # Disable parallel compilation early if we know we're going to be doing + # an optimized compile, because the driver will not emit separate jobs + # unless we also explicitly disable cross-module optimization. See + # https://github.com/swiftlang/swift-driver/blob/c647e91574122f2b104d294ab1ec5baadaa1aa95/Sources/SwiftDriver/Jobs/EmitModuleJob.swift#L156-L181. + unsupported_features.append(SWIFT_FEATURE_COMPILE_IN_PARALLEL) + if is_wmo_manually_requested(user_compile_flags = swiftcopts): - features.append(SWIFT_FEATURE__WMO_IN_SWIFTCOPTS) + requested_features.append(SWIFT_FEATURE__WMO_IN_SWIFTCOPTS) if find_num_threads_flag_value(user_compile_flags = swiftcopts) == 1: - features.append(SWIFT_FEATURE__NUM_THREADS_1_IN_SWIFTCOPTS) - return features + requested_features.append(SWIFT_FEATURE__NUM_THREADS_1_IN_SWIFTCOPTS) + + return requested_features, unsupported_features def _safe_int(s): """Returns the base-10 integer value of `s` or `None` if it is invalid. diff --git a/swift/toolchains/BUILD b/swift/toolchains/BUILD index fa6333527..bcefb4f09 100644 --- a/swift/toolchains/BUILD +++ b/swift/toolchains/BUILD @@ -14,10 +14,10 @@ bzl_library( "//swift/internal:action_names", "//swift/internal:feature_names", "//swift/internal:features", + "//swift/internal:optimization", "//swift/internal:providers", "//swift/internal:target_triples", "//swift/internal:utils", - "//swift/internal:wmo", "//swift/toolchains/config:action_config", "//swift/toolchains/config:all_actions_config", "//swift/toolchains/config:compile_config", @@ -40,10 +40,10 @@ bzl_library( "//swift/internal:attrs", "//swift/internal:feature_names", "//swift/internal:features", + "//swift/internal:optimization", "//swift/internal:providers", "//swift/internal:target_triples", "//swift/internal:utils", - "//swift/internal:wmo", "//swift/toolchains/config:action_config", "//swift/toolchains/config:all_actions_config", "//swift/toolchains/config:compile_config", diff --git a/swift/toolchains/config/BUILD b/swift/toolchains/config/BUILD index 5c28ba69b..ccc59230f 100644 --- a/swift/toolchains/config/BUILD +++ b/swift/toolchains/config/BUILD @@ -31,6 +31,7 @@ bzl_library( ":action_config", "//swift/internal:action_names", "//swift/internal:feature_names", + "//swift/internal:optimization", "@bazel_skylib//lib:paths", "@bazel_skylib//lib:types", ], diff --git a/swift/toolchains/config/compile_config.bzl b/swift/toolchains/config/compile_config.bzl index 21dcc4458..9a35adcff 100644 --- a/swift/toolchains/config/compile_config.bzl +++ b/swift/toolchains/config/compile_config.bzl @@ -65,6 +65,10 @@ load( "SWIFT_FEATURE__SUPPORTS_V6", "SWIFT_FEATURE__WMO_IN_SWIFTCOPTS", ) +load( + "@build_bazel_rules_swift//swift/internal:optimization.bzl", + "is_wmo_manually_requested", +) load(":action_config.bzl", "ActionConfigInfo", "ConfigResultInfo", "add_arg") visibility([ @@ -77,14 +81,6 @@ visibility([ # when an API to obtain this is available. _DEFAULT_WMO_THREAD_COUNT = 12 -# Swift command line flags that enable whole module optimization. (This -# dictionary is used as a set for quick lookup; the values are irrelevant.) -_WMO_FLAGS = { - "-wmo": True, - "-whole-module-optimization": True, - "-force-single-frontend-invocation": True, -} - def compile_action_configs( *, additional_objc_copts = [], @@ -1040,7 +1036,7 @@ def _global_module_cache_configurator(prerequisites, args): def _batch_mode_configurator(prerequisites, args): """Adds flags to enable batch compilation mode.""" - if not _is_wmo_manually_requested(prerequisites.user_compile_flags): + if not is_wmo_manually_requested(prerequisites.user_compile_flags): args.add("-enable-batch-mode") def _c_layering_check_configurator(prerequisites, args): @@ -1550,25 +1546,11 @@ def _make_wmo_thread_count_configurator(should_check_flags): return lambda _prerequisites, args: _add_num_threads(args) def _flag_checking_wmo_thread_count_configurator(prerequisites, args): - if _is_wmo_manually_requested(prerequisites.user_compile_flags): + if is_wmo_manually_requested(prerequisites.user_compile_flags): _add_num_threads(args) return _flag_checking_wmo_thread_count_configurator -def _is_wmo_manually_requested(user_compile_flags): - """Returns `True` if a WMO flag is in the given list of compiler flags. - - Args: - user_compile_flags: A list of compiler flags to scan for WMO usage. - - Returns: - True if WMO is enabled in the given list of flags. - """ - for copt in user_compile_flags: - if copt in _WMO_FLAGS: - return True - return False - def _exclude_swift_incompatible_define(define): """A `map_each` helper that excludes a define if it is not Swift-compatible. diff --git a/swift/toolchains/xcode_swift_toolchain.bzl b/swift/toolchains/xcode_swift_toolchain.bzl index 3f5ccb9af..9027f7eb2 100644 --- a/swift/toolchains/xcode_swift_toolchain.bzl +++ b/swift/toolchains/xcode_swift_toolchain.bzl @@ -54,6 +54,10 @@ load( "default_features_for_toolchain", "features_for_build_modes", ) +load( + "@build_bazel_rules_swift//swift/internal:optimization.bzl", + "optimization_features_from_swiftcopts", +) load( "@build_bazel_rules_swift//swift/internal:providers.bzl", "SwiftCrossImportOverlayInfo", @@ -69,10 +73,6 @@ load( "compact", "get_swift_executable_for_toolchain", ) -load( - "@build_bazel_rules_swift//swift/internal:wmo.bzl", - "wmo_features_from_swiftcopts", -) load( "@build_bazel_rules_swift//swift/toolchains/config:action_config.bzl", "ActionConfigInfo", @@ -639,10 +639,16 @@ def _xcode_swift_toolchain_impl(ctx): # Compute the default requested features and conditional ones based on Xcode # version. - requested_features = features_for_build_modes( - ctx, - cpp_fragment = cpp_fragment, - ) + wmo_features_from_swiftcopts(swiftcopts = swiftcopts) + requested_features, unsupported_features = ( + optimization_features_from_swiftcopts(swiftcopts = swiftcopts) + ) + build_mode_requested_features, build_mode_unsupported_features = ( + features_for_build_modes( + ctx, + cpp_fragment = cpp_fragment, + ) + ) + requested_features.extend(build_mode_requested_features) requested_features.extend(ctx.features) requested_features.extend(default_features_for_toolchain( ctx = ctx, @@ -663,6 +669,13 @@ def _xcode_swift_toolchain_impl(ctx): if _is_xcode_at_least_version(xcode_config, "999.0"): requested_features.append(SWIFT_FEATURE__SUPPORTS_V6) + unsupported_features.extend(build_mode_unsupported_features) + unsupported_features.extend(ctx.disabled_features) + + # `-fmodule-map-file-home-is-cwd` is incompatible with Apple's module maps, + # which we must use for system frameworks. + unsupported_features.append(SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD) + env = _xcode_env(target_triple = target_triple, xcode_config = xcode_config) execution_requirements = xcode_config.execution_info() @@ -739,9 +752,7 @@ def _xcode_swift_toolchain_impl(ctx): test_linking_contexts = [test_linking_context], ), tool_configs = all_tool_configs, - unsupported_features = ctx.disabled_features + [ - SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD, - ], + unsupported_features = unsupported_features, ) return [