Skip to content

Add the parallel front-end test suite #143953

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ignore = [
"/tests/crashes/", # Many of these tests contain syntax errors.
"/tests/debuginfo/", # These tests are somewhat sensitive to source code layout.
"/tests/incremental/", # These tests are somewhat sensitive to source code layout.
"/tests/parallel/", # These tests contain syntax errors.
"/tests/pretty/", # These tests are very sensitive to source code layout.
"/tests/run-make/export", # These tests contain syntax errors.
"/tests/run-make/translation/test.rs", # This test contains syntax errors.
Expand Down
2 changes: 2 additions & 0 deletions src/bootstrap/src/core/build_steps/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,8 @@ test!(RustdocJson {
only_hosts: true,
});

test!(Parallel { path: "tests/parallel", mode: "parallel", suite: "parallel", default: true });

test!(Pretty {
path: "tests/pretty",
mode: "pretty",
Expand Down
2 changes: 2 additions & 0 deletions src/bootstrap/src/core/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ const PATH_REMAP: &[(&str, &[&str])] = &[
"tests/debuginfo",
"tests/incremental",
"tests/mir-opt",
"tests/parallel",
"tests/pretty",
"tests/run-make",
"tests/rustdoc",
Expand Down Expand Up @@ -1054,6 +1055,7 @@ impl<'a> Builder<'a> {
test::UiFullDeps,
test::Rustdoc,
test::CoverageRunRustdoc,
test::Parallel,
test::Pretty,
test::CodegenCranelift,
test::CodegenGCC,
Expand Down
2 changes: 2 additions & 0 deletions src/tools/compiletest/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ string_enum! {
CoverageMap => "coverage-map",
CoverageRun => "coverage-run",
Crashes => "crashes",
Parallel => "parallel",
}
}

Expand Down Expand Up @@ -66,6 +67,7 @@ string_enum! {
Debuginfo => "debuginfo",
Incremental => "incremental",
MirOpt => "mir-opt",
Parallel => "parallel",
Pretty => "pretty",
RunMake => "run-make",
Rustdoc => "rustdoc",
Expand Down
16 changes: 16 additions & 0 deletions src/tools/compiletest/src/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ pub struct TestProps {
// empty before the test starts. Incremental mode tests will reuse the
// incremental directory between passes in the same test.
pub incremental: bool,
// Number of times to run the test, for the parallel front-end test suit.
// Repeat tests to ensure no ICEs occur.
pub iteration_count: Option<usize>,
// If `true`, this test is a known bug.
//
// When set, some requirements are relaxed. Currently, this only means no
Expand Down Expand Up @@ -238,6 +241,7 @@ mod directives {
pub const ASSEMBLY_OUTPUT: &'static str = "assembly-output";
pub const STDERR_PER_BITWIDTH: &'static str = "stderr-per-bitwidth";
pub const INCREMENTAL: &'static str = "incremental";
pub const ITERATION_COUNT: &'static str = "iteration-count";
pub const KNOWN_BUG: &'static str = "known-bug";
pub const TEST_MIR_PASS: &'static str = "test-mir-pass";
pub const REMAP_SRC_BASE: &'static str = "remap-src-base";
Expand Down Expand Up @@ -280,6 +284,7 @@ impl TestProps {
forbid_output: vec![],
incremental_dir: None,
incremental: false,
iteration_count: None,
known_bug: false,
pass_mode: None,
fail_mode: None,
Expand Down Expand Up @@ -540,6 +545,16 @@ impl TestProps {
&mut self.stderr_per_bitwidth,
);
config.set_name_directive(ln, INCREMENTAL, &mut self.incremental);
config.set_name_value_directive(
ln,
ITERATION_COUNT,
&mut self.iteration_count,
|s| {
s.trim()
.parse()
.expect("The value of iteration-count must be a positive integer.")
},
);

// Unlike the other `name_value_directive`s this needs to be handled manually,
// because it sets a `bool` flag.
Expand Down Expand Up @@ -892,6 +907,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"ignore-x86_64-pc-windows-gnu",
"ignore-x86_64-unknown-linux-gnu",
"incremental",
"iteration-count",
"known-bug",
"llvm-cov-flags",
"max-llvm-major-version",
Expand Down
11 changes: 8 additions & 3 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ mod debuginfo;
mod incremental;
mod js_doc;
mod mir_opt;
mod parallel;
mod pretty;
mod run_make;
mod rustdoc;
Expand Down Expand Up @@ -272,6 +273,7 @@ impl<'test> TestCx<'test> {
TestMode::CoverageMap => self.run_coverage_map_test(), // see self::coverage
TestMode::CoverageRun => self.run_coverage_run_test(), // see self::coverage
TestMode::Crashes => self.run_crash_test(),
TestMode::Parallel => self.run_parallel_test(),
}
}

Expand Down Expand Up @@ -1518,8 +1520,10 @@ impl<'test> TestCx<'test> {
};
rustc.arg(input_file);

// Use a single thread for efficiency and a deterministic error message order
rustc.arg("-Zthreads=1");
if self.config.mode != TestMode::Parallel {
// Use a single thread for efficiency and a deterministic error message order
rustc.arg("-Zthreads=1");
}

// Hide libstd sources from ui tests to make sure we generate the stderr
// output that users will see.
Expand Down Expand Up @@ -1705,7 +1709,8 @@ impl<'test> TestCx<'test> {
| TestMode::Rustdoc
| TestMode::RustdocJson
| TestMode::RunMake
| TestMode::RustdocJs => {
| TestMode::RustdocJs
| TestMode::Parallel => {
// do not use JSON output
}
}
Expand Down
16 changes: 16 additions & 0 deletions src/tools/compiletest/src/runtest/parallel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use crate::runtest::{TestCx, WillExecute};

impl TestCx<'_> {
pub(super) fn run_parallel_test(&self) {
let pm = self.pass_mode();
let emit_metadata = self.should_emit_metadata(pm);

// Repeated testing due to instability in multithreaded environments.
let iteration_count = self.props.iteration_count.unwrap_or(50);
for _ in 0..iteration_count {
let proc_res = self.compile_test(WillExecute::No, emit_metadata);
// Ensure there is no ICE during parallel complication.
self.check_no_compiler_crash(&proc_res, false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add a timeout here to prevent CI from getting stuck in a deadlock.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or does the current test set already have it? @jieyouxu

Copy link
Member

@jieyouxu jieyouxu Jul 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current compiletest executor is basically libtest's. That is, tests are ran under test threads. There's a test running for too long warning message, but no hard kill-after-timeout, which IIUC is hard to implement with this setup because it'd require some kind of collaborative scheme.

For existing tests we've massaged them to not take that long, or disabled them if e.g. the compile time truly that long pointing to an issue tracking it.

}
}
}
1 change: 1 addition & 0 deletions src/tools/opt-dist/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ llvm-config = "{llvm_config}"
"tests/codegen-units",
"tests/incremental",
"tests/mir-opt",
"tests/parallel",
"tests/pretty",
// Make sure that we don't use too new GLIBC symbols on x64
"tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu",
Expand Down
8 changes: 8 additions & 0 deletions tests/parallel/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
This directory contains the robustness test for prallel front end, which means deadlocks
and other ice bugs. In other words, we don't care whether the compiler output in these tests,
but whether they can compile normally without deadlock or other ice bugs.

So when a test in this directory fails, please pay attention to whether it causes any ice problems.
If so(it should do), please post your comments in the issue corresponding to each test (or create a new issue
with the `wg-parallel-rustc` label). Even if it is an existing issue, please add a new comment,
which will help us determine the reproducibility of the bug.
14 changes: 14 additions & 0 deletions tests/parallel/cache-after-waiting-issue-111528.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Test for #111528, the ice issue cause waiting on a query that panicked
//
//@ compile-flags: -Z threads=16

#![crate_type = "rlib"]
#![allow(warnings)]

#[export_name = "fail"]
pub fn a() {}

#[export_name = "fail"]
pub fn b() {}

fn main() {}
6 changes: 6 additions & 0 deletions tests/parallel/cycle_crash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//@ compile-flags: -Z threads=2
//@ iteration-count: 20

const FOO: usize = FOO;

fn main() {}
56 changes: 56 additions & 0 deletions tests/parallel/deadlock-issue-119785.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Test for #119785, which causes a deadlock bug
//
//@ compile-flags: -Z threads=200

#![allow(incomplete_features)]
#![feature(
const_trait_impl,
effects,
)]

use std::marker::Destruct;

const fn cmp(a: &impl ~const PartialEq) -> bool {
a == a
}

const fn wrap(x: impl ~const PartialEq + ~const Destruct)
-> impl ~const PartialEq + ~const Destruct
{
x
}

#[const_trait]
trait Foo {
fn foo(&mut self, x: <Self as Index>::Output) -> <Self as Index>::Output;
}

impl const Foo for () {
fn huh() -> impl ~const PartialEq + ~const Destruct + Copy {
123
}
}

const _: () = {
assert!(cmp(&0xDEADBEEFu32));
assert!(cmp(&()));
assert!(wrap(123) == wrap(123));
assert!(wrap(123) != wrap(456));
let x = <() as Foo>::huh();
assert!(x == x);
};

#[const_trait]
trait T {}
struct S;
impl const T for S {}

const fn rpit() -> impl ~const T { S }

const fn apit(_: impl ~const T + ~const Destruct) {}

const fn rpit_assoc_bound() -> impl IntoIterator<Item: ~const T> { Some(S) }

const fn apit_assoc_bound(_: impl IntoIterator<Item: ~const T> + ~From Destruct) {}

fn main() {}
114 changes: 114 additions & 0 deletions tests/parallel/deadlock-issue-120757.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Test for #120757, which causes a deadlock bug
//
//@ compile-flags: -Z threads=50

#![feature(generic_const_exprs)]

trait TensorDimension {
const DIM: usize;
const ISSCALAR: bool = Self::DIM == 0;
fn is_scalar(&self) -> bool {
Self::ISSCALAR
}
}

trait TensorSize: TensorDimension {
fn size(&self) -> [usize; Self::DIM];
fn inbounds(&self, index: [usize; Self::DIM]) -> bool {
index.iter().zip(self.size().iter()).all(|(i, s)| i < s)
}
}

trait Broadcastable: TensorSize + Sized {
type Element;
fn bget(&self, index: [usize; Self::DIM]) -> Option<Self::Element>;
fn lazy_updim<const NEWDIM: usize>(
&self,
size: [usize; NEWDIM],
) -> LazyUpdim<Self, { Self::DIM }, NEWDIM> {
assert!(
NEWDIM >= Self::DIM,
"Updimmed tensor cannot have fewer indices than the initial one."
); // const generic bounds on nightly. ( )
LazyUpdim { size, reference: &self }
}
fn bmap<T, F: Fn(Self::Element) -> T>(&self, foo: F) -> BMap<T, Self, F, { Self::DIM }> {
BMap { reference: self, closure: foo }
}
}

struct LazyUpdim<'a, T: Broadcastable, const OLDDIM: usize, const DIM: usize> {
size: [usize; DIM],
reference: &'a T,
}

impl<'a, T: Broadcastable, const DIM: usize> TensorDimension for LazyUpdim<'a, T, { T::DIM }, DIM> {
const DIM: usize = DIM;
}
impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T::DIM }, DIM> {
fn size(&self) -> [usize; DIM] {
self.size
}
}
impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> {
type Element = T::Element;
fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
assert!(DIM >= T::DIM);
if !self.inbounds(index) {
return None;
}
let size = self.size();
//array_init::array_init(|i| if size[i] > 1 {index[i]} else {0});
let newindex: [usize; T::DIM] = Default::default();
self.reference.bget(newindex)
}
}

struct BMap<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> {
reference: &'a T,
closure: F,
}

impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorDimension
for BMap<'a, R, T, F, DIM>
{
const DIM: usize = DIM;
}
impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorSize
for BMap<'a, R, T, F, DIM>
{
fn size(&self) -> [usize; DIM] {
self.reference.size()
}
}
impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> Broadcastable
for BMap<'a, R, T, F, DIM>
{
type Element = R;
fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
self.reference.bget(index).map(ns_window)
}
}

impl<T> TensorDimension for Vec<T> {
const DIM: usize = 1;
}
impl<T> TensorSize for Vec<T> {
fn size(&self) -> [usize; 1] {
[self.len()]
}
}
impl<T: Clone> Broadcastable for Vec<T> {
type Element = T;
fn bget(&self, index: [usize; 1]) -> Option<T> {
self.get(index[0]).cloned()
}
}

fn main() {
let v = vec![1, 2, 3];
let bv = v.lazy_updim([3, 4]);
let bbv = bv.bmap(|x| x * x);

println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds."));
}
24 changes: 24 additions & 0 deletions tests/parallel/deadlock-issue-120759.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Test for #120759, which causes a deadlock bug
//
//@ compile-flags: -Z threads=50

#![crate_type= "lib"]
#![feature(transmutability)]

mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub struct Context;

pub fn is_maybe_transmutable<Src, Dst>(&self, cpu: &mut CPU)
where
Dst: BikeshedIntrinsicFrom<Src, Context>,
{
}
}

fn should_pad_explicitly_packed_field() {
#[repr(C)]
struct ExplicitlyPadded(ExplicitlyPadded);

assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
}
Loading
Loading