Skip to content

Commit c91a3a4

Browse files
committed
Test the PGO/BOLT/LTO optimized x64 Linux compiler on CI
1 parent 774a3d1 commit c91a3a4

File tree

3 files changed

+127
-7
lines changed

3 files changed

+127
-7
lines changed

src/bootstrap/lib.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ pub struct Build {
222222
initial_cargo: PathBuf,
223223
initial_lld: PathBuf,
224224
initial_libdir: PathBuf,
225+
initial_sysroot: PathBuf,
225226

226227
// Runtime state filled in later on
227228
// C/C++ compilers and archiver for all targets
@@ -389,13 +390,16 @@ impl Build {
389390
"/dummy".to_string()
390391
} else {
391392
output(Command::new(&config.initial_rustc).arg("--print").arg("sysroot"))
392-
};
393+
}
394+
.trim()
395+
.to_string();
396+
393397
let initial_libdir = initial_target_dir
394398
.parent()
395399
.unwrap()
396400
.parent()
397401
.unwrap()
398-
.strip_prefix(initial_sysroot.trim())
402+
.strip_prefix(&initial_sysroot)
399403
.unwrap()
400404
.to_path_buf();
401405

@@ -425,6 +429,7 @@ impl Build {
425429
initial_cargo: config.initial_cargo.clone(),
426430
initial_lld,
427431
initial_libdir,
432+
initial_sysroot: initial_sysroot.into(),
428433
local_rebuild: config.local_rebuild,
429434
fail_fast: config.cmd.fail_fast(),
430435
doc_tests: config.cmd.doc_tests(),

src/bootstrap/test.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1424,7 +1424,15 @@ note: if you're sure you want to do this, please open an issue as to why. In the
14241424

14251425
cmd.arg("--src-base").arg(builder.src.join("tests").join(suite));
14261426
cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite));
1427-
cmd.arg("--sysroot-base").arg(builder.sysroot(compiler));
1427+
1428+
// When top stage is 0, that means that we're testing an externally provided compiler.
1429+
// In that case we need to use its specific sysroot for tests to pass.
1430+
let sysroot = if builder.top_stage == 0 {
1431+
builder.initial_sysroot.clone()
1432+
} else {
1433+
builder.sysroot(compiler).to_path_buf()
1434+
};
1435+
cmd.arg("--sysroot-base").arg(sysroot);
14281436
cmd.arg("--stage-id").arg(stage_id);
14291437
cmd.arg("--suite").arg(suite);
14301438
cmd.arg("--mode").arg(mode);

src/ci/stage-build.py

+111-4
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ def llvm_bolt_profile_merged_file(self) -> Path:
124124
def metrics_path(self) -> Path:
125125
return self.build_root() / "build" / "metrics.json"
126126

127+
def executable_extension(self) -> str:
128+
raise NotImplementedError
129+
130+
def skipped_tests(self) -> Iterable[str]:
131+
return ()
132+
127133

128134
class LinuxPipeline(Pipeline):
129135
def checkout_path(self) -> Path:
@@ -152,6 +158,13 @@ def build_rustc_perf(self):
152158
def supports_bolt(self) -> bool:
153159
return True
154160

161+
def executable_extension(self) -> str:
162+
return ""
163+
164+
def skipped_tests(self) -> Iterable[str]:
165+
# This test fails because of linker errors, as of June 2023.
166+
yield "tests/ui/process/nofile-limit.rs"
167+
155168

156169
class WindowsPipeline(Pipeline):
157170
def __init__(self):
@@ -211,6 +224,13 @@ def rustc_profile_template_path(self) -> Path:
211224
def supports_bolt(self) -> bool:
212225
return False
213226

227+
def executable_extension(self) -> str:
228+
return ".exe"
229+
230+
def skipped_tests(self) -> Iterable[str]:
231+
# This test fails as of June 2023
232+
yield "tests\\codegen\\vec-shrink-panik.rs"
233+
214234

215235
def get_timestamp() -> float:
216236
return time.time()
@@ -403,9 +423,9 @@ def delete_directory(path: Path):
403423
shutil.rmtree(path)
404424

405425

406-
def unpack_archive(archive: Path):
426+
def unpack_archive(archive: Path, target_dir: Optional[Path] = None):
407427
LOGGER.info(f"Unpacking archive `{archive}`")
408-
shutil.unpack_archive(archive)
428+
shutil.unpack_archive(str(archive), extract_dir=str(target_dir) if target_dir is not None else None)
409429

410430

411431
def download_file(src: str, target: Path):
@@ -455,6 +475,7 @@ def cmd(
455475
)
456476
return subprocess.run(args, env=environment, check=True)
457477

478+
458479
class BenchmarkRunner:
459480
def run_rustc(self, pipeline: Pipeline):
460481
raise NotImplementedError
@@ -465,6 +486,7 @@ def run_llvm(self, pipeline: Pipeline):
465486
def run_bolt(self, pipeline: Pipeline):
466487
raise NotImplementedError
467488

489+
468490
class DefaultBenchmarkRunner(BenchmarkRunner):
469491
def run_rustc(self, pipeline: Pipeline):
470492
# Here we're profiling the `rustc` frontend, so we also include `Check`.
@@ -478,6 +500,7 @@ def run_rustc(self, pipeline: Pipeline):
478500
LLVM_PROFILE_FILE=str(pipeline.rustc_profile_template_path())
479501
)
480502
)
503+
481504
def run_llvm(self, pipeline: Pipeline):
482505
run_compiler_benchmarks(
483506
pipeline,
@@ -494,6 +517,7 @@ def run_bolt(self, pipeline: Pipeline):
494517
crates=LLVM_BOLT_CRATES
495518
)
496519

520+
497521
def run_compiler_benchmarks(
498522
pipeline: Pipeline,
499523
profiles: List[str],
@@ -650,10 +674,8 @@ def gather_llvm_profiles(pipeline: Pipeline, runner: BenchmarkRunner):
650674
def gather_rustc_profiles(pipeline: Pipeline, runner: BenchmarkRunner):
651675
LOGGER.info("Running benchmarks with PGO instrumented rustc")
652676

653-
654677
runner.run_rustc(pipeline)
655678

656-
657679
profile_path = pipeline.rustc_profile_merged_file()
658680
LOGGER.info(f"Merging Rustc PGO profiles to {profile_path}")
659681
cmd([
@@ -770,6 +792,86 @@ def record_metrics(pipeline: Pipeline, timer: Timer):
770792
log_metrics(metrics)
771793

772794

795+
def run_tests(pipeline: Pipeline):
796+
"""
797+
After `dist` is executed, we extract its archived components into a sysroot directory,
798+
and then use that extracted rustc as a stage0 compiler.
799+
Then we run a subset of tests using that compiler, to have a basic smoke test which checks
800+
whether the optimization pipeline hasn't broken something.
801+
"""
802+
build_dir = pipeline.build_root() / "build"
803+
dist_dir = build_dir / "dist"
804+
805+
def extract_dist_dir(name: str) -> Path:
806+
target_dir = build_dir / "optimized-dist"
807+
target_dir.mkdir(parents=True, exist_ok=True)
808+
unpack_archive(dist_dir / f"{name}.tar.xz", target_dir=target_dir)
809+
extracted_path = target_dir / name
810+
assert extracted_path.is_dir()
811+
return extracted_path
812+
813+
# Extract rustc, libstd, cargo and src archives to create the optimized sysroot
814+
rustc_dir = extract_dist_dir(f"rustc-nightly-{PGO_HOST}") / "rustc"
815+
libstd_dir = extract_dist_dir(f"rust-std-nightly-{PGO_HOST}") / f"rust-std-{PGO_HOST}"
816+
cargo_dir = extract_dist_dir(f"cargo-nightly-{PGO_HOST}") / f"cargo"
817+
extracted_src_dir = extract_dist_dir("rust-src-nightly") / "rust-src"
818+
819+
# We need to manually copy libstd to the extracted rustc sysroot
820+
shutil.copytree(
821+
libstd_dir / "lib" / "rustlib" / PGO_HOST / "lib",
822+
rustc_dir / "lib" / "rustlib" / PGO_HOST / "lib"
823+
)
824+
825+
# Extract sources - they aren't in the `rustc-nightly-{host}` tarball, so we need to manually copy libstd
826+
# sources to the extracted sysroot. We need sources available so that `-Zsimulate-remapped-rust-src-base`
827+
# works correctly.
828+
shutil.copytree(
829+
extracted_src_dir / "lib" / "rustlib" / "src",
830+
rustc_dir / "lib" / "rustlib" / "src"
831+
)
832+
833+
rustc_path = rustc_dir / "bin" / f"rustc{pipeline.executable_extension()}"
834+
assert rustc_path.is_file()
835+
cargo_path = cargo_dir / "bin" / f"cargo{pipeline.executable_extension()}"
836+
assert cargo_path.is_file()
837+
838+
config_content = f"""profile = "user"
839+
changelog-seen = 2
840+
841+
[build]
842+
rustc = "{rustc_path.as_posix()}"
843+
cargo = "{cargo_path.as_posix()}"
844+
845+
[llvm]
846+
download-ci-llvm = true
847+
"""
848+
logging.info(f"Using following `config.toml` for running tests:\n{config_content}")
849+
850+
# Simulate a stage 0 compiler with the extracted optimized dist artifacts.
851+
with open("config.toml", "w") as f:
852+
f.write(config_content)
853+
854+
args = [
855+
sys.executable,
856+
pipeline.checkout_path() / "x.py",
857+
"test",
858+
"--stage", "0",
859+
"tests/assembly",
860+
"tests/codegen",
861+
"tests/codegen-units",
862+
"tests/incremental",
863+
"tests/mir-opt",
864+
"tests/pretty",
865+
"tests/run-pass-valgrind",
866+
"tests/ui",
867+
]
868+
for test_path in pipeline.skipped_tests():
869+
args.extend(["--exclude", test_path])
870+
cmd(args=args, env=dict(
871+
COMPILETEST_FORCE_STAGE0="1"
872+
))
873+
874+
773875
def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRunner, final_build_args: List[str]):
774876
# Clear and prepare tmp directory
775877
shutil.rmtree(pipeline.opt_artifacts(), ignore_errors=True)
@@ -844,6 +946,11 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRu
844946
cmd(final_build_args)
845947
record_metrics(pipeline, stage4)
846948

949+
# Try builds can be in various broken states, so we don't want to gatekeep them with tests
950+
if not is_try_build():
951+
with timer.section("Run tests"):
952+
run_tests(pipeline)
953+
847954

848955
def run(runner: BenchmarkRunner):
849956
logging.basicConfig(

0 commit comments

Comments
 (0)