smt_scope/analysis/graph/analysis/
reconnect.rs1use std::collections::hash_map::Entry;
2
3#[cfg(feature = "mem_dbg")]
4use mem_dbg::{MemDbg, MemSize};
5
6use fxhash::FxHashSet;
7
8use crate::{
9 analysis::{raw::Node, InstGraph, RawNodeIndex},
10 BoxSlice, FxHashMap, TiVec,
11};
12
13use super::run::TopoAnalysis;
14
15pub trait ReachKind: Copy + core::fmt::Debug + From<bool> + Into<bool> {
16 fn current(node: &Node) -> Self;
17 fn value(self) -> bool {
18 self.into()
19 }
20}
21
22macro_rules! reach_kind {
23 ($name:ident($node:ident => $e:expr)) => {
24 #[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
25 #[cfg_attr(feature = "mem_dbg", copy_type)]
26 #[derive(Debug, Clone, Copy)]
27 pub struct $name(bool);
28 impl ReachKind for $name {
29 fn current($node: &Node) -> Self {
30 Self($e)
31 }
32 }
33 impl From<bool> for $name {
34 fn from(b: bool) -> Self {
35 Self(b)
36 }
37 }
38 impl From<$name> for bool {
39 fn from(b: $name) -> bool {
40 b.0
41 }
42 }
43 };
44}
45
46reach_kind!(ReachVisible(node => node.visible()));
47reach_kind!(ReachNonDisabled(node => !node.disabled()));
48
49#[derive(Clone, Copy)]
52pub struct BwdReachableAnalysis<Kind: ReachKind>(core::marker::PhantomData<Kind>);
53impl<Kind: ReachKind> Default for BwdReachableAnalysis<Kind> {
54 fn default() -> Self {
55 Self(Default::default())
56 }
57}
58
59impl<Kind: ReachKind> TopoAnalysis<false, false> for BwdReachableAnalysis<Kind> {
60 type Value = Kind;
61
62 fn collect<'a, 'n, T: Iterator<Item = (RawNodeIndex, &'n Self::Value)>>(
63 &mut self,
64 _graph: &'a InstGraph,
65 _cidx: RawNodeIndex,
66 node: &'a Node,
67 from_all: impl Fn() -> T,
68 ) -> Self::Value
69 where
70 Self::Value: 'n,
71 {
72 let curr = Kind::current(node).value();
73 let reach = curr || from_all().any(|(_, &b)| b.value());
74 reach.into()
75 }
76}
77
78pub struct ReconnectAnalysis(pub TiVec<RawNodeIndex, ReachVisible>);
89
90#[derive(Debug, Default)]
91pub struct ReconnectData {
92 pub above: FxHashMap<ReconnectEdge, Option<BoxSlice<RawNodeIndex>>>,
94 pub transitive: FxHashSet<ReconnectFrom>,
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
100pub struct ReconnectFrom {
101 pub visible: RawNodeIndex,
103 pub hidden: Option<RawNodeIndex>,
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
109pub struct ReconnectEdge {
110 pub from: ReconnectFrom,
111 pub to_h: RawNodeIndex,
114 pub indirect: bool,
117}
118
119impl ReconnectEdge {
120 fn direct_visible(from: RawNodeIndex) -> Self {
123 Self {
124 from: ReconnectFrom {
125 visible: from,
126 hidden: None,
127 },
128 to_h: from,
129 indirect: false,
130 }
131 }
132
133 pub fn is_direct_visible(&self) -> bool {
134 self.from.visible == self.to_h
135 }
136
137 fn set_indirect(self, indirect: bool) -> Self {
138 Self {
139 indirect: indirect || self.indirect,
140 ..self
141 }
142 }
143
144 fn set_to_h(self, to_h: RawNodeIndex) -> Self {
145 debug_assert_eq!(self.from.visible, self.to_h);
146 debug_assert_ne!(self.to_h, to_h);
147 Self { to_h, ..self }
148 }
149}
150
151impl ReconnectData {
152 fn above(&self) -> impl Iterator<Item = (ReconnectEdge, Option<&[RawNodeIndex]>)> + '_ {
153 self.above
154 .iter()
155 .map(|(edge, path)| (*edge, path.as_deref()))
156 }
157
158 pub fn insert(
159 &mut self,
160 edge: ReconnectEdge,
161 path: Option<&[RawNodeIndex]>,
162 prev: Option<RawNodeIndex>,
163 ) {
164 match self.above.entry(edge) {
165 Entry::Occupied(o) => *o.into_mut() = None,
166 Entry::Vacant(v) => {
167 v.insert(path.map(|p| p.iter().copied().chain(prev).collect()));
168 }
169 }
170 }
171
172 pub fn extend(&mut self, other: &Self, prev: RawNodeIndex, hidden: bool) {
173 self.above
175 .retain(|above, _| !other.transitive.contains(&above.from));
176 for (edge, path) in other.above() {
179 if self.transitive.contains(&edge.from) {
180 continue;
181 }
182 self.insert(edge.set_indirect(hidden), path, Some(prev));
183 }
184 self.transitive.extend(other.transitive.iter().copied());
186 }
187
188 pub fn reached_visible(&mut self, other: &Self, prev: RawNodeIndex) {
189 let transitive = other.above().map(|(above, _)| above.from);
190 self.transitive.extend(transitive);
191 for (edge, path) in other.above() {
192 self.insert(edge.set_to_h(prev), path, None);
193 }
194 }
195}
196
197impl TopoAnalysis<true, false> for ReconnectAnalysis {
198 type Value = ReconnectData;
199
200 fn collect<'a, 'n, T: Iterator<Item = (RawNodeIndex, &'n Self::Value)>>(
201 &mut self,
202 graph: &'a InstGraph,
203 cidx: RawNodeIndex,
204 node: &'a Node,
205 from_all: impl Fn() -> T,
206 ) -> Self::Value
207 where
208 Self::Value: 'n,
209 {
210 let mut data = Self::Value::default();
211 if !self.0[cidx].value() {
212 return data;
213 }
214
215 let visible = node.visible();
216 let hidden = node.hidden();
217 for (fidx, from_data) in from_all() {
218 let from = &graph.raw[fidx];
219 match (from.visible(), visible) {
220 (false, false) => {
221 data.extend(from_data, fidx, hidden);
222 }
223 (true, false) => {
224 let cidx = Some(cidx).filter(|_| from.kind().reconnect_child(node.kind()));
225 let edge = ReconnectEdge {
226 from: ReconnectFrom {
227 visible: fidx,
228 hidden: cidx,
229 },
230 to_h: fidx,
231 indirect: hidden,
232 };
233 data.insert(edge, Some(&[]), None);
234 data.transitive.extend(from_data.transitive.iter().copied());
235 }
236 (false, true) => {
237 data.reached_visible(from_data, fidx);
238 }
239 (true, true) => {
240 data.insert(ReconnectEdge::direct_visible(fidx), Some(&[]), None);
241 data.transitive.extend(from_data.transitive.iter().copied());
242 }
243 }
244 }
245
246 data
247 }
248}