Skip to content

Commit 7b28a6b

Browse files
committed
Auto merge of #111495 - Kobzol:dist-tests, r=Mark-Simulacrum
Run tests on PGO/LTO/BOLT optimized dist artifacts This PR adds baisc tests for the optimized dist builds on x64 Linux and Windows. A subset of the test suite is run, so it's not perfect, but it's better than the status quo (which is basically no testing at all, apart from the perf bot on Linux). r? `@ghost`
2 parents afab366 + c91a3a4 commit 7b28a6b

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)