Skip to content

Support dataflow problems on arbitrary lattices #76044

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

Merged
merged 8 commits into from
Sep 7, 2020
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3758,6 +3758,7 @@ dependencies = [
"itertools 0.8.2",
"log_settings",
"polonius-engine",
"regex",
"rustc_apfloat",
"rustc_ast",
"rustc_attr",
Expand Down
54 changes: 37 additions & 17 deletions compiler/rustc_index/src/bit_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,20 @@ pub const WORD_BITS: usize = WORD_BYTES * 8;
/// will panic if the bitsets have differing domain sizes.
///
/// [`GrowableBitSet`]: struct.GrowableBitSet.html
#[derive(Clone, Eq, PartialEq, Decodable, Encodable)]
pub struct BitSet<T: Idx> {
#[derive(Eq, PartialEq, Decodable, Encodable)]
pub struct BitSet<T> {
domain_size: usize,
words: Vec<Word>,
marker: PhantomData<T>,
}

impl<T> BitSet<T> {
/// Gets the domain size.
pub fn domain_size(&self) -> usize {
self.domain_size
}
}

impl<T: Idx> BitSet<T> {
/// Creates a new, empty bitset with a given `domain_size`.
#[inline]
Expand All @@ -52,11 +59,6 @@ impl<T: Idx> BitSet<T> {
result
}

/// Gets the domain size.
pub fn domain_size(&self) -> usize {
self.domain_size
}

/// Clear all elements.
#[inline]
pub fn clear(&mut self) {
Expand All @@ -75,12 +77,6 @@ impl<T: Idx> BitSet<T> {
}
}

/// Efficiently overwrite `self` with `other`.
pub fn overwrite(&mut self, other: &BitSet<T>) {
assert!(self.domain_size == other.domain_size);
self.words.clone_from_slice(&other.words);
}

/// Count the number of set bits in the set.
pub fn count(&self) -> usize {
self.words.iter().map(|e| e.count_ones() as usize).sum()
Expand Down Expand Up @@ -243,6 +239,21 @@ impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> {
}
}

impl<T> Clone for BitSet<T> {
fn clone(&self) -> Self {
BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData }
}

fn clone_from(&mut self, from: &Self) {
if self.domain_size != from.domain_size {
self.words.resize(from.domain_size, 0);
self.domain_size = from.domain_size;
}

self.words.copy_from_slice(&from.words);
}
}

impl<T: Idx> fmt::Debug for BitSet<T> {
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
w.debug_list().entries(self.iter()).finish()
Expand Down Expand Up @@ -363,7 +374,7 @@ const SPARSE_MAX: usize = 8;
///
/// This type is used by `HybridBitSet`; do not use directly.
#[derive(Clone, Debug)]
pub struct SparseBitSet<T: Idx> {
pub struct SparseBitSet<T> {
domain_size: usize,
elems: ArrayVec<[T; SPARSE_MAX]>,
}
Expand Down Expand Up @@ -464,18 +475,27 @@ impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> {
/// All operations that involve an element will panic if the element is equal
/// to or greater than the domain size. All operations that involve two bitsets
/// will panic if the bitsets have differing domain sizes.
#[derive(Clone, Debug)]
pub enum HybridBitSet<T: Idx> {
#[derive(Clone)]
pub enum HybridBitSet<T> {
Sparse(SparseBitSet<T>),
Dense(BitSet<T>),
}

impl<T: Idx> fmt::Debug for HybridBitSet<T> {
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Sparse(b) => b.fmt(w),
Self::Dense(b) => b.fmt(w),
}
}
}

impl<T: Idx> HybridBitSet<T> {
pub fn new_empty(domain_size: usize) -> Self {
HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size))
}

fn domain_size(&self) -> usize {
pub fn domain_size(&self) -> usize {
match self {
HybridBitSet::Sparse(sparse) => sparse.domain_size,
HybridBitSet::Dense(dense) => dense.domain_size,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ itertools = "0.8"
tracing = "0.1"
log_settings = "0.1.1"
polonius-engine = "0.12.0"
regex = "1"
rustc_middle = { path = "../rustc_middle" }
rustc_attr = { path = "../rustc_attr" }
rustc_data_structures = { path = "../rustc_data_structures" }
Expand Down
41 changes: 25 additions & 16 deletions compiler/rustc_mir/src/dataflow/framework/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::borrow::Borrow;
use std::cmp::Ordering;

use rustc_index::bit_set::BitSet;
use rustc_index::vec::Idx;
use rustc_middle::mir::{self, BasicBlock, Location};

use super::{Analysis, Direction, Effect, EffectIndex, Results};
Expand All @@ -26,7 +27,7 @@ where
{
body: &'mir mir::Body<'tcx>,
results: R,
state: BitSet<A::Idx>,
state: A::Domain,

pos: CursorPosition,

Expand All @@ -46,17 +47,16 @@ where
{
/// Returns a new cursor that can inspect `results`.
pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
let bits_per_block = results.borrow().entry_set_for_block(mir::START_BLOCK).domain_size();

let bottom_value = results.borrow().analysis.bottom_value(body);
ResultsCursor {
body,
results,

// Initialize to an empty `BitSet` and set `state_needs_reset` to tell the cursor that
// Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that
// it needs to reset to block entry before the first seek. The cursor position is
// immaterial.
state_needs_reset: true,
state: BitSet::new_empty(bits_per_block),
state: bottom_value,
pos: CursorPosition::block_entry(mir::START_BLOCK),

#[cfg(debug_assertions)]
Expand All @@ -68,23 +68,21 @@ where
self.body
}

/// Returns the `Analysis` used to generate the underlying results.
/// Returns the underlying `Results`.
pub fn results(&self) -> &Results<'tcx, A> {
&self.results.borrow()
}

/// Returns the `Analysis` used to generate the underlying `Results`.
pub fn analysis(&self) -> &A {
&self.results.borrow().analysis
}

/// Returns the dataflow state at the current location.
pub fn get(&self) -> &BitSet<A::Idx> {
pub fn get(&self) -> &A::Domain {
&self.state
}

/// Returns `true` if the dataflow state at the current location contains the given element.
///
/// Shorthand for `self.get().contains(elem)`
pub fn contains(&self, elem: A::Idx) -> bool {
self.state.contains(elem)
}

/// Resets the cursor to hold the entry set for the given basic block.
///
/// For forward dataflow analyses, this is the dataflow state prior to the first statement.
Expand All @@ -94,7 +92,7 @@ where
#[cfg(debug_assertions)]
assert!(self.reachable_blocks.contains(block));

self.state.overwrite(&self.results.borrow().entry_set_for_block(block));
self.state.clone_from(&self.results.borrow().entry_set_for_block(block));
self.pos = CursorPosition::block_entry(block);
self.state_needs_reset = false;
}
Expand Down Expand Up @@ -202,12 +200,23 @@ where
///
/// This can be used, e.g., to apply the call return effect directly to the cursor without
/// creating an extra copy of the dataflow state.
pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut BitSet<A::Idx>)) {
pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) {
f(&self.results.borrow().analysis, &mut self.state);
self.state_needs_reset = true;
}
}

impl<'mir, 'tcx, A, R, T> ResultsCursor<'mir, 'tcx, A, R>
where
A: Analysis<'tcx, Domain = BitSet<T>>,
T: Idx,
R: Borrow<Results<'tcx, A>>,
{
pub fn contains(&self, elem: T) -> bool {
self.get().contains(elem)
}
}

#[derive(Clone, Copy, Debug)]
struct CursorPosition {
block: BasicBlock,
Expand Down
28 changes: 14 additions & 14 deletions compiler/rustc_mir/src/dataflow/framework/direction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub trait Direction {
/// `effects.start()` must precede or equal `effects.end()` in this direction.
fn apply_effects_in_range<A>(
analysis: &A,
state: &mut BitSet<A::Idx>,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
effects: RangeInclusive<EffectIndex>,
Expand All @@ -27,7 +27,7 @@ pub trait Direction {

fn apply_effects_in_block<A>(
analysis: &A,
state: &mut BitSet<A::Idx>,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
) where
Expand Down Expand Up @@ -55,9 +55,9 @@ pub trait Direction {
tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
dead_unwinds: Option<&BitSet<BasicBlock>>,
exit_state: &mut BitSet<A::Idx>,
exit_state: &mut A::Domain,
block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>;
}
Expand All @@ -72,7 +72,7 @@ impl Direction for Backward {

fn apply_effects_in_block<A>(
analysis: &A,
state: &mut BitSet<A::Idx>,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
) where
Expand Down Expand Up @@ -112,7 +112,7 @@ impl Direction for Backward {

fn apply_effects_in_range<A>(
analysis: &A,
state: &mut BitSet<A::Idx>,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
effects: RangeInclusive<EffectIndex>,
Expand Down Expand Up @@ -224,9 +224,9 @@ impl Direction for Backward {
_tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
dead_unwinds: Option<&BitSet<BasicBlock>>,
exit_state: &mut BitSet<A::Idx>,
exit_state: &mut A::Domain,
(bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
mut propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
{
Expand Down Expand Up @@ -281,7 +281,7 @@ impl Direction for Forward {

fn apply_effects_in_block<A>(
analysis: &A,
state: &mut BitSet<A::Idx>,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
) where
Expand Down Expand Up @@ -321,7 +321,7 @@ impl Direction for Forward {

fn apply_effects_in_range<A>(
analysis: &A,
state: &mut BitSet<A::Idx>,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
effects: RangeInclusive<EffectIndex>,
Expand Down Expand Up @@ -428,9 +428,9 @@ impl Direction for Forward {
tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
dead_unwinds: Option<&BitSet<BasicBlock>>,
exit_state: &mut BitSet<A::Idx>,
exit_state: &mut A::Domain,
(bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
mut propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
{
Expand Down Expand Up @@ -499,7 +499,7 @@ impl Direction for Forward {
// MIR building adds discriminants to the `values` array in the same order as they
// are yielded by `AdtDef::discriminants`. We rely on this to match each
// discriminant in `values` to its corresponding variant in linear time.
let mut tmp = BitSet::new_empty(exit_state.domain_size());
let mut tmp = analysis.bottom_value(body);
let mut discriminants = enum_def.discriminants(tcx);
for (value, target) in values.iter().zip(targets.iter().copied()) {
let (variant_idx, _) =
Expand All @@ -508,7 +508,7 @@ impl Direction for Forward {
from that of `SwitchInt::values`",
);

tmp.overwrite(exit_state);
tmp.clone_from(exit_state);
analysis.apply_discriminant_switch_effect(
&mut tmp,
bb,
Expand Down
Loading