Skip to content

Commit 632d80e

Browse files
committed
[cherry-pick] Implement global index store cache
bazelbuild#567 swift and clang write index data based on the status of what resides in `index-store-path`. When using a transient per-swift-library index, it writes O( M imports * N libs) indexer data and slows down compilation significantly: to the tune of 300% slow down and 6GB+ index data in my testing. Bazel likes to use per `swift_library` data in order to remote cache them, which needs extra consideration to work with the design of swift and clang and preserve the performance This PR does 2 things to make index while building use the original model of the index-store so we don't have to patch clang and swift for this yet. When the flag `-Xwrapped-swift-enable-global-index-store` is set: 1. it writes to a global index cache, `bazel-out/global_index_store` 2. it copies indexstore data (records and units) to where Bazel expected them for caching: e.g. the original location with `swift.index_while_building` Note that while this uses a `index-import` binary, it is very different than how index-import currently works. In the variant this expects, it is program that can _copy_ index data for specific compiler outputs. This has the effect fo remote caching the subset that was used in compilation for this library. I added a detailed description of this feature in the RFC to add it there MobileNativeFoundation/index-import#53. In practice, transitive imports will be indexed by deps and if they aren't cached then the fallback is Xcode wil index transitive files if we set those as deps on the unit files
1 parent ed81c15 commit 632d80e

File tree

8 files changed

+149
-4
lines changed

8 files changed

+149
-4
lines changed

swift/internal/actions.bzl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616

1717
load("@bazel_skylib//lib:partial.bzl", "partial")
1818
load("@bazel_skylib//lib:types.bzl", "types")
19-
load(":features.bzl", "are_all_features_enabled")
19+
load(":feature_names.bzl", "SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2")
20+
load(":features.bzl", "are_all_features_enabled", "is_feature_enabled")
2021
load(":toolchain_config.bzl", "swift_toolchain_config")
2122

2223
# The names of actions currently supported by the Swift build rules.
@@ -218,6 +219,19 @@ def run_toolchain_action(
218219
):
219220
execution_requirements["supports-workers"] = "1"
220221

222+
# For index_while_building_v2 we import a subset of the indexed outputs
223+
# from the global index store cache into bazel-out. The program that
224+
# handles this is an external program
225+
if is_feature_enabled(
226+
feature_configuration = feature_configuration,
227+
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2,
228+
) and len(swift_toolchain.swift_worker_output_processor[DefaultInfo].files.to_list()):
229+
processor = swift_toolchain.swift_worker_output_processor[DefaultInfo].files.to_list()[0]
230+
tools.append(processor)
231+
args.add_all([
232+
"-Xwrapped-swift-output-processor",
233+
processor.path])
234+
221235
executable = swift_toolchain.swift_worker
222236
tool_executable_args.add(tool_config.executable)
223237
if not types.is_string(tool_config.executable):

swift/internal/compiling.bzl

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ load(
4545
"SWIFT_FEATURE_FULL_DEBUG_INFO",
4646
"SWIFT_FEATURE_IMPLICIT_MODULES",
4747
"SWIFT_FEATURE_INDEX_WHILE_BUILDING",
48+
"SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2",
4849
"SWIFT_FEATURE_MINIMAL_DEPS",
4950
"SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD",
5051
"SWIFT_FEATURE_NO_EMBED_DEBUG_MODULE",
@@ -1405,6 +1406,15 @@ def compile(
14051406
else:
14061407
vfsoverlay_file = None
14071408

1409+
# For index while building v2 it uses a global index store which is
1410+
# configured by this flag
1411+
additional_copts = []
1412+
if is_feature_enabled(
1413+
feature_configuration = feature_configuration,
1414+
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2,
1415+
):
1416+
additional_copts.append("-Xwrapped-swift-enable-global-index-store")
1417+
14081418
prerequisites = struct(
14091419
additional_inputs = additional_inputs,
14101420
bin_dir = bin_dir,
@@ -1421,7 +1431,7 @@ def compile(
14211431
transitive_defines = merged_providers.swift_info.transitive_defines,
14221432
transitive_modules = transitive_modules,
14231433
transitive_swiftmodules = transitive_swiftmodules,
1424-
user_compile_flags = copts + swift_toolchain.command_line_copts,
1434+
user_compile_flags = copts + swift_toolchain.command_line_copts + additional_copts,
14251435
vfsoverlay_file = vfsoverlay_file,
14261436
vfsoverlay_search_path = _SWIFTMODULES_VFS_ROOT,
14271437
# Merge the compile outputs into the prerequisites.
@@ -1756,6 +1766,9 @@ def _declare_compile_outputs(
17561766
index_while_building = is_feature_enabled(
17571767
feature_configuration = feature_configuration,
17581768
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING,
1769+
) or is_feature_enabled(
1770+
feature_configuration = feature_configuration,
1771+
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2,
17591772
)
17601773
if (
17611774
index_while_building and

swift/internal/feature_names.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ SWIFT_FEATURE_FULL_DEBUG_INFO = "swift.full_debug_info"
105105
# explicitly.
106106
SWIFT_FEATURE_IMPLICIT_MODULES = "swift.implicit_modules"
107107

108+
# Index while building - using a global index store cache
109+
# https://docs.google.com/document/d/1cH2sTpgSnJZCkZtJl1aY-rzy4uGPcrI-6RrUpdATO2Q/
110+
SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2 = "swift.index_while_building_v2"
111+
108112
# If enabled, the compilation action for a target will produce an index store.
109113
SWIFT_FEATURE_INDEX_WHILE_BUILDING = "swift.index_while_building"
110114

swift/internal/providers.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ target.
200200
`File`. The executable representing the worker executable used to invoke the
201201
compiler and other Swift tools (for both incremental and non-incremental
202202
compiles).
203+
""",
204+
"swift_worker_output_processor": """\
205+
`File`. An executable that runs to manage llvm indexes for index while building v2.
203206
""",
204207
"system_name": """\
205208
`String`. The name of the operating system that the toolchain is targeting.

swift/internal/swift_toolchain.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ def _swift_toolchain_impl(ctx):
215215
root_dir = toolchain_root,
216216
supports_objc_interop = False,
217217
swift_worker = ctx.executable._worker,
218+
swift_worker_output_processor = ctx.attr._worker_output_processor,
218219
system_name = ctx.attr.os,
219220
test_configuration = struct(
220221
env = {},
@@ -273,6 +274,14 @@ for incremental compilation using a persistent mode.
273274
""",
274275
executable = True,
275276
),
277+
"_worker_output_processor": attr.label(
278+
cfg = "host",
279+
allow_files = True,
280+
default = "@build_bazel_rules_swift//tools/worker:worker_output_processor",
281+
doc = """\
282+
An executable used to operate on llvm indexs for index while building
283+
""",
284+
),
276285
},
277286
),
278287
doc = "Represents a Swift compiler toolchain.",

swift/internal/xcode_swift_toolchain.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ def _xcode_swift_toolchain_impl(ctx):
691691
required_implicit_deps = [],
692692
supports_objc_interop = True,
693693
swift_worker = ctx.executable._worker,
694+
swift_worker_output_processor = ctx.attr._worker_output_processor,
694695
system_name = "darwin",
695696
test_configuration = struct(
696697
env = env,
@@ -726,6 +727,14 @@ for incremental compilation using a persistent mode.
726727
""",
727728
executable = True,
728729
),
730+
"_worker_output_processor": attr.label(
731+
cfg = "host",
732+
allow_files = True,
733+
default = "@build_bazel_rules_swift//tools/worker:worker_output_processor",
734+
doc = """\
735+
An executable used to operate on llvm indexs for index while building.
736+
""",
737+
),
729738
"_xcode_config": attr.label(
730739
default = configuration_field(
731740
name = "xcode_config_label",

tools/worker/BUILD

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,29 @@ cc_binary(
7777
}),
7878
)
7979

80+
# Internal hinge for index while building V2 feature
81+
config_setting(
82+
name = "index_while_building_v2",
83+
values = {
84+
"features": "swift.index_while_building_v2"
85+
},
86+
)
87+
88+
89+
filegroup(
90+
name = "worker_output_processor",
91+
# with index_while_building_v2 we want to add a dependency on the tool that
92+
# manages the per swift_library index. Otherwise, don't load it
93+
# This is mainly an internal capability right now - check
94+
# tools/worker/work_processor.cc for usage:
95+
# build_bazel_rules_swift_index_import is be provided by the user for now
96+
srcs = select({
97+
":index_while_building_v2": ["@build_bazel_rules_swift_index_import//:index_import"],
98+
"//conditions:default": []
99+
}),
100+
visibility = ["//visibility:public"],
101+
)
102+
80103
# Consumed by Bazel integration tests.
81104
filegroup(
82105
name = "for_bazel_tests",

tools/worker/work_processor.cc

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
#include <nlohmann/json.hpp>
2323
#include <sstream>
2424
#include <string>
25+
#include <vector>
2526

2627
#include "tools/common/file_system.h"
2728
#include "tools/common/path_utils.h"
29+
#include "tools/common/process.h"
2830
#include "tools/common/string_utils.h"
2931
#include "tools/common/temp_file.h"
3032
#include "tools/worker/output_file_map.h"
@@ -64,6 +66,13 @@ void WorkProcessor::ProcessWorkRequest(
6466
bool is_wmo = false;
6567

6668
std::string prev_arg;
69+
70+
std::string bazel_index_store_path;
71+
bool enable_global_index_store = false;
72+
73+
74+
std::string output_processor;
75+
6776
for (auto arg : request.arguments()) {
6877
auto original_arg = arg;
6978
// Peel off the `-output-file-map` argument, so we can rewrite it if
@@ -77,16 +86,49 @@ void WorkProcessor::ProcessWorkRequest(
7786
is_wmo = true;
7887
}
7988

89+
// The output processor is passed as an argument
90+
if (arg == "-Xwrapped-swift-output-processor") {
91+
arg.clear();
92+
} else if (prev_arg == "-Xwrapped-swift-output-processor") {
93+
output_processor = arg;
94+
arg.clear();
95+
}
96+
97+
// Peel off index-store-path, we will conditionally pop on a new one if
98+
// it passes -Xwrapped-swift-enable-global-index-store
99+
if (arg == "-index-store-path") {
100+
arg.clear();
101+
} else if (prev_arg == "-index-store-path") {
102+
bazel_index_store_path = arg;
103+
arg.clear();
104+
}
105+
if (arg == "-Xwrapped-swift-enable-global-index-store") {
106+
arg.clear();
107+
enable_global_index_store = true;
108+
}
109+
80110
if (!arg.empty()) {
81111
params_file_stream << arg << '\n';
82112
}
83113

84114
prev_arg = original_arg;
85115
}
86116

117+
// Material within this index store is namespaced to arcitecture, os, etc
118+
auto exec_root = GetCurrentDirectory();
119+
auto global_index_store = exec_root + "/bazel-out/global_index_store";
120+
if (enable_global_index_store) {
121+
processed_args.push_back("-index-store-path");
122+
processed_args.push_back(global_index_store);
123+
} else if (!bazel_index_store_path.empty()) {
124+
processed_args.push_back("-index-store-path");
125+
processed_args.push_back(bazel_index_store_path);
126+
}
127+
87128
if (!output_file_map_path.empty()) {
129+
output_file_map.ReadFromPath(output_file_map_path);
130+
88131
if (!is_wmo) {
89-
output_file_map.ReadFromPath(output_file_map_path);
90132

91133
// Rewrite the output file map to use the incremental storage area and
92134
// pass the compiler the path to the rewritten file.
@@ -130,7 +172,6 @@ void WorkProcessor::ProcessWorkRequest(
130172
SwiftRunner swift_runner(processed_args, /*force_response_file=*/true);
131173

132174
int exit_code = swift_runner.Run(&stderr_stream, /*stdout_to_stderr=*/true);
133-
134175
if (!is_wmo) {
135176
// Copy the output files from the incremental storage area back to the
136177
// locations where Bazel declared the files.
@@ -144,6 +185,35 @@ void WorkProcessor::ProcessWorkRequest(
144185
}
145186
}
146187

188+
// Import global indexes if compilation succeeds
189+
if (exit_code != EXIT_FAILURE && enable_global_index_store) {
190+
std::vector<std::string> ii_args;
191+
192+
auto index_import_path = exec_root + "/" + output_processor;
193+
ii_args.push_back(index_import_path);
194+
195+
// Use the output path map to deterine what compilation ouputs to import
196+
auto outputs = output_file_map.incremental_outputs();
197+
std::map<std::string, std::string>::iterator it;
198+
for (it = outputs.begin(); it != outputs.end(); it++) {
199+
auto output_path = it->first;
200+
auto file_type = output_path.substr(output_path.find_last_of(".") + 1);
201+
if (file_type == "o") {
202+
ii_args.push_back("-import-output-file");
203+
ii_args.push_back(output_path);
204+
}
205+
}
206+
207+
// Copy back from the global index store to bazel's index store
208+
ii_args.push_back(global_index_store);
209+
210+
ii_args.push_back(exec_root + "/" + bazel_index_store_path);
211+
212+
const std::vector<std::string>& ii_args_ = ii_args;
213+
// Dupes the output into the action for failure cases
214+
exit_code = RunSubProcess(ii_args_, &stderr_stream, /*stdout_to_stderr=*/true);
215+
}
216+
147217
response->set_exit_code(exit_code);
148218
response->set_output(stderr_stream.str());
149219
}

0 commit comments

Comments
 (0)