pub mod gpu;
pub mod rustc_facts;
pub type Point = u32;
pub type Loan = u32;
pub type Place = u32;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum LoanKind {
Shared,
Mut,
}
#[derive(Clone, Debug, Default)]
pub struct BorrowFacts {
pub point_count: u32,
pub cfg_edges: Vec<(Point, Point)>,
pub loan_place: Vec<Place>,
pub loan_kind: Vec<LoanKind>,
pub loan_issued_at: Vec<Point>,
pub loan_offset: Vec<u32>,
pub loan_used_at: Vec<(Loan, Point)>,
}
impl BorrowFacts {
#[must_use]
pub fn loan_count(&self) -> usize {
self.loan_place.len()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ConflictKind {
TwoMutable,
MutableAndShared,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Conflict {
pub first: Loan,
pub second: Loan,
pub offset: u32,
pub kind: ConflictKind,
}
#[must_use]
pub fn analyze(facts: &BorrowFacts) -> Vec<Conflict> {
let n = facts.point_count as usize;
let loans = facts.loan_count();
if loans < 2 || n == 0 {
return Vec::new();
}
let mut succ: Vec<Vec<usize>> = vec![Vec::new(); n];
let mut pred: Vec<Vec<usize>> = vec![Vec::new(); n];
for &(a, b) in &facts.cfg_edges {
let (a, b) = (a as usize, b as usize);
if a < n && b < n {
succ[a].push(b);
pred[b].push(a);
}
}
let words = loans.div_ceil(64);
let mut use_seed = vec![vec![0u64; words]; n];
for &(l, p) in &facts.loan_used_at {
if (l as usize) < loans && (p as usize) < n {
set_bit(&mut use_seed[p as usize], l as usize);
}
}
let mut issue_seed = vec![vec![0u64; words]; n];
for l in 0..loans {
let p = facts.loan_issued_at[l] as usize;
if p < n {
set_bit(&mut issue_seed[p], l);
}
}
let backward = fixpoint(&succ, &use_seed, n, words);
let forward = fixpoint(&pred, &issue_seed, n, words);
let mut conflicts = Vec::new();
for a in 0..loans {
for b in (a + 1)..loans {
if facts.loan_place[a] != facts.loan_place[b] {
continue;
}
let a_mut = facts.loan_kind[a] == LoanKind::Mut;
let b_mut = facts.loan_kind[b] == LoanKind::Mut;
if !(a_mut || b_mut) {
continue;
}
let issue_a = facts.loan_issued_at[a] as usize;
let issue_b = facts.loan_issued_at[b] as usize;
let a_live_at_b =
issue_b < n && test_bit(&forward[issue_b], a) && test_bit(&backward[issue_b], a);
let b_live_at_a =
issue_a < n && test_bit(&forward[issue_a], b) && test_bit(&backward[issue_a], b);
let overlap = a_live_at_b || b_live_at_a;
if overlap {
let (first, second) = if issue_a <= issue_b { (a, b) } else { (b, a) };
conflicts.push(Conflict {
first: first as Loan,
second: second as Loan,
offset: facts.loan_offset[second],
kind: if a_mut && b_mut {
ConflictKind::TwoMutable
} else {
ConflictKind::MutableAndShared
},
});
}
}
}
conflicts
}
fn fixpoint(adj: &[Vec<usize>], seed: &[Vec<u64>], n: usize, words: usize) -> Vec<Vec<u64>> {
let mut dependents: Vec<Vec<usize>> = vec![Vec::new(); n];
for (p, edges) in adj.iter().enumerate() {
for &q in edges {
dependents[q].push(p);
}
}
let mut out = seed.to_vec();
let mut work: Vec<usize> = (0..n).collect();
let mut queued = vec![true; n];
while let Some(p) = work.pop() {
queued[p] = false;
let mut next = seed[p].clone();
for &q in &adj[p] {
or_into(&mut next, &out[q], words);
}
if next != out[p] {
out[p] = next;
for &d in &dependents[p] {
if !queued[d] {
queued[d] = true;
work.push(d);
}
}
}
}
out
}
#[inline]
fn set_bit(set: &mut [u64], bit: usize) {
set[bit / 64] |= 1u64 << (bit % 64);
}
#[inline]
fn test_bit(set: &[u64], bit: usize) -> bool {
(set[bit / 64] >> (bit % 64)) & 1 == 1
}
#[inline]
fn or_into(dst: &mut [u64], src: &[u64], words: usize) {
for i in 0..words {
dst[i] |= src[i];
}
}