Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
06b4ab1
Add a module for marking Cilk tasks separately
aleph-oh Mar 31, 2024
997cf11
Update initialized-variable analysis to consider syncs
aleph-oh Apr 1, 2024
8f763d9
Fix DefinitelyInitializedVariables to merge state correctly
aleph-oh Apr 2, 2024
000993b
Refactor DefinitelyInitializedPlaces::terminator_effect
aleph-oh Apr 2, 2024
bb763f0
Appease tidy script
aleph-oh Apr 2, 2024
117f35e
Use a preorder traversal rather than visitor to build TaskTree
aleph-oh Apr 2, 2024
bbe3d4e
Minor nits about variable placement in TaskTree::from_body
aleph-oh Apr 2, 2024
a36f4ba
Refactor TaskTree::from_body into helpers
aleph-oh Apr 2, 2024
d21f806
Update test expectation line numbers
aleph-oh Apr 2, 2024
bade597
Implement Extend<T> for WorkQueue<T>
aleph-oh Apr 4, 2024
2a7d553
Avoid labeling basic blocks in unwind subgraph with a task
aleph-oh Apr 4, 2024
02233bc
Implement and test ChunkedBitSet::intersect(ChunkedBitSet)
aleph-oh Apr 4, 2024
7013aac
Update test expectations for correct semantics
aleph-oh Apr 5, 2024
b4ea04a
Only inject FakeRead for when the RHS is not a CilkSpawn
aleph-oh Apr 5, 2024
b6301bc
Add additional tests for sync in uninitialized variable checking
aleph-oh Apr 5, 2024
7bda8b6
Don't emit FakeRead when assigning the result of a spawn
aleph-oh Apr 5, 2024
8f26f0a
Add test that an error occurs when we return from a spawned block
aleph-oh Apr 5, 2024
1d41014
Resolve outdated FIXME about AST liveness
aleph-oh Apr 5, 2024
f637bd4
Merge branch 'cilk' into support-sync-in-uninitialized-variable-analysis
aleph-oh Apr 5, 2024
67f4614
Rename cilk feature gate test to follow convention
aleph-oh Apr 5, 2024
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
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -145,11 +145,11 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
self.mutate_place(location, *resume_arg, Deep);
}
TerminatorKind::Reattach { continuation } => {
// FIXME(jhilton): Should we be invalidating locals in the current basic block as well? I think we should be,
// FIXME(jhilton): Should we be invalidating locals in the current basic block as well? I think we should be,
// for consistency with what we did in the type-checker. My concern is that we only want to kill locals for the
// *current block*, which I don't think we're currently expressing. One way to think about modeling this
// is that we can imagine Detach as pushing a new stack frame for the spawned task so that way we don't
// invalidate all locals, only the locals that are generated in the spawned task. Alternatively, loan invalidation
// invalidate all locals, only the locals that are generated in the spawned task. Alternatively, loan invalidation
// can be handled by however blocks handle this situation.
let borrow_set = self.borrow_set;
let end_spawned_task =
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_data_structures/src/work_queue.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,3 +42,11 @@ impl<T: Idx> WorkQueue<T> {
}
}
}

impl<T: Idx> Extend<T> for WorkQueue<T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for element in iter {
self.insert(element);
}
}
}
59 changes: 57 additions & 2 deletions compiler/rustc_index/src/bit_set.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -677,8 +677,63 @@ impl<T: Idx> BitRelations<ChunkedBitSet<T>> for ChunkedBitSet<T> {
unimplemented!("implement if/when necessary");
}

fn intersect(&mut self, _other: &ChunkedBitSet<T>) -> bool {
unimplemented!("implement if/when necessary");
fn intersect(&mut self, other: &ChunkedBitSet<T>) -> bool {
assert_eq!(self.domain_size, other.domain_size);
debug_assert_eq!(self.chunks.len(), other.chunks.len());

let mut changed = false;
for (mut self_chunk, other_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) {
match (&mut self_chunk, &other_chunk) {
(_, Ones(_)) | (Zeros(_), _) => {}
(Ones(self_chunk_domain_size), Zeros(other_chunk_domain_size))
| (Ones(self_chunk_domain_size), Mixed(other_chunk_domain_size, ..))
| (Mixed(self_chunk_domain_size, ..), Zeros(other_chunk_domain_size, ..)) => {
// `other_chunk` fully overwrites `self_chunk`
debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size);
*self_chunk = other_chunk.clone();
changed = true;
}
(
Mixed(
self_chunk_domain_size,
ref mut self_chunk_count,
ref mut self_chunk_words,
),
Mixed(other_chunk_domain_size, _other_chunk_count, other_chunk_words),
) => {
debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size);
// First check if the operation would change
// `self_chunk.words`. If not, we can avoid allocating some
// words, and this happens often enough that it's a
// performance win. Also, we only need to operate on the
// in-use words, hence the slicing.
let op = |a, b| a & b;
let num_words = num_words(*self_chunk_domain_size as usize);
if bitwise_changes(
&self_chunk_words[0..num_words],
&other_chunk_words[0..num_words],
op,
) {
let self_chunk_words = Rc::make_mut(self_chunk_words);
let has_changed = bitwise(
&mut self_chunk_words[0..num_words],
&other_chunk_words[0..num_words],
op,
);
debug_assert!(has_changed);
*self_chunk_count = self_chunk_words[0..num_words]
.iter()
.map(|w| w.count_ones() as ChunkSize)
.sum();
if *self_chunk_count == *self_chunk_domain_size {
*self_chunk = Ones(*self_chunk_domain_size);
}
changed = true;
}
}
}
}
changed
}
}

Expand Down
86 changes: 86 additions & 0 deletions compiler/rustc_index/src/bit_set/tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -437,6 +437,92 @@ fn chunked_bitset_iter() {
check_iter(&bit, &vec);
}

#[test]
fn chunked_bitset_intersect_empty_empty() {
let vec: Vec<usize> = Vec::new();
let n = 10000;
let mut bit1 = with_elements_chunked(&vec, n);
let mut bit2 = with_elements_chunked(&vec, n);
assert!(!bit1.intersect(&bit2));
assert_eq!(bit1, bit2);
assert!(!bit2.intersect(&bit1));
assert_eq!(bit1, bit2);
}

#[test]
fn chunked_bitset_intersect_empty_filled() {
let n = 10000;
let vec1: Vec<usize> = Vec::new();
let vec2: Vec<usize> = (0..n).collect();
let mut bit1 = with_elements_chunked(&vec1, n);
let mut bit2 = with_elements_chunked(&vec2, n);
let bit1_clone = bit1.clone();
assert!(!bit1.intersect(&bit2));
assert_eq!(bit1, bit1_clone);
assert!(bit2.intersect(&bit1));
assert_eq!(bit2, bit1);
}

#[test]
fn chunked_bitset_intersect_empty_mixed() {
let n = 10000;
let vec1: Vec<usize> = Vec::new();
let vec2: Vec<usize> = vec![0, 1, 2, 2010, 2047, 2099, 6000, 6002, 6004];
let mut bit1 = with_elements_chunked(&vec1, n);
let mut bit2 = with_elements_chunked(&vec2, n);
let bit1_clone = bit1.clone();
assert!(!bit1.intersect(&bit2));
assert_eq!(bit1, bit1_clone);
assert!(bit2.intersect(&bit1));
assert_eq!(bit2, bit1);
}

#[test]
fn chunked_bitset_intersect_filled_filled() {
let n = 10000;
let vec1: Vec<usize> = (0..n).collect();
let vec2: Vec<usize> = (0..n).collect();
let mut bit1 = with_elements_chunked(&vec1, n);
let mut bit2 = with_elements_chunked(&vec2, n);
let bit1_clone = bit1.clone();
assert!(!bit1.intersect(&bit2));
assert_eq!(bit1, bit1_clone);
assert!(!bit2.intersect(&bit1));
assert_eq!(bit2, bit1);
}

#[test]
fn chunked_bitset_intersect_filled_mixed() {
let n = 10000;
let vec1: Vec<usize> = (0..n).collect();
let vec2: Vec<usize> = vec![0, 1, 2, 2010, 2047, 2099, 6000, 6002, 6004];
let vec_expected: Vec<usize> = vec![0, 1, 2, 2010, 2047, 2099, 6000, 6002, 6004];
let mut bit1 = with_elements_chunked(&vec1, n);
let mut bit2 = with_elements_chunked(&vec2, n);
let bit_expected = with_elements_chunked(&vec_expected, n);
let bit1_clone = bit1.clone();
assert!(bit1.intersect(&bit2));
assert_eq!(bit1, bit_expected);
assert!(!bit2.intersect(&bit1_clone));
assert_eq!(bit2, bit_expected);
}

#[test]
fn chunked_bitset_intersect_mixed_mixed() {
let n = 10000;
let vec1: Vec<usize> = vec![0, 1, 2, 2010, 5, 10, 20];
let vec2: Vec<usize> = vec![0, 1, 2, 2010, 2047, 2099, 6000, 6002, 6004];
let vec_expected: Vec<usize> = vec![0, 1, 2, 2010];
let mut bit1 = with_elements_chunked(&vec1, n);
let mut bit2 = with_elements_chunked(&vec2, n);
let expected = with_elements_chunked(&vec_expected, n);
let bit1_clone = bit1.clone();
assert!(bit1.intersect(&bit2));
assert_eq!(bit1, expected);
assert!(bit2.intersect(&bit1_clone));
assert_eq!(bit2, expected);
}

#[test]
fn grow() {
let mut set: GrowableBitSet<usize> = GrowableBitSet::with_capacity(65);
Expand Down
29 changes: 22 additions & 7 deletions compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -553,6 +553,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}

fn should_inject_fake_read(&self, initializer_id: ExprId) -> bool {
// For spawns, we don't want to emit a FakeRead since the value will only be defined on sync.
// They're nested inside a Scope consistently, so we just unwrap the scope.
match self.thir[initializer_id] {
Expr { kind: ExprKind::Scope { value, .. }, .. } => {
!matches!(self.thir[value], Expr { kind: ExprKind::CilkSpawn { .. }, .. })
}
_ => true,
}
}

pub(super) fn expr_into_pattern(
&mut self,
mut block: BasicBlock,
Expand All@@ -566,9 +577,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
unpack!(block = self.expr_into_dest(place, block, initializer_id));

// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let source_info = self.source_info(irrefutable_pat.span);
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place);
if self.should_inject_fake_read(initializer_id) {
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let source_info = self.source_info(irrefutable_pat.span);
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place);
}

self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
block.unit()
Expand DownExpand Up@@ -597,10 +610,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
unpack!(block = self.expr_into_dest(place, block, initializer_id));

// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let pattern_source_info = self.source_info(irrefutable_pat.span);
let cause_let = FakeReadCause::ForLet(None);
self.cfg.push_fake_read(block, pattern_source_info, cause_let, place);
if self.should_inject_fake_read(initializer_id) {
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let pattern_source_info = self.source_info(irrefutable_pat.span);
let cause_let = FakeReadCause::ForLet(None);
self.cfg.push_fake_read(block, pattern_source_info, cause_let, place);
}

let ty_source_info = self.source_info(annotation.span);

Expand Down
Loading