-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
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. |
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.
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() {} |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.")); | ||
} |
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; | ||
} |
There was a problem hiding this comment.
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?)There was a problem hiding this comment.
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.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to clarify, you mean the number of threads used by parallel front end is only not 1 if the user overrides it, right?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, at the moment