Skip to content
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

Add parallel front end robustness test to ui tests #132051

Open
wants to merge 2 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 src/tools/compiletest/src/command-list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"only-x86_64-pc-windows-gnu",
"only-x86_64-pc-windows-msvc",
"only-x86_64-unknown-linux-gnu",
"parallel-front-end-robustness",
"pp-exact",
"pretty-compare-only",
"pretty-expanded",
Expand Down
10 changes: 10 additions & 0 deletions src/tools/compiletest/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ pub struct TestProps {
pub force_host: bool,
// Check stdout for error-pattern output as well as stderr
pub check_stdout: bool,
// For parallel front end, use repeated tests to ensure
// there is no deadlock or other ice problems.
pub parallel_front_end_robustness: bool,
// Check stdout & stderr for output of run-pass test
pub check_run_results: bool,
// For UI tests, allows compiler to generate arbitrary output to stdout
Expand Down Expand Up @@ -213,6 +216,7 @@ mod directives {
pub const CHECK_RUN_RESULTS: &'static str = "check-run-results";
pub const DONT_CHECK_COMPILER_STDOUT: &'static str = "dont-check-compiler-stdout";
pub const DONT_CHECK_COMPILER_STDERR: &'static str = "dont-check-compiler-stderr";
pub const PARALLEL_FRONT_END_ROBUTNESS: &'static str = "parallel-front-end-robustness";
pub const NO_PREFER_DYNAMIC: &'static str = "no-prefer-dynamic";
pub const PRETTY_EXPANDED: &'static str = "pretty-expanded";
pub const PRETTY_MODE: &'static str = "pretty-mode";
Expand Down Expand Up @@ -273,6 +277,7 @@ impl TestProps {
dont_check_compiler_stderr: false,
compare_output_lines_by_subset: false,
no_prefer_dynamic: false,
parallel_front_end_robustness: false,
pretty_expanded: false,
pretty_mode: "normal".to_string(),
pretty_compare_only: false,
Expand Down Expand Up @@ -494,6 +499,11 @@ impl TestProps {
DONT_CHECK_FAILURE_STATUS,
&mut self.dont_check_failure_status,
);
config.set_name_directive(
ln,
PARALLEL_FRONT_END_ROBUTNESS,
&mut self.parallel_front_end_robustness,
);

config.set_name_directive(ln, RUN_RUSTFIX, &mut self.run_rustfix);
config.set_name_directive(
Expand Down
3 changes: 0 additions & 3 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1349,9 +1349,6 @@ impl<'test> TestCx<'test> {
};
rustc.arg(input_file);

// Use a single thread for efficiency and a deterministic error message order
rustc.arg("-Zthreads=1");

Comment on lines -1352 to -1354
Copy link
Member

@jieyouxu jieyouxu Oct 26, 2024

Choose a reason for hiding this comment

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

Question: though if we no longer have the -Z threads=1, would other non-parallel tests potentially become flaky? Or is the parallel front-end currently such that by default is only -Z threads=1? Or rather, is that an intended feature (that we may discover some ui tests being flaky and thus expose bugs in current parallel rustc infra?)

Copy link
Member Author

Choose a reason for hiding this comment

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

The thread of parallel front end is always 1 only if users pass -Zthreads=n mannually. On the other hand, the user can easily break the limit by passing the -Z threads option(Later option will reset the previous one, we may need to improve on this), so this code doesn't make much sense.

Defaulting the number of threads to be greater than 1 is intended but will not be implemented for a short time. When we do, I think we still don't need to manually limit -Z threads=1 in test suit, but change the it to fit the output of a multithreaded environment.

Copy link
Member

Choose a reason for hiding this comment

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

The thread of parallel front end is always 1 only if users pass -Zthreads=n mannually. On the other hand, the user can easily break the limit by passing the -Z threads option(Later option will reset the previous one, we may need to improve on this), so this code doesn't make much sense.

Sorry to clarify, you mean the number of threads used by parallel front end is only not 1 if the user overrides it, right?

Copy link
Member

Choose a reason for hiding this comment

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

But that makes sense to me.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yea, at the moment

// Hide libstd sources from ui tests to make sure we generate the stderr
// output that users will see.
// Without this, we may be producing good diagnostics in-tree but users
Expand Down
19 changes: 18 additions & 1 deletion src/tools/compiletest/src/runtest/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,24 @@ impl TestCx<'_> {
let pm = self.pass_mode();
let should_run = self.should_run(pm);
let emit_metadata = self.should_emit_metadata(pm);
let proc_res = self.compile_test(should_run, emit_metadata);
let mut proc_res = self.compile_test(should_run, emit_metadata);

if self.props.parallel_front_end_robustness {
// Ensure there is no ice during parallel front end.
self.check_no_compiler_crash(&proc_res, false);

// Repeated testing due to instability in multithreaded environments.
for _ in 0..50 {
proc_res = self.compile_test(should_run, emit_metadata);
self.check_no_compiler_crash(&proc_res, false);
}
Comment on lines +33 to +37
Copy link
Member

@jieyouxu jieyouxu Oct 23, 2024

Choose a reason for hiding this comment

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

Question: this is going to be expensive as running 50 times involves not only spawning multiple processes, but also more expensive is the compiling itself. Do we have any local benchmarks to see how much longer the test will take w/ the extra 49 runs compared to just running once? I'm not suggesting we shouldn't do this -- we should -- but I would like to have more info on the impact of this on test time and then make an informed decision to accept the cost.

Context: I'm primarily thinking about some tests that might take a long time to compile, e.g. tests that have deep recursive types.

Copy link
Member Author

Choose a reason for hiding this comment

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

I haven't found a good benchmark yet, from what I've observed so far these tests don't noticeably extend the test time though. I think we can keep this comment until we get more information later


// For the parallel front end, we are currently only concerned with whether
// deadlock or other ice problems occur. The correctness of the output is
// guaranteed by other compiler tests.
return;
}

self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res);
if matches!(proc_res.truncated, Truncated::Yes)
&& !self.props.dont_check_compiler_stdout
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/parallel-rustc/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.
16 changes: 8 additions & 8 deletions tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// Test for #111528, the ice issue cause waiting on a query that panicked
//
//@ parallel-front-end-robustness
//@ compile-flags: -Z threads=16
//@ build-fail

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

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

#[export_name="fail"]
pub fn b() {
//~^ Error symbol `fail` is already defined
}
#[export_name = "fail"]
pub fn b() {}

fn main() {}

This file was deleted.

57 changes: 57 additions & 0 deletions tests/ui/parallel-rustc/deadlock-issue-119785.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Test for #119785, which causes a deadlock bug
//
//@ parallel-front-end-robustness
//@ 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() {}
116 changes: 116 additions & 0 deletions tests/ui/parallel-rustc/deadlock-issue-120757.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Test for #120757, which causes a deadlock bug
//
//@ parallel-front-end-robustness
//@ compile-flags: -Z threads=50

#![feature(generic_const_exprs)]
//~^ WARNING
Copy link
Member

Choose a reason for hiding this comment

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

Nit: stray warning?


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."));
}
69 changes: 69 additions & 0 deletions tests/ui/parallel-rustc/deadlock-issue-129911.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Test for #129911, which causes a deadlock bug
//
//@ parallel-front-end-robustness
//@ compile-flags: -Z threads=16

fn main() {
type KooArc = Frc<
{
{
{
{};
}
type Frc = Frc<{}>::Arc;;
}
type Frc = Frc<
{
{
{
{};
}
type Frc = Frc<{}>::Arc;;
}
type Frc = Frc<
{
{
{
{};
}
type Frc = Frc<{}>::Arc;;
}
type Frc = Frc<
{
{
{
{};
}
type Frc = Frc<{}>::Arc;;
}
type Frc = Frc<
{
{
{
{
{};
}
type Frc = Frc<{}>::Arc;;
};
}
type Frc = Frc<
{
{
{
{};
};
}
type Frc = Frc<{}>::Arc;;
},
>::Arc;;
},
>::Arc;;
},
>::Arc;;
},
>::Arc;;
},
>::Arc;;
},
>::Arc;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Test for #118205, which causes a deadlock bug
//
//@ parallel-front-end-robustness
//@ compile-flags:-C extra-filename=-1 -Z threads=16
//@ no-prefer-dynamic
//@ build-pass
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Test for #118205, which causes a deadlock bug
//
//@ parallel-front-end-robustness
//@ compile-flags: -Z threads=16
//@ build-pass

Expand Down
Loading
Loading