Skip to content

Commit

Permalink
Disable parallel compilation if an optimization flag in the -O grou…
Browse files Browse the repository at this point in the history
…p 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
  • Loading branch information
allevato authored and swiple-rules-gardener committed Aug 13, 2024
1 parent 9a4bcc0 commit 29f0ec2
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 92 deletions.
19 changes: 9 additions & 10 deletions swift/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"],
Expand Down Expand Up @@ -190,7 +198,6 @@ bzl_library(
"//swift:module_name",
"//swift:providers",
"@bazel_skylib//lib:dicts",
"@bazel_skylib//rules:common_settings",
],
)

Expand Down Expand Up @@ -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",
Expand Down
28 changes: 25 additions & 3 deletions swift/internal/compiling.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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/...",
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
35 changes: 25 additions & 10 deletions swift/internal/features.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -158,29 +159,43 @@ 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
rules, so there is no concern about supporting differing call sites.
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.
Expand Down
78 changes: 46 additions & 32 deletions swift/internal/wmo.bzl → swift/internal/optimization.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
)
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions swift/toolchains/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
1 change: 1 addition & 0 deletions swift/toolchains/config/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
Expand Down
30 changes: 6 additions & 24 deletions swift/toolchains/config/compile_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand All @@ -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 = [],
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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.
Expand Down
Loading

0 comments on commit 29f0ec2

Please sign in to comment.