Skip to content

Commit 50f2176

Browse files
committed
Auto merge of rust-lang#112012 - Kobzol:try-build-llvm-rebuild, r=nikic
Avoid one `rustc` rebuild in the optimized build pipeline This PR changes the optimized build pipeline to avoid one `rustc` rebuild, inspired by [this comment](rust-lang#112011 (comment)). This speeds up the pipeline by 5-10 minutes. After this change, we **no longer gather LLVM PGO profiles from compiling stage 2 of `rustc`**. Now we build `rustc` two times (1x PGO instrumented, 1x PGO optimized) and LLVM three times (1x normal, 1x PGO instrumented, 1x PGO optimized). It should be possible to cache the normal LLVM build, but I'll leave that for another PR.
2 parents f383703 + 41f9f63 commit 50f2176

File tree

2 files changed

+99
-55
lines changed

2 files changed

+99
-55
lines changed

src/bootstrap/compile.rs

+4
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ impl Step for Std {
118118
|| builder.config.keep_stage_std.contains(&compiler.stage)
119119
{
120120
builder.info("Warning: Using a potentially old libstd. This may not behave well.");
121+
122+
copy_third_party_objects(builder, &compiler, target);
123+
copy_self_contained_objects(builder, &compiler, target);
124+
121125
builder.ensure(StdLink::from_std(self, compiler));
122126
return;
123127
}

src/ci/stage-build.py

+95-55
Original file line numberDiff line numberDiff line change
@@ -620,20 +620,25 @@ def get_files(directory: Path, filter: Optional[Callable[[Path], bool]] = None)
620620
yield path
621621

622622

623-
def build_rustc(
623+
def bootstrap_build(
624624
pipeline: Pipeline,
625625
args: List[str],
626-
env: Optional[Dict[str, str]] = None
626+
env: Optional[Dict[str, str]] = None,
627+
targets: Iterable[str] = ("library/std", )
627628
):
629+
if env is None:
630+
env = {}
631+
else:
632+
env = dict(env)
633+
env["RUST_BACKTRACE"] = "1"
628634
arguments = [
629635
sys.executable,
630636
pipeline.checkout_path() / "x.py",
631637
"build",
632638
"--target", PGO_HOST,
633639
"--host", PGO_HOST,
634640
"--stage", "2",
635-
"library/std"
636-
] + args
641+
] + list(targets) + args
637642
cmd(arguments, env=env)
638643

639644

@@ -776,18 +781,18 @@ def record_metrics(pipeline: Pipeline, timer: Timer):
776781
if metrics is None:
777782
return
778783
llvm_steps = tuple(metrics.find_all_by_type("bootstrap::llvm::Llvm"))
779-
assert len(llvm_steps) > 0
780784
llvm_duration = sum(step.duration for step in llvm_steps)
781785

782786
rustc_steps = tuple(metrics.find_all_by_type("bootstrap::compile::Rustc"))
783-
assert len(rustc_steps) > 0
784787
rustc_duration = sum(step.duration for step in rustc_steps)
785788

786789
# The LLVM step is part of the Rustc step
787-
rustc_duration -= llvm_duration
790+
rustc_duration = max(0, rustc_duration - llvm_duration)
788791

789-
timer.add_duration("LLVM", llvm_duration)
790-
timer.add_duration("Rustc", rustc_duration)
792+
if llvm_duration > 0:
793+
timer.add_duration("LLVM", llvm_duration)
794+
if rustc_duration > 0:
795+
timer.add_duration("Rustc", rustc_duration)
791796

792797
log_metrics(metrics)
793798

@@ -872,79 +877,114 @@ def extract_dist_dir(name: str) -> Path:
872877
))
873878

874879

875-
def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRunner, final_build_args: List[str]):
880+
def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRunner, dist_build_args: List[str]):
876881
# Clear and prepare tmp directory
877882
shutil.rmtree(pipeline.opt_artifacts(), ignore_errors=True)
878883
os.makedirs(pipeline.opt_artifacts(), exist_ok=True)
879884

880885
pipeline.build_rustc_perf()
881886

882-
# Stage 1: Build rustc + PGO instrumented LLVM
883-
with timer.section("Stage 1 (LLVM PGO)") as stage1:
884-
with stage1.section("Build rustc and LLVM") as rustc_build:
885-
build_rustc(pipeline, args=[
886-
"--llvm-profile-generate"
887-
], env=dict(
888-
LLVM_PROFILE_DIR=str(pipeline.llvm_profile_dir_root() / "prof-%p")
889-
))
890-
record_metrics(pipeline, rustc_build)
887+
"""
888+
Stage 1: Build PGO instrumented rustc
889+
890+
We use a normal build of LLVM, because gathering PGO profiles for LLVM and `rustc` at the same time
891+
can cause issues.
892+
"""
893+
with timer.section("Stage 1 (rustc PGO)") as stage1:
894+
with stage1.section("Build PGO instrumented rustc and LLVM") as rustc_pgo_instrument:
895+
bootstrap_build(pipeline, args=[
896+
"--rust-profile-generate",
897+
pipeline.rustc_profile_dir_root()
898+
])
899+
record_metrics(pipeline, rustc_pgo_instrument)
891900

892901
with stage1.section("Gather profiles"):
893-
gather_llvm_profiles(pipeline, runner)
902+
gather_rustc_profiles(pipeline, runner)
894903
print_free_disk_space(pipeline)
895904

896-
clear_llvm_files(pipeline)
897-
final_build_args += [
898-
"--llvm-profile-use",
899-
pipeline.llvm_profile_merged_file()
900-
]
901-
902-
# Stage 2: Build PGO instrumented rustc + LLVM
903-
with timer.section("Stage 2 (rustc PGO)") as stage2:
904-
with stage2.section("Build rustc and LLVM") as rustc_build:
905-
build_rustc(pipeline, args=[
906-
"--rust-profile-generate",
907-
pipeline.rustc_profile_dir_root()
905+
with stage1.section("Build PGO optimized rustc") as rustc_pgo_use:
906+
bootstrap_build(pipeline, args=[
907+
"--rust-profile-use",
908+
pipeline.rustc_profile_merged_file()
908909
])
909-
record_metrics(pipeline, rustc_build)
910+
record_metrics(pipeline, rustc_pgo_use)
911+
dist_build_args += [
912+
"--rust-profile-use",
913+
pipeline.rustc_profile_merged_file()
914+
]
915+
916+
"""
917+
Stage 2: Gather LLVM PGO profiles
918+
"""
919+
with timer.section("Stage 2 (LLVM PGO)") as stage2:
920+
# Clear normal LLVM artifacts
921+
clear_llvm_files(pipeline)
922+
923+
with stage2.section("Build PGO instrumented LLVM") as llvm_pgo_instrument:
924+
bootstrap_build(pipeline, args=[
925+
"--llvm-profile-generate",
926+
# We want to keep the already built PGO-optimized `rustc`.
927+
"--keep-stage", "0",
928+
"--keep-stage", "1"
929+
], env=dict(
930+
LLVM_PROFILE_DIR=str(pipeline.llvm_profile_dir_root() / "prof-%p")
931+
))
932+
record_metrics(pipeline, llvm_pgo_instrument)
910933

911934
with stage2.section("Gather profiles"):
912-
gather_rustc_profiles(pipeline, runner)
935+
gather_llvm_profiles(pipeline, runner)
936+
937+
dist_build_args += [
938+
"--llvm-profile-use",
939+
pipeline.llvm_profile_merged_file(),
940+
]
913941
print_free_disk_space(pipeline)
914942

915-
clear_llvm_files(pipeline)
916-
final_build_args += [
917-
"--rust-profile-use",
918-
pipeline.rustc_profile_merged_file()
919-
]
943+
# Clear PGO-instrumented LLVM artifacts
944+
clear_llvm_files(pipeline)
920945

921-
# Stage 3: Build rustc + BOLT instrumented LLVM
946+
"""
947+
Stage 3: Build BOLT instrumented LLVM
948+
949+
We build a PGO optimized LLVM in this step, then instrument it with BOLT and gather BOLT profiles.
950+
Note that we don't remove LLVM artifacts after this step, so that they are reused in the final dist build.
951+
BOLT instrumentation is performed "on-the-fly" when the LLVM library is copied to the sysroot of rustc,
952+
therefore the LLVM artifacts on disk are not "tainted" with BOLT instrumentation and they can be reused.
953+
"""
922954
if pipeline.supports_bolt():
923955
with timer.section("Stage 3 (LLVM BOLT)") as stage3:
924-
with stage3.section("Build rustc and LLVM") as rustc_build:
925-
build_rustc(pipeline, args=[
956+
with stage3.section("Build BOLT instrumented LLVM") as llvm_bolt_instrument:
957+
bootstrap_build(pipeline, args=[
926958
"--llvm-profile-use",
927959
pipeline.llvm_profile_merged_file(),
928960
"--llvm-bolt-profile-generate",
929-
"--rust-profile-use",
930-
pipeline.rustc_profile_merged_file()
961+
# We want to keep the already built PGO-optimized `rustc`.
962+
"--keep-stage", "0",
963+
"--keep-stage", "1"
931964
])
932-
record_metrics(pipeline, rustc_build)
965+
record_metrics(pipeline, llvm_bolt_instrument)
933966

934967
with stage3.section("Gather profiles"):
935968
gather_llvm_bolt_profiles(pipeline, runner)
936969

937-
# LLVM is not being cleared here, we want to reuse the previous build
938-
print_free_disk_space(pipeline)
939-
final_build_args += [
940-
"--llvm-bolt-profile-use",
941-
pipeline.llvm_bolt_profile_merged_file()
942-
]
970+
dist_build_args += [
971+
"--llvm-bolt-profile-use",
972+
pipeline.llvm_bolt_profile_merged_file()
973+
]
974+
print_free_disk_space(pipeline)
943975

944-
# Stage 4: Build PGO optimized rustc + PGO/BOLT optimized LLVM
945-
with timer.section("Stage 4 (final build)") as stage4:
946-
cmd(final_build_args)
947-
record_metrics(pipeline, stage4)
976+
# We want to keep the already built PGO-optimized `rustc`.
977+
dist_build_args += [
978+
"--keep-stage", "0",
979+
"--keep-stage", "1"
980+
]
981+
982+
"""
983+
Final stage: Build PGO optimized rustc + PGO/BOLT optimized LLVM
984+
"""
985+
with timer.section("Final stage (dist build)") as final_stage:
986+
cmd(dist_build_args)
987+
record_metrics(pipeline, final_stage)
948988

949989
# Try builds can be in various broken states, so we don't want to gatekeep them with tests
950990
if not is_try_build():

0 commit comments

Comments
 (0)