Skip to content

Commit 468742b

Browse files
committed
Add dataflow analysis of enum variants
Constify `Discriminant` Fix wrong size scalar in const_from_scalar Kill aliasable enums bless mir Praise the mir Switch to FxHashMap Rm bots Attempt btreemap instead of hash Maybe there were issues with queries? Reattempt index vec Implement cache for dataflow analysis Add test Move single enum after const prop
1 parent c396bb3 commit 468742b

File tree

11 files changed

+940
-5
lines changed

11 files changed

+940
-5
lines changed

compiler/rustc_mir_dataflow/src/framework/fmt.rs

+25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Custom formatting traits used when outputting Graphviz diagrams with the results of a dataflow
22
//! analysis.
33
4+
use crate::lattice::{FactArray, FactCache};
45
use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet};
56
use rustc_index::vec::Idx;
67
use std::fmt;
@@ -124,6 +125,30 @@ where
124125
}
125126
}
126127

128+
impl<T: Clone + Eq + fmt::Debug, C, const N: usize> DebugWithContext<C> for FactArray<T, N>
129+
where
130+
T: DebugWithContext<C>,
131+
{
132+
fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133+
f.debug_map()
134+
.entries(
135+
self.arr
136+
.iter()
137+
.enumerate()
138+
.map(|(i, ref v)| (i, DebugWithAdapter { this: *v, ctxt })),
139+
)
140+
.finish()
141+
}
142+
}
143+
144+
impl<I: Eq + fmt::Debug, L: Eq + fmt::Debug, F: Eq + fmt::Debug, C, const N: usize>
145+
DebugWithContext<C> for FactCache<I, L, F, N>
146+
{
147+
fn fmt_with(&self, _: &C, _: &mut fmt::Formatter<'_>) -> fmt::Result {
148+
todo!();
149+
}
150+
}
151+
127152
fn fmt_diff<T, C>(
128153
inserted: &HybridBitSet<T>,
129154
removed: &HybridBitSet<T>,

compiler/rustc_mir_dataflow/src/framework/lattice.rs

+180
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,183 @@ impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> {
250250
true
251251
}
252252
}
253+
254+
macro_rules! packed_int_join_semi_lattice {
255+
($name: ident, $base: ty) => {
256+
#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
257+
pub struct $name($base);
258+
impl $name {
259+
pub const TOP: Self = Self(<$base>::MAX);
260+
// If the value is too large it will be top, which is more conservative and thus
261+
// alright. It is only unsafe to make items bot.
262+
#[inline]
263+
pub const fn new(v: $base) -> Self {
264+
Self(v)
265+
}
266+
267+
#[inline]
268+
pub fn saturating_new(v: impl TryInto<$base>) -> Self {
269+
v.try_into().map(|v| Self(v)).unwrap_or(Self::TOP)
270+
}
271+
272+
pub const fn inner(self) -> $base {
273+
self.0
274+
}
275+
}
276+
277+
impl JoinSemiLattice for $name {
278+
#[inline]
279+
fn join(&mut self, other: &Self) -> bool {
280+
match (*self, *other) {
281+
(Self::TOP, _) => false,
282+
(a, b) if a == b => false,
283+
_ => {
284+
*self = Self::TOP;
285+
true
286+
}
287+
}
288+
}
289+
}
290+
291+
impl<C> crate::fmt::DebugWithContext<C> for $name {
292+
fn fmt_with(&self, _: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293+
if *self == Self::TOP { write!(f, "TOP") } else { write!(f, "{}", self.inner()) }
294+
}
295+
}
296+
};
297+
}
298+
299+
packed_int_join_semi_lattice!(PackedU8JoinSemiLattice, u8);
300+
301+
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
302+
pub struct FactArray<T, const N: usize> {
303+
// FIXME(julianknodt): maybe map Idxs to each N element?
304+
pub arr: [T; N],
305+
}
306+
307+
impl<T, const N: usize> FactArray<T, N> {
308+
#[inline]
309+
pub fn insert(&mut self, i: impl Idx, fact: T) {
310+
let Some(v) = self.arr.get_mut(i.index()) else { return };
311+
*v = fact;
312+
}
313+
#[inline]
314+
pub fn get(&self, i: &impl Idx) -> Option<&T> {
315+
self.arr.get(i.index())
316+
}
317+
}
318+
319+
impl<T: JoinSemiLattice, const N: usize> JoinSemiLattice for FactArray<T, N> {
320+
fn join(&mut self, other: &Self) -> bool {
321+
let mut changed = false;
322+
for (a, b) in self.arr.iter_mut().zip(other.arr.iter()) {
323+
changed |= a.join(b);
324+
}
325+
changed
326+
}
327+
}
328+
329+
impl<T: MeetSemiLattice, const N: usize> MeetSemiLattice for FactArray<T, N> {
330+
fn meet(&mut self, other: &Self) -> bool {
331+
let mut changed = false;
332+
for (a, b) in self.arr.iter_mut().zip(other.arr.iter()) {
333+
changed |= a.meet(b);
334+
}
335+
changed
336+
}
337+
}
338+
339+
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
340+
pub struct FactCache<I, L, F, const N: usize> {
341+
facts: [F; N],
342+
ord: [(I, L); N],
343+
len: usize,
344+
}
345+
346+
impl<I: Idx, L: Ord + Eq + Copy, F, const N: usize> FactCache<I, L, F, N> {
347+
pub fn new(empty_i: I, empty_l: L, empty_f: F) -> Self
348+
where
349+
F: Copy,
350+
{
351+
Self { facts: [empty_f; N], ord: [(empty_i, empty_l); N], len: 0 }
352+
}
353+
/// inserts a fact into the cache, evicting the oldest one,
354+
/// Or updating it if there is information on one already. If the new fact being
355+
/// inserted is older than the previous fact, it will not be inserted.
356+
pub fn insert(&mut self, i: I, l: L, fact: F) {
357+
let mut idx = None;
358+
for (j, (ci, cl)) in self.ord[..self.len].iter_mut().enumerate() {
359+
if *ci == i {
360+
assert!(*cl <= l);
361+
idx = Some(j);
362+
break;
363+
}
364+
}
365+
if idx.is_none() && self.len < N {
366+
let new_len = self.len + 1;
367+
idx = Some(std::mem::replace(&mut self.len, new_len));
368+
};
369+
if let Some(idx) = idx {
370+
self.facts[idx] = fact;
371+
self.ord[idx] = (i, l);
372+
return;
373+
};
374+
let (p, (_, old_l)) = self.ord.iter().enumerate().min_by_key(|k| k.1.1).unwrap();
375+
// FIXME(julianknodt) maybe don't make this an assert but just don't update?
376+
assert!(*old_l <= l);
377+
self.ord[p] = (i, l);
378+
self.facts[p] = fact;
379+
}
380+
pub fn get(&self, i: I) -> Option<(&L, &F)> {
381+
let (p, (_, loc)) =
382+
self.ord[..self.len].iter().enumerate().find(|(_, iloc)| iloc.0 == i)?;
383+
Some((loc, &self.facts[p]))
384+
}
385+
pub fn remove(&mut self, i: I) -> bool {
386+
let Some(pos) = self.ord[..self.len].iter().position(|(ci, _)| *ci == i)
387+
else { return false };
388+
389+
self.remove_idx(pos);
390+
return true;
391+
}
392+
#[inline]
393+
fn remove_idx(&mut self, i: usize) {
394+
assert!(i < self.len);
395+
self.ord.swap(i, self.len);
396+
self.facts.swap(i, self.len);
397+
self.len -= 1;
398+
}
399+
400+
fn drain_filter(&mut self, mut should_rm: impl FnMut(&I, &mut L, &mut F) -> bool) {
401+
let mut i = 0;
402+
while i < self.len {
403+
let (idx, l) = &mut self.ord[i];
404+
let f = &mut self.facts[i];
405+
if should_rm(idx, l, f) {
406+
self.remove_idx(i);
407+
continue;
408+
}
409+
i += 1;
410+
}
411+
}
412+
}
413+
414+
impl<I: Idx, L: Ord + Eq + Copy, F: Eq, const N: usize> JoinSemiLattice for FactCache<I, L, F, N> {
415+
fn join(&mut self, other: &Self) -> bool {
416+
let mut changed = false;
417+
self.drain_filter(|i, l, f| {
418+
let Some((other_loc, other_fact)) = other.get(*i) else {
419+
changed = true;
420+
return true;
421+
};
422+
if other_fact == f {
423+
*l = (*l).max(*other_loc);
424+
return false;
425+
}
426+
changed = true;
427+
return true;
428+
});
429+
430+
changed
431+
}
432+
}

compiler/rustc_mir_dataflow/src/impls/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis};
2121
mod borrowed_locals;
2222
mod init_locals;
2323
mod liveness;
24+
mod single_enum_variant;
2425
mod storage_liveness;
2526

2627
pub use self::borrowed_locals::borrowed_locals;
2728
pub use self::borrowed_locals::MaybeBorrowedLocals;
2829
pub use self::init_locals::MaybeInitializedLocals;
2930
pub use self::liveness::MaybeLiveLocals;
3031
pub use self::liveness::MaybeTransitiveLiveLocals;
32+
pub use self::single_enum_variant::SingleEnumVariant;
3133
pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive};
3234

3335
/// `MaybeInitializedPlaces` tracks all places that might be

0 commit comments

Comments
 (0)