rucc_regalloc/moves.rs
1//! Putting a set of moves that happen at once into an order they can happen in one at a time.
2//!
3//! Design: `spec/10-backend.md` section 10.4.
4//!
5//! The parameters of a block arrive from every edge into it, and after allocation each of them is
6//! a place and each argument is a place, so an edge becomes a set of moves. They all happen at
7//! once: every argument is read as it was at the end of the predecessor, and nothing an edge
8//! writes is visible to anything else the edge writes. A machine does not have that instruction.
9//! It has one move, and the moves have to go in an order.
10//!
11//! Most of the time any order will do, but not always. Two parameters that swap two values are
12//! two moves that each destroy what the other wants to read, and no order of the two is right.
13//! The way out is a third place to keep one of the values in, which is the scratch, and the
14//! algorithm below is the one that finds out when one is needed and writes as few extra moves as
15//! it can. `spec/10-backend.md` says this is a small algorithm that is wrong in a startling number
16//! of compilers, which is why it is written here on its own and tested on its own rather than
17//! being a loop inside the allocator.
18//!
19//! Nothing here knows what a place is. A place is a register after allocation, or a stack slot
20//! for a value that was spilled, and the algorithm is the same either way, so the caller says what
21//! its places are and gets the same kind of thing back.
22
23/// One move: what is written, and what is read.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub struct Move<T> {
26 /// The place written.
27 pub to: T,
28 /// The place read.
29 pub from: T,
30}
31
32impl<T> Move<T> {
33 /// A move from one place to another.
34 pub const fn new(to: T, from: T) -> Self {
35 Self { to, from }
36 }
37}
38
39/// The moves in an order they can be made in, one at a time.
40///
41/// The result writes exactly the places the input writes, and every one of them ends up holding
42/// what the input said it should, except that the scratch may hold anything afterwards. A move
43/// from a place to itself is not in the result, because it is nothing to do.
44///
45/// The scratch is written only when a set of moves is a cycle, which is the case no order can
46/// answer on its own. Nothing else in the moves may name it, since it is the one place the
47/// algorithm is free to destroy.
48///
49/// # Panics
50///
51/// Panics if two moves write the same place. That is not a set of parallel moves, it is a
52/// question about which of two values a place ends up holding, and the caller has to answer it
53/// before asking for an order.
54#[must_use]
55pub fn sequence<T: Copy + PartialEq>(moves: &[Move<T>], scratch: T) -> Vec<Move<T>> {
56 let mut pending: Vec<Move<T>> =
57 moves.iter().copied().filter(|one| one.to != one.from).collect();
58 for (index, one) in pending.iter().enumerate() {
59 assert!(
60 !pending[..index].iter().any(|earlier| earlier.to == one.to),
61 "two parallel moves write the same place"
62 );
63 }
64
65 let mut order = Vec::with_capacity(pending.len());
66 while !pending.is_empty() {
67 // A move whose destination nothing else still has to read is one that can go now, and
68 // taking those first is what keeps a chain of moves a chain of moves.
69 let ready = pending.iter().position(|one| {
70 !pending.iter().any(|other| other.from == one.to && other.to != one.to)
71 });
72 match ready {
73 Some(index) => order.push(pending.remove(index)),
74 None => break_cycle(&mut pending, &mut order, scratch),
75 }
76 }
77 order
78}
79
80/// Puts one value of a cycle somewhere safe, which leaves the rest of it an ordinary chain.
81fn break_cycle<T: Copy + PartialEq>(pending: &mut [Move<T>], order: &mut Vec<Move<T>>, scratch: T) {
82 // Every move left is in a cycle, so any of them will do. Reading the first one's source into
83 // the scratch means nothing wants that source any more, so the move that writes it is free to
84 // go, and the move that wanted it reads the scratch instead.
85 let source = pending[0].from;
86 order.push(Move::new(scratch, source));
87 for one in pending.iter_mut().filter(|one| one.from == source) {
88 one.from = scratch;
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 /// Moves between places named by a letter, which is what a test can read.
97 fn moves(pairs: &[(char, char)]) -> Vec<Move<char>> {
98 pairs.iter().map(|&(to, from)| Move::new(to, from)).collect()
99 }
100
101 /// What the places hold after the moves are made in that order, starting from each place
102 /// holding its own name.
103 fn run(order: &[Move<char>]) -> Vec<(char, char)> {
104 let mut held: Vec<(char, char)> = ('a'..='z').map(|place| (place, place)).collect();
105 for one in order {
106 let value = held.iter().find(|&&(place, _)| place == one.from).expect("a place").1;
107 held.iter_mut().find(|(place, _)| *place == one.to).expect("a place").1 = value;
108 }
109 held
110 }
111
112 /// Whether an order leaves every place holding what the parallel moves said it should.
113 fn correct(parallel: &[Move<char>], order: &[Move<char>]) -> bool {
114 let held = run(order);
115 parallel.iter().all(|one| {
116 held.iter().find(|&&(place, _)| place == one.to).expect("a place").1 == one.from
117 })
118 }
119
120 #[test]
121 fn moves_that_get_in_nobody_s_way_are_made_in_any_order() {
122 let parallel = moves(&[('a', 'b'), ('c', 'd')]);
123 let order = sequence(¶llel, 'z');
124 assert_eq!(order.len(), 2);
125 assert!(correct(¶llel, &order));
126 }
127
128 #[test]
129 fn a_chain_is_made_from_the_end_of_it() {
130 // `a` gets what is in `b` and `b` gets what is in `c`, so `b` has to be read before it is
131 // written and no scratch is needed to see that.
132 let parallel = moves(&[('b', 'c'), ('a', 'b')]);
133 let order = sequence(¶llel, 'z');
134 assert_eq!(order, moves(&[('a', 'b'), ('b', 'c')]));
135 assert!(correct(¶llel, &order));
136 }
137
138 #[test]
139 fn two_values_that_swap_need_somewhere_to_put_one_of_them() {
140 let parallel = moves(&[('a', 'b'), ('b', 'a')]);
141 let order = sequence(¶llel, 'z');
142 assert!(correct(¶llel, &order));
143 assert_eq!(order.len(), 3);
144 assert!(order.iter().any(|one| one.to == 'z'));
145 }
146
147 #[test]
148 fn a_longer_cycle_costs_the_same_one_extra_move() {
149 // Three values going round: `a` takes `b`'s, `b` takes `c`'s and `c` takes `a`'s.
150 let parallel = moves(&[('a', 'b'), ('b', 'c'), ('c', 'a')]);
151 let order = sequence(¶llel, 'z');
152 assert!(correct(¶llel, &order));
153 assert_eq!(order.len(), 4);
154 }
155
156 #[test]
157 fn two_cycles_are_broken_one_at_a_time() {
158 let parallel = moves(&[('a', 'b'), ('b', 'a'), ('c', 'd'), ('d', 'c')]);
159 let order = sequence(¶llel, 'z');
160 assert!(correct(¶llel, &order));
161 // The scratch is reused, because it is free again as soon as the first cycle is closed.
162 assert_eq!(order.len(), 6);
163 }
164
165 #[test]
166 fn a_value_wanted_in_two_places_is_read_twice() {
167 let parallel = moves(&[('a', 'c'), ('b', 'c')]);
168 let order = sequence(¶llel, 'z');
169 assert!(correct(¶llel, &order));
170 assert_eq!(order.len(), 2);
171 assert!(!order.iter().any(|one| one.to == 'z'));
172 }
173
174 #[test]
175 fn a_cycle_with_a_tail_hanging_off_it_is_still_one_extra_move() {
176 // `d` also wants what is in `a`, which is not part of the cycle and has to be read before
177 // the cycle overwrites it.
178 let parallel = moves(&[('a', 'b'), ('b', 'a'), ('d', 'a')]);
179 let order = sequence(¶llel, 'z');
180 assert!(correct(¶llel, &order));
181 assert_eq!(order.len(), 4);
182 }
183
184 #[test]
185 fn a_move_from_a_place_to_itself_is_nothing_to_do() {
186 let order = sequence(&moves(&[('a', 'a'), ('b', 'c')]), 'z');
187 assert_eq!(order, moves(&[('b', 'c')]));
188 }
189
190 #[test]
191 fn nothing_to_move_is_nothing_to_do() {
192 assert_eq!(sequence::<char>(&[], 'z'), []);
193 }
194
195 #[test]
196 #[should_panic(expected = "two parallel moves write the same place")]
197 fn two_moves_that_write_one_place_are_not_a_question_this_can_answer() {
198 let _ = sequence(&moves(&[('a', 'b'), ('a', 'c')]), 'z');
199 }
200}