From 7ce3dfa43b6d41e691eca3edd5c3c9db490c58ad Mon Sep 17 00:00:00 2001 From: Faith Ekstrand Date: Fri, 16 Jun 2023 19:09:10 -0500 Subject: [PATCH] nak: Add a more awesome CFG data structure Part-of: --- src/nouveau/compiler/nak_cfg.rs | 271 ++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) diff --git a/src/nouveau/compiler/nak_cfg.rs b/src/nouveau/compiler/nak_cfg.rs index 51735d35f23..4f3b95cc306 100644 --- a/src/nouveau/compiler/nak_cfg.rs +++ b/src/nouveau/compiler/nak_cfg.rs @@ -3,9 +3,280 @@ * SPDX-License-Identifier: MIT */ +use crate::bitset::BitSet; use crate::nak_ir::*; use std::collections::HashMap; +use std::hash::Hash; +use std::ops::{Deref, DerefMut, Index, IndexMut}; +use std::slice; + +pub struct CFGNode { + node: N, + dom: usize, + pred: Vec, + succ: Vec, +} + +impl Deref for CFGNode { + type Target = N; + + fn deref(&self) -> &N { + &self.node + } +} + +impl DerefMut for CFGNode { + fn deref_mut(&mut self) -> &mut N { + &mut self.node + } +} + +fn graph_post_dfs( + nodes: &Vec>, + id: usize, + seen: &mut BitSet, + post_idx: &mut Vec, + count: &mut usize, +) { + if seen.get(id) { + return; + } + seen.insert(id); + + /* Reverse the order of the successors so that any successors which are + * forward edges get descending indices. This ensures that, in the reverse + * post order, successors (and their dominated children) come in-order. + * In particular, as long as fall-through edges are only ever used for + * forward edges and the fall-through edge comes first, we guarantee that + * the fallthrough block comes immediately after its predecessor. + */ + for s in nodes[id].succ.iter().rev() { + graph_post_dfs(nodes, *s, seen, post_idx, count); + } + + post_idx[id] = *count; + *count += 1; +} + +fn rev_post_order_sort(nodes: &mut Vec>) { + let mut seen = BitSet::new(); + let mut post_idx = Vec::new(); + post_idx.resize(nodes.len(), usize::MAX); + let mut count = 0; + + graph_post_dfs(nodes, 0, &mut seen, &mut post_idx, &mut count); + + assert!(count <= nodes.len()); + + let remap_idx = |i: usize| { + let pid = post_idx[i]; + if pid == usize::MAX { + None + } else { + assert!(pid < count); + Some((count - 1) - pid) + } + }; + assert!(remap_idx(0) == Some(0)); + + /* Re-map edges to use post-index numbering */ + for n in nodes.iter_mut() { + let remap_filter_idx = |i: &mut usize| { + if let Some(r) = remap_idx(*i) { + *i = r; + true + } else { + false + } + }; + n.pred.retain_mut(remap_filter_idx); + n.succ.retain_mut(remap_filter_idx); + } + + /* We know a priori that each non-MAX post_idx is unique so we can sort the + * nodes by inserting them into a new array by index. + */ + let mut sorted: Vec> = Vec::with_capacity(count); + for (i, n) in nodes.drain(..).enumerate() { + if let Some(r) = remap_idx(i) { + unsafe { sorted.as_mut_ptr().add(r).write(n) }; + } + } + unsafe { sorted.set_len(count) }; + + std::mem::swap(nodes, &mut sorted); +} + +fn find_common_dom( + nodes: &Vec>, + mut a: usize, + mut b: usize, +) -> usize { + while a != b { + while a > b { + a = nodes[a].dom; + } + while b > a { + b = nodes[b].dom; + } + } + a +} + +fn calc_dominance(nodes: &mut Vec>) { + nodes[0].dom = 0; + loop { + let mut changed = false; + for i in 1..nodes.len() { + let mut dom = nodes[i].pred[0]; + for p in &nodes[i].pred[1..] { + if nodes[*p].dom != usize::MAX { + dom = find_common_dom(nodes, dom, *p); + } + } + assert!(dom != usize::MAX); + if nodes[i].dom != dom { + nodes[i].dom = dom; + changed = true; + } + } + + if !changed { + break; + } + } +} + +pub struct CFG2 { + nodes: Vec>, +} + +impl CFG2 { + pub fn from_blocks_edges( + nodes: impl IntoIterator, + edges: impl IntoIterator, + ) -> Self { + let mut nodes = Vec::from_iter(nodes.into_iter().map(|n| CFGNode { + node: n, + dom: usize::MAX, + pred: Vec::new(), + succ: Vec::new(), + })); + + for (p, s) in edges { + nodes[s].pred.push(p); + nodes[p].succ.push(s); + } + + rev_post_order_sort(&mut nodes); + calc_dominance(&mut nodes); + + CFG2 { nodes: nodes } + } + + pub fn get(&self, idx: usize) -> Option<&N> { + self.nodes.get(idx).map(|n| &n.node) + } + + pub fn get_mut(&mut self, idx: usize) -> Option<&mut N> { + self.nodes.get_mut(idx).map(|n| &mut n.node) + } + + pub fn iter(&self) -> slice::Iter> { + self.nodes.iter() + } + + pub fn iter_mut(&mut self) -> slice::IterMut> { + self.nodes.iter_mut() + } + + pub fn len(&self) -> usize { + self.nodes.len() + } + + pub fn succ_indices(&self, idx: usize) -> &[usize] { + &self.nodes[idx].succ[..] + } + + pub fn pred_indices(&self, idx: usize) -> &[usize] { + &self.nodes[idx].pred[..] + } +} + +impl Index for CFG2 { + type Output = N; + + fn index(&self, idx: usize) -> &N { + &self.nodes[idx].node + } +} + +impl IndexMut for CFG2 { + fn index_mut(&mut self, idx: usize) -> &mut N { + &mut self.nodes[idx].node + } +} + +impl<'a, N> IntoIterator for &'a CFG2 { + type Item = &'a CFGNode; + type IntoIter = slice::Iter<'a, CFGNode>; + + fn into_iter(self) -> slice::Iter<'a, CFGNode> { + self.iter() + } +} + +impl<'a, N> IntoIterator for &'a mut CFG2 { + type Item = &'a mut CFGNode; + type IntoIter = slice::IterMut<'a, CFGNode>; + + fn into_iter(self) -> slice::IterMut<'a, CFGNode> { + self.iter_mut() + } +} + +pub struct CFGBuilder { + nodes: Vec, + edges: Vec<(K, K)>, + key_map: HashMap, +} + +impl CFGBuilder { + pub fn new() -> CFGBuilder { + CFGBuilder { + nodes: Vec::new(), + edges: Vec::new(), + key_map: HashMap::new(), + } + } +} + +impl CFGBuilder { + pub fn add_node(&mut self, k: K, n: N) { + self.key_map.insert(k, self.nodes.len()); + self.nodes.push(n); + } + + pub fn add_edge(&mut self, s: K, p: K) { + self.edges.push((s, p)); + } + + pub fn as_cfg(mut self) -> CFG2 { + let edges = self.edges.drain(..).map(|(s, p)| { + let s = *self.key_map.get(&s).unwrap(); + let p = *self.key_map.get(&p).unwrap(); + (s, p) + }); + CFG2::from_blocks_edges(self.nodes, edges) + } +} + +impl Default for CFGBuilder { + fn default() -> Self { + CFGBuilder::new() + } +} struct CFGBlock { pred: Vec,