Skip to content

Commit 8fe9c8a

Browse files
committed
transmutability: remove NFA intermediate representation
Prior to this commit, the transmutability analysis used an intermediate NFA representation of type layout. We then determinized this representation into a DFA, upon which we ran the core transmutability analysis. Unfortunately, determinizing NFAs is expensive. In this commit, we avoid NFAs entirely by observing that Rust `union`s are the only source of nondeterminism and that it is comparatively cheap to compute the DFA union of DFAs. We also implement Graphviz DOT debug formatting of DFAs. Fixes rust-lang/project-safe-transmute#23 Fixes rust-lang/project-safe-transmute#24
1 parent 883f9f7 commit 8fe9c8a

File tree

7 files changed

+220
-290
lines changed

7 files changed

+220
-290
lines changed

Diff for: compiler/rustc_transmute/src/layout/dfa.rs

+175-92
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
use std::fmt;
22
use std::sync::atomic::{AtomicU32, Ordering};
33

4-
use tracing::instrument;
5-
6-
use super::{Byte, Nfa, Ref, nfa};
4+
use super::{Byte, Ref, Tree, Uninhabited};
75
use crate::Map;
86

9-
#[derive(PartialEq, Clone, Debug)]
7+
#[derive(PartialEq, Clone)]
108
pub(crate) struct Dfa<R>
119
where
1210
R: Ref,
@@ -34,35 +32,15 @@ where
3432
}
3533
}
3634

37-
impl<R> Transitions<R>
38-
where
39-
R: Ref,
40-
{
41-
#[cfg(test)]
42-
fn insert(&mut self, transition: Transition<R>, state: State) {
43-
match transition {
44-
Transition::Byte(b) => {
45-
self.byte_transitions.insert(b, state);
46-
}
47-
Transition::Ref(r) => {
48-
self.ref_transitions.insert(r, state);
49-
}
50-
}
51-
}
52-
}
53-
5435
/// The states in a `Nfa` represent byte offsets.
5536
#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)]
56-
pub(crate) struct State(u32);
37+
pub(crate) struct State(pub(crate) u32);
5738

58-
#[cfg(test)]
59-
#[derive(Hash, Eq, PartialEq, Clone, Copy)]
60-
pub(crate) enum Transition<R>
61-
where
62-
R: Ref,
63-
{
64-
Byte(Byte),
65-
Ref(R),
39+
impl State {
40+
pub(crate) fn new() -> Self {
41+
static COUNTER: AtomicU32 = AtomicU32::new(0);
42+
Self(COUNTER.fetch_add(1, Ordering::SeqCst))
43+
}
6644
}
6745

6846
impl fmt::Debug for State {
@@ -71,19 +49,6 @@ impl fmt::Debug for State {
7149
}
7250
}
7351

74-
#[cfg(test)]
75-
impl<R> fmt::Debug for Transition<R>
76-
where
77-
R: Ref,
78-
{
79-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80-
match &self {
81-
Self::Byte(b) => b.fmt(f),
82-
Self::Ref(r) => r.fmt(f),
83-
}
84-
}
85-
}
86-
8752
impl<R> Dfa<R>
8853
where
8954
R: Ref,
@@ -94,58 +59,153 @@ where
9459
let start = State::new();
9560
let accepting = State::new();
9661

97-
transitions.entry(start).or_default().insert(Transition::Byte(Byte::Init(0x00)), accepting);
62+
transitions.entry(start).or_default().byte_transitions.insert(Byte::Init(0x00), accepting);
9863

99-
transitions.entry(start).or_default().insert(Transition::Byte(Byte::Init(0x01)), accepting);
64+
transitions.entry(start).or_default().byte_transitions.insert(Byte::Init(0x01), accepting);
10065

10166
Self { transitions, start, accepting }
10267
}
10368

104-
#[instrument(level = "debug")]
105-
pub(crate) fn from_nfa(nfa: Nfa<R>) -> Self {
106-
let Nfa { transitions: nfa_transitions, start: nfa_start, accepting: nfa_accepting } = nfa;
69+
pub(crate) fn unit() -> Self {
70+
let transitions: Map<State, Transitions<R>> = Map::default();
71+
let start = State::new();
72+
let accepting = start;
73+
74+
Self { transitions, start, accepting }
75+
}
10776

108-
let mut dfa_transitions: Map<State, Transitions<R>> = Map::default();
109-
let mut nfa_to_dfa: Map<nfa::State, State> = Map::default();
110-
let dfa_start = State::new();
111-
nfa_to_dfa.insert(nfa_start, dfa_start);
77+
pub(crate) fn from_byte(byte: Byte) -> Self {
78+
let mut transitions: Map<State, Transitions<R>> = Map::default();
79+
let start = State::new();
80+
let accepting = State::new();
11281

113-
let mut queue = vec![(nfa_start, dfa_start)];
82+
transitions.entry(start).or_default().byte_transitions.insert(byte, accepting);
11483

115-
while let Some((nfa_state, dfa_state)) = queue.pop() {
116-
if nfa_state == nfa_accepting {
117-
continue;
118-
}
84+
Self { transitions, start, accepting }
85+
}
86+
87+
pub(crate) fn from_ref(r: R) -> Self {
88+
let mut transitions: Map<State, Transitions<R>> = Map::default();
89+
let start = State::new();
90+
let accepting = State::new();
91+
92+
transitions.entry(start).or_default().ref_transitions.insert(r, accepting);
11993

120-
for (nfa_transition, next_nfa_states) in nfa_transitions[&nfa_state].iter() {
121-
let dfa_transitions =
122-
dfa_transitions.entry(dfa_state).or_insert_with(Default::default);
123-
124-
let mapped_state = next_nfa_states.iter().find_map(|x| nfa_to_dfa.get(x).copied());
125-
126-
let next_dfa_state = match nfa_transition {
127-
&nfa::Transition::Byte(b) => *dfa_transitions
128-
.byte_transitions
129-
.entry(b)
130-
.or_insert_with(|| mapped_state.unwrap_or_else(State::new)),
131-
&nfa::Transition::Ref(r) => *dfa_transitions
132-
.ref_transitions
133-
.entry(r)
134-
.or_insert_with(|| mapped_state.unwrap_or_else(State::new)),
135-
};
136-
137-
for &next_nfa_state in next_nfa_states {
138-
nfa_to_dfa.entry(next_nfa_state).or_insert_with(|| {
139-
queue.push((next_nfa_state, next_dfa_state));
140-
next_dfa_state
141-
});
94+
Self { transitions, start, accepting }
95+
}
96+
97+
pub(crate) fn from_tree(tree: Tree<!, R>) -> Result<Self, Uninhabited> {
98+
Ok(match tree {
99+
Tree::Byte(b) => Self::from_byte(b),
100+
Tree::Ref(r) => Self::from_ref(r),
101+
Tree::Alt(alts) => {
102+
let mut alts = alts.into_iter().map(Self::from_tree);
103+
let mut dfa = alts.next().ok_or(Uninhabited)??;
104+
for alt in alts {
105+
dfa = dfa.union(alt?, State::new);
142106
}
107+
dfa
108+
}
109+
Tree::Seq(elts) => {
110+
let mut dfa = Self::unit();
111+
for elt in elts.into_iter().map(Self::from_tree) {
112+
dfa = dfa.concat(elt?);
113+
}
114+
dfa
115+
}
116+
})
117+
}
118+
119+
/// Concatenate two `Dfa`s.
120+
pub(crate) fn concat(self, other: Self) -> Self {
121+
if self.start == self.accepting {
122+
return other;
123+
} else if other.start == other.accepting {
124+
return self;
125+
}
126+
127+
let start = self.start;
128+
let accepting = other.accepting;
129+
130+
let mut transitions: Map<State, Transitions<R>> = self.transitions;
131+
132+
for (source, transition) in other.transitions {
133+
let fix_state = |state| if state == other.start { self.accepting } else { state };
134+
let entry = transitions.entry(fix_state(source)).or_default();
135+
for (edge, destination) in transition.byte_transitions {
136+
entry.byte_transitions.insert(edge, fix_state(destination));
137+
}
138+
for (edge, destination) in transition.ref_transitions {
139+
entry.ref_transitions.insert(edge, fix_state(destination));
143140
}
144141
}
145142

146-
let dfa_accepting = nfa_to_dfa[&nfa_accepting];
143+
Self { transitions, start, accepting }
144+
}
145+
146+
/// Compute the union of two `Nfa`s.
147+
pub(crate) fn union(self, other: Self, mut new_state: impl FnMut() -> State) -> Self {
148+
let a = self;
149+
let b = other;
150+
151+
let accepting = new_state();
152+
153+
let mut mapping: Map<(Option<State>, Option<State>), State> = Map::default();
154+
155+
let mut mapped = |(a_state, b_state)| {
156+
if Some(a.accepting) == a_state || Some(b.accepting) == b_state {
157+
accepting
158+
} else {
159+
*mapping.entry((a_state, b_state)).or_insert_with(&mut new_state)
160+
}
161+
};
162+
163+
let start = mapped((Some(a.start), Some(b.start)));
164+
let mut transitions: Map<State, Transitions<R>> = Map::default();
165+
let mut queue = vec![(Some(a.start), Some(b.start))];
166+
let empty_transitions = Transitions::default();
167+
168+
while let Some((a_src, b_src)) = queue.pop() {
169+
let a_transitions =
170+
a_src.and_then(|a_src| a.transitions.get(&a_src)).unwrap_or(&empty_transitions);
171+
let b_transitions =
172+
b_src.and_then(|b_src| b.transitions.get(&b_src)).unwrap_or(&empty_transitions);
173+
174+
let byte_transitions =
175+
a_transitions.byte_transitions.keys().chain(b_transitions.byte_transitions.keys());
176+
177+
for byte_transition in byte_transitions {
178+
let a_dst = a_transitions.byte_transitions.get(byte_transition).copied();
179+
180+
let b_dst = b_transitions.byte_transitions.get(byte_transition).copied();
181+
182+
assert!(a_dst.is_some() || b_dst.is_some());
183+
let src = mapped((a_src, b_src));
184+
let dst = mapped((a_dst, b_dst));
185+
186+
transitions.entry(src).or_default().byte_transitions.insert(*byte_transition, dst);
187+
188+
queue.push((a_dst, b_dst))
189+
}
190+
191+
let ref_transitions =
192+
a_transitions.ref_transitions.keys().chain(b_transitions.ref_transitions.keys());
147193

148-
Self { transitions: dfa_transitions, start: dfa_start, accepting: dfa_accepting }
194+
for ref_transition in ref_transitions {
195+
let a_dst = a_transitions.ref_transitions.get(ref_transition).copied();
196+
197+
let b_dst = b_transitions.ref_transitions.get(ref_transition).copied();
198+
199+
let src = mapped((a_src, b_src));
200+
let dst = mapped((a_dst, b_dst));
201+
202+
transitions.entry(src).or_default().ref_transitions.insert(*ref_transition, dst);
203+
204+
queue.push((a_dst, b_dst))
205+
}
206+
}
207+
208+
Self { transitions, start, accepting }
149209
}
150210

151211
pub(crate) fn bytes_from(&self, start: State) -> Option<&Map<Byte, State>> {
@@ -159,24 +219,47 @@ where
159219
pub(crate) fn refs_from(&self, start: State) -> Option<&Map<R, State>> {
160220
Some(&self.transitions.get(&start)?.ref_transitions)
161221
}
162-
}
163222

164-
impl State {
165-
pub(crate) fn new() -> Self {
166-
static COUNTER: AtomicU32 = AtomicU32::new(0);
167-
Self(COUNTER.fetch_add(1, Ordering::SeqCst))
223+
#[cfg(test)]
224+
pub(crate) fn from_edges<B: Copy + Into<Byte>>(
225+
start: u32,
226+
accept: u32,
227+
edges: &[(u32, B, u32)],
228+
) -> Self {
229+
let start = State(start);
230+
let accepting = State(accept);
231+
let mut transitions: Map<State, Transitions<R>> = Map::default();
232+
233+
for &(src, edge, dst) in edges {
234+
let src = State(src);
235+
let dst = State(dst);
236+
let old = transitions.entry(src).or_default().byte_transitions.insert(edge.into(), dst);
237+
assert!(old.is_none());
238+
}
239+
240+
Self { start, accepting, transitions }
168241
}
169242
}
170243

171-
#[cfg(test)]
172-
impl<R> From<nfa::Transition<R>> for Transition<R>
244+
impl<R> fmt::Debug for Dfa<R>
173245
where
174246
R: Ref,
175247
{
176-
fn from(nfa_transition: nfa::Transition<R>) -> Self {
177-
match nfa_transition {
178-
nfa::Transition::Byte(byte) => Transition::Byte(byte),
179-
nfa::Transition::Ref(r) => Transition::Ref(r),
248+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249+
writeln!(f, "digraph {{")?;
250+
writeln!(f, " {:?} [shape = doublecircle]", self.start)?;
251+
writeln!(f, " {:?} [shape = doublecircle]", self.accepting)?;
252+
253+
for (src, transitions) in self.transitions.iter() {
254+
for (t, dst) in transitions.byte_transitions.iter() {
255+
writeln!(f, " {src:?} -> {dst:?} [label=\"{t:?}\"]")?;
256+
}
257+
258+
for (t, dst) in transitions.ref_transitions.iter() {
259+
writeln!(f, " {src:?} -> {dst:?} [label=\"{t:?}\"]")?;
260+
}
180261
}
262+
263+
writeln!(f, "}}")
181264
}
182265
}

Diff for: compiler/rustc_transmute/src/layout/mod.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ use std::hash::Hash;
44
pub(crate) mod tree;
55
pub(crate) use tree::Tree;
66

7-
pub(crate) mod nfa;
8-
pub(crate) use nfa::Nfa;
9-
107
pub(crate) mod dfa;
118
pub(crate) use dfa::Dfa;
129

@@ -29,6 +26,13 @@ impl fmt::Debug for Byte {
2926
}
3027
}
3128

29+
#[cfg(test)]
30+
impl From<u8> for Byte {
31+
fn from(src: u8) -> Self {
32+
Self::Init(src)
33+
}
34+
}
35+
3236
pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {
3337
fn has_safety_invariants(&self) -> bool;
3438
}

0 commit comments

Comments
 (0)