rucc_codegen/switch.rs
1//! What a `switch` becomes on the way to the machine, and how that shape is chosen.
2//!
3//! Design: `spec/optimizer/24-switch-lowering.md`. Section 24.4 puts the choice here, at the
4//! boundary into the machine level and nowhere earlier, and section 24.2 says what the choice
5//! looks like.
6//!
7//! # Why the shape is decided here and not in the front end
8//!
9//! What a `switch` should become is a target decision and not a language one. A chain of compares
10//! is right for three cases and wrong for two hundred, where the answer is a jump table, and wrong
11//! again for twenty spread over a million, where it is a binary search on the value. A front end
12//! that picked one would be picking for every target at once, and the IR would no longer hold what
13//! the program said. So the `switch` survives as far as here, and here is where it is given up.
14//!
15//! Keeping it whole that long buys something on the way as well. A `switch` is one node from which
16//! the range on each outgoing edge is exact: on the edge to case five the operand is five, and on
17//! the default edge it is outside the case set. A `switch` lowered early is a pile of branches that
18//! every pass afterwards has to work those facts back out of.
19//!
20//! # A switch is a partition and not a shape
21//!
22//! The reason to sort the cases and cut them into runs, rather than pick one shape for the whole
23//! statement, is that a real `switch` is more than one thing at once. A `switch` in a parser has a
24//! dense stretch of ASCII values best served by a jump table, a few scattered large constants best
25//! served by comparisons, and a set of aliased cases best served by a bit test, all in the same
26//! statement. A design that picks one shape for the whole of it cannot say that. So the case list
27//! is sorted, partitioned into clusters, and a decision tree is built over the clusters.
28//!
29//! All four shapes are written. A `Cluster::One` is one case value and one equality test,
30//! which is what every case was before this module existed. A `Cluster::Run` is a stretch of
31//! consecutive values that all go to the same place, and it is one subtraction and one unsigned
32//! comparison however long the stretch is, which is what makes `case 'a' ... 'z'` twenty six cases
33//! in the IR and two instructions in the machine code. A `Cluster::Bits` is a set of values
34//! scattered through a span narrower than a word, each destination holding the bits of a mask, and
35//! it is one shift and one test per destination however many values are in it, which is what makes
36//! `case 'a': case 'e': case 'i': case 'o': case 'u':` five compares before this and one after.
37//!
38//! A `Cluster::Table` is a stretch of clusters dense enough that a table with one cell per value
39//! is cheaper than testing them, and it is one range check and one jump through the table however
40//! many cases are in it, which is what the dispatch loop of an interpreter wants and what
41//! tamnd/rucc#1548 found missing: pcre2's matcher is a `switch` of about a hundred opcodes, and
42//! walking a tree down to one of them on every step cost more than four times what gcc's table
43//! did. The table is not written here. What is written is the range check and then a `switch`
44//! again, on the value less the lowest case and widened to a word, which `crate::lower` turns into
45//! the load and the jump, and `rucc_asm` puts the table itself after the function's last
46//! instruction, where both ends of every distance in it are in one section and nothing is left for
47//! a linker. See `JUMP_TABLE_GROWTH` for what dense means.
48//!
49//! # Why the tree compares signed
50//!
51//! The IR gives a `switch` a width and not a signedness, because signedness in this IR is a
52//! property of an operation rather than of a type, so there is nothing here to ask whether the
53//! program switched on an `int` or an `unsigned`. What makes that harmless is that the sort and the
54//! tree use the same order: the cases are sorted by their signed reading and the tree splits with a
55//! signed comparison, so the tree is consistent with itself and every value comes down it to the
56//! one cluster that can hold it. Sorting one way and comparing the other is the bug this is
57//! written to not have.
58//!
59//! A run is not affected either way. Testing `x - low` against `high - low` unsigned is modular
60//! arithmetic and gives the same answer whichever way the operand is read.
61//!
62//! # What it refuses to get wrong
63//!
64//! Section 24.6 lists the ways this goes wrong and two of them are arithmetic. The width of a run
65//! is worked out in `i128`, which holds the difference of any two values of any type C can switch
66//! on, so nothing here overflows the way the same computation in the switch's own type would. A run
67//! that covers a whole type comes out as a width of every bit set, which read as an unsigned
68//! comparison is a test that is true of everything, and that is exactly right for a `switch` no
69//! value falls out of.
70//!
71//! The third is the default edge, and the rule is that it is never dropped. Every leaf of the tree
72//! ends by branching to the default, so a value that matches nothing arrives there whichever way it
73//! came down, and there is no path through any of this that leaves a block without saying where
74//! control goes next.
75//!
76//! The fourth is the bit test's shift. Shifting by more than the width of the word being shifted is
77//! undefined, and the amount is the operand, so it is the operand that has to be shown to be in
78//! range first. Section 24.6 is firm that the bound before the shift is not an optimization
79//! decision and cannot be dropped when the value looks like it has to be in range, and here it is
80//! written by the same code that writes the shift rather than added afterwards.
81//!
82//! # What it does not carry yet
83//!
84//! Section 24.5 asks for document 11's `Frequency` on every cluster from the start, so that the
85//! tree can lean towards the hot cases rather than be balanced, and so that adding it later is not
86//! a change to every place a cluster is built. It is not here because there is nowhere to read it
87//! from. Block frequencies are worked out in `rucc-opt`, which is above this crate rather than
88//! below it, and what would carry the number down is the IR, which has nowhere to put it yet.
89
90use rucc_cost::heuristics::JUMP_TABLE_MIN_TARGETS;
91use rucc_diag::Span;
92use rucc_ir::{
93 Block, BlockCall, Builder, Extra, Flags, Func, Imm, Inst, IntPred, Opcode, Type, Value,
94};
95
96/// The most clusters a leaf of the decision tree tests one at a time, which is also the count below
97/// which nothing new is built at all. A `switch` of this many clusters or fewer stays the chain of
98/// compares it has always been, in the block it has always been in.
99///
100/// Thirty two, and the number is measured rather than picked. What it trades is not a comparison
101/// against a comparison, which is what it looks like on paper and is the reason a small number looks
102/// right. A walk of `n` clusters is `n` compares and a search is about `log2(n)`, so on paper the
103/// search wins from about five cases upward and the threshold should be about five.
104///
105/// The machine does not agree, because the two kinds of comparison do not cost the same. Every
106/// compare in a walk is a branch that is almost never taken, one case out of `n`, so the predictor
107/// gets all of them right and the front end runs through them several per cycle. Every branch in a
108/// search is a branch that goes each way about half the time, so the predictor gets a fair share of
109/// them wrong and each of those costs the whole pipeline. Twenty compares nobody mispredicts are
110/// cheaper than six branches that mispredict a third of the time, and that stays true further up
111/// than it seems it should.
112///
113/// Measured on an interpreter loop dispatching on a sparse `switch`, four million iterations picking
114/// a case at random, the walk is ahead up to about thirty two cases and the search is ahead above
115/// about thirty six. At seventeen cases a search costs sixteen percent, at twenty four it costs
116/// twenty two, at thirty six it saves nine, at fifty it saves twenty three and at a hundred it saves
117/// half. Thirty two is where those two lines cross.
118///
119/// Two things would move it. The first is the jump table, which is what a dense `switch` this large
120/// becomes now, so the cases that reach the tree are the sparse ones. It was measured before the
121/// table was written, on a sparse `switch`, and a sparser search may be worth starting sooner now
122/// that nothing dense is left in it.
123/// The second is knowing which case is hot, because a walk that tests the common case first is
124/// cheaper than any search and the tree cannot use that ordering. That is document 11's `Frequency`
125/// and it is not carried here yet.
126///
127/// gcc has the same knob under the name `case-values-threshold` and a small number in it, which is
128/// the right number for gcc because gcc reaches for a jump table first and the tree is what it falls
129/// back to on cases a table cannot hold.
130pub const LINEAR: usize = 32;
131
132/// Rewrites every `switch` in the function into branches, and leaves everything else alone.
133///
134/// The function is changed in place, which is what makes this the last thing that reads the IR as
135/// the front end built it. `--emit=ir` prints before this runs, and nothing after this asks what
136/// the program said, only what the machine has to do.
137pub fn switches(func: &mut Func) {
138 let found: Vec<Inst> = func
139 .blocks()
140 .filter_map(|block| func.terminator(block))
141 .filter(|&inst| func[inst].opcode == Opcode::Switch)
142 .collect();
143 for inst in found {
144 lower(func, inst);
145 }
146}
147
148/// One `switch`, as the clusters its cases fall into and a decision tree over them.
149fn lower(func: &mut Func, inst: Inst) {
150 let block = func.block_of(inst).expect("a terminator is in a block");
151 let span = func.span(inst);
152 let Extra::Switch(info) = func[inst].extra else { return };
153 let info = func[info];
154 let Some(&value) = func[func[inst].args].first() else { return };
155 // The lane, because a `switch` on a vector is not a thing C can write and the immediates are
156 // an integer's either way.
157 let ty = func[value].ty.lane();
158 let calls: Vec<BlockCall> = func[info.targets].to_vec();
159 let cases: Vec<Imm> = func[info.cases].to_vec();
160 let Some((&default, arms)) = calls.split_first() else { return };
161 let clusters = group(func, tables(func, clusters(func, &cases, arms, ty), ty));
162
163 // Before anything is written, because the builder appends and the `switch` is where the
164 // appending has to happen.
165 func.remove_inst(inst);
166 tree(func, &Lowering { value, ty, default, span }, block, &clusters);
167}
168
169/// What every test written for one `switch` shares.
170///
171/// The tree hands the same four things down to every leaf and every leaf hands them to every test,
172/// so they travel together rather than as four more parameters at each step.
173struct Lowering {
174 /// The operand being switched on.
175 value: Value,
176 /// Its width, which every constant written here takes.
177 ty: Type,
178 /// Where a value that matches no case goes, which is every leaf's last edge.
179 default: BlockCall,
180 /// The source location of the `switch`, which everything written for it takes.
181 span: Span,
182}
183
184/// A stretch of case values that one test separates from the rest of them.
185///
186/// This is the structure `spec/optimizer/24-switch-lowering.md` section 24.2 describes, with all
187/// four of its variants. It is an enum rather than a struct with a low and a high in it because the
188/// last one carries something the others do not: a jump table carries a table, and adding it was a
189/// variant here and an arm in [`test`] rather than a change to how a `switch` is taken apart.
190#[derive(Clone, Debug)]
191enum Cluster {
192 /// One case value, which is one equality test.
193 One {
194 /// The value the operand has to equal.
195 value: i128,
196 /// Where it goes when it does.
197 call: BlockCall,
198 },
199 /// Every value from `low` to `high`, all of which go to the same place.
200 Run {
201 /// The lowest value in the run.
202 low: i128,
203 /// The highest, which is at least one above the lowest.
204 high: i128,
205 /// Where any of them goes.
206 call: BlockCall,
207 },
208 /// Every value from `low` to `high` looked up in a table, with the ones no case names going to
209 /// the default.
210 Table {
211 /// The lowest value in the table, which is the first cell.
212 low: i128,
213 /// The highest, which is the last cell.
214 high: i128,
215 /// Every case value in the table and where it goes, lowest first. A run is one entry per
216 /// value, because a run is one cell per value in a table.
217 arms: Vec<(i128, BlockCall)>,
218 },
219 /// Values scattered through `low` to `high` going to several places, each place being the bits
220 /// of one mask.
221 Bits {
222 /// The lowest value any of the masks names, which every bit is counted from.
223 low: i128,
224 /// The highest, which is less than a word above the lowest.
225 high: i128,
226 /// One mask per destination, in the order the destinations were first seen. Bit `n` of a
227 /// mask is set when the value `low + n` goes to that destination.
228 arms: Vec<(u64, BlockCall)>,
229 },
230}
231
232impl Cluster {
233 /// The lowest value this cluster holds.
234 fn low(&self) -> i128 {
235 match *self {
236 Self::One { value, .. } => value,
237 Self::Run { low, .. } | Self::Bits { low, .. } | Self::Table { low, .. } => low,
238 }
239 }
240
241 /// The highest value this cluster holds.
242 fn high(&self) -> i128 {
243 match *self {
244 Self::One { value, .. } => value,
245 Self::Run { high, .. } | Self::Bits { high, .. } | Self::Table { high, .. } => high,
246 }
247 }
248
249 /// Whether every value in this cluster goes where that edge goes.
250 ///
251 /// A bit test never does, because it has more than one destination and this is only asked in
252 /// order to merge two clusters into one run. Grouping happens after that merging and never
253 /// before it, so the question does not come up, and answering no is right either way.
254 fn goes_to(&self, func: &Func, call: BlockCall) -> bool {
255 match *self {
256 Self::One { call: mine, .. } | Self::Run { call: mine, .. } => same(func, mine, call),
257 Self::Bits { .. } | Self::Table { .. } => false,
258 }
259 }
260
261 /// Grows the cluster upwards to a value, which the caller has already checked is the one
262 /// immediately above it and goes to the same place.
263 fn grow(&mut self, value: i128) {
264 let call = match *self {
265 Self::One { call, .. } | Self::Run { call, .. } => call,
266 Self::Bits { .. } | Self::Table { .. } => {
267 unreachable!("a bit test or a table is never grown into a run")
268 }
269 };
270 *self = Self::Run { low: self.low(), high: value, call };
271 }
272}
273
274/// How many cells a table may have for each comparison it replaces, which is what dense means.
275///
276/// Eight, and it is gcc's number rather than one measured here: `jump-table-max-growth-ratio-for-
277/// speed` is 800 percent, counted the way this counts, with a single value as one comparison and a
278/// run as two. It is a size bound rather than a speed one. A table is faster than a tree over the
279/// same cases at any density a `switch` is written at, since it is one load and one jump however
280/// many cases there are, so what stops a table from covering a sparse `switch` is the four bytes a
281/// cell costs against the few bytes a comparison does. Eight cells for each comparison is where
282/// gcc stops paying that, and agreeing with it means a table here is a table there, which is what
283/// the corpus reports compare.
284const JUMP_TABLE_GROWTH: i128 = 8;
285
286/// The clusters again, with each stretch dense enough for a table turned into one.
287///
288/// Greedy, the way [`group`] is: each position takes the longest stretch from there that is dense
289/// enough and has enough clusters in it, and either takes the whole stretch or takes one cluster
290/// and moves on. gcc finds the best partition with a quadratic search, and the difference shows
291/// only on a `switch` with two dense stretches overlapping in a way a greedy scan cuts in the
292/// wrong place, which is rare enough that the simpler one is what is here.
293///
294/// Before [`group`] rather than after it, because a table is cheaper than a bit test over the same
295/// values once there are enough of them, and after it the single values a table wants would
296/// already be gone into masks. Only on an operand a word wide or narrower, since the index a
297/// table is read with is a word and a wider operand does not fit in one.
298fn tables(func: &Func, clusters: Vec<Cluster>, ty: Type) -> Vec<Cluster> {
299 if ty.bits() == 0 || ty.bits() > u64::BITS {
300 return clusters;
301 }
302 let mut out: Vec<Cluster> = Vec::with_capacity(clusters.len());
303 let mut at = 0;
304 while at < clusters.len() {
305 match dense(func, &clusters[at..]) {
306 Some(end) => {
307 out.push(table(&clusters[at..at + end]));
308 at += end;
309 }
310 None => {
311 out.push(clusters[at].clone());
312 at += 1;
313 }
314 }
315 }
316 out
317}
318
319/// How many clusters from the front of these make the longest stretch a table is worth writing
320/// for, or nothing when no stretch is.
321///
322/// Dense is what gcc's `jump_table_cluster::can_be_handled` says it is: the values the table
323/// covers are at most [`JUMP_TABLE_GROWTH`] times the comparisons it replaces. Worth writing is at
324/// least [`JUMP_TABLE_MIN_TARGETS`] clusters, below which the range check, the load and the
325/// indirect jump are more than the compares they replace.
326///
327/// A stretch inside one word going to [`BIT_TEST_TARGETS`] places or fewer is left for [`group`],
328/// because a bit test over it is that many tests and no load, which is the choice gcc makes too.
329///
330/// The scan stops once the span is wider than every cluster left could pay for even if each were
331/// a run, since the span only grows and the count cannot catch it after that.
332fn dense(func: &Func, clusters: &[Cluster]) -> Option<usize> {
333 let low = clusters.first()?.low();
334 let most = 2 * i128::try_from(clusters.len()).ok()?;
335 let least = usize::try_from(JUMP_TABLE_MIN_TARGETS).ok()?;
336 let mut compares: i128 = 0;
337 let mut places: Vec<BlockCall> = Vec::new();
338 let mut best = None;
339 for (index, cluster) in clusters.iter().enumerate() {
340 let call = match *cluster {
341 Cluster::One { call, .. } => {
342 compares += 1;
343 call
344 }
345 Cluster::Run { call, .. } => {
346 compares += 2;
347 call
348 }
349 Cluster::Bits { .. } | Cluster::Table { .. } => return best,
350 };
351 if places.len() <= BIT_TEST_TARGETS && !places.iter().any(|&seen| same(func, seen, call)) {
352 places.push(call);
353 }
354 let span = cluster.high() - low + 1;
355 if span > JUMP_TABLE_GROWTH * most {
356 break;
357 }
358 let masks = span <= WORD && places.len() <= BIT_TEST_TARGETS;
359 if index + 1 >= least && span <= JUMP_TABLE_GROWTH * compares && !masks {
360 best = Some(index + 1);
361 }
362 }
363 best
364}
365
366/// The most destinations a stretch can have and still be left for a bit test rather than made a
367/// table. Three, which is gcc's `m_max_case_bit_tests`: past that the tests one after another cost
368/// more than the load and the jump.
369const BIT_TEST_TARGETS: usize = 3;
370
371/// One table over a stretch of clusters that [`dense`] said makes one.
372fn table(stretch: &[Cluster]) -> Cluster {
373 let mut arms = Vec::new();
374 for cluster in stretch {
375 match *cluster {
376 Cluster::One { value, call } => arms.push((value, call)),
377 Cluster::Run { low, high, call } => {
378 arms.extend((low..=high).map(|value| (value, call)))
379 }
380 Cluster::Bits { .. } | Cluster::Table { .. } => {
381 unreachable!("tables are found before anything is grouped")
382 }
383 }
384 }
385 let low = stretch.first().map_or(0, Cluster::low);
386 let high = stretch.last().map_or(0, Cluster::high);
387 Cluster::Table { low, high, arms }
388}
389
390/// The widest span of values one bit test covers, which is the width of the word its mask lives in.
391///
392/// This is a correctness bound and not a tuning one, which is what section 24.6 asks it to be.
393/// `1 << (x - low)` is undefined once `x - low` reaches the width of the word being shifted, so a
394/// group is only ever formed inside this span and the range check in front of the shift is what
395/// makes the shift amount stay there. Sixty four because the mask is held in an `i64`, which every
396/// target this compiler has can shift by a register.
397const WORD: i128 = 64;
398
399/// How many more case values a group needs than it has destinations before a bit test is worth
400/// writing.
401///
402/// Three, and it comes from counting instructions rather than from anywhere else. A bit test is a
403/// subtraction, a comparison and a branch for the range check, then a shift, then a mask and a
404/// branch for each destination: five instructions and two more per destination. What it replaces is
405/// two instructions per case value. So `n` values going to `t` destinations cost `2n` as compares
406/// and `5 + 2t` as a bit test, the two are level when `n` is two and a half clear of `t`, and three
407/// is the first whole number above that.
408///
409/// Unlike [`LINEAR`] this one is not fighting the branch predictor, which is why counting is enough
410/// here and was not enough there. A walk over `n` values and a bit test over the same `n` both end
411/// in a branch that is taken about as often, so what is left between them is the instruction count.
412const MARGIN: usize = 3;
413
414/// The case list sorted and cut into clusters.
415///
416/// Sorting is what makes the rest of this possible: a decision tree needs an order to split on, and
417/// a run of consecutive values is only visible once the values are next to each other. It is
418/// `n log n` and it is the most expensive thing in the module, which section 24.7 says is fine
419/// because everything here is cheap next to the size of the construct.
420///
421/// # Panics
422///
423/// Panics on two cases of the same value. C forbids them and the front end rejects them, so
424/// everything below is written believing the clusters are disjoint, and section 24.6 asks for that
425/// belief to be recorded here rather than left implicit. Dropping the later of the pair instead
426/// would leave its arm with nothing branching to it, which is a function the IR verifier refuses,
427/// and quietly keeping both would put two clusters of the same value into a search that assumes it
428/// can tell them apart. A `switch` that arrives with a duplicate is a bug above this, and stopping
429/// on it is how it gets found.
430fn clusters(func: &Func, cases: &[Imm], arms: &[BlockCall], ty: Type) -> Vec<Cluster> {
431 let mut sorted: Vec<(i128, BlockCall)> =
432 cases.iter().zip(arms).map(|(&imm, &call)| (imm.signed(ty), call)).collect();
433 sorted.sort_by_key(|&(value, _)| value);
434 assert!(
435 sorted.windows(2).all(|pair| pair[0].0 != pair[1].0),
436 "a switch with two cases of the same value reached the back end"
437 );
438
439 let mut clusters: Vec<Cluster> = Vec::with_capacity(sorted.len());
440 for (value, call) in sorted {
441 match clusters.last_mut() {
442 // In `i128`, so that a run reaching the top of its own type is the addition it looks
443 // like rather than an overflow.
444 Some(last) if last.high() + 1 == value && last.goes_to(func, call) => {
445 last.grow(value);
446 }
447 _ => clusters.push(Cluster::One { value, call }),
448 }
449 }
450 clusters
451}
452
453/// Whether two edges go to the same block carrying the same values.
454///
455/// Both halves matter. Two cases whose arms are the same block but which pass it different
456/// arguments are two different destinations, and merging them into a run would hand the block one
457/// of the two whichever value arrived.
458fn same(func: &Func, a: BlockCall, b: BlockCall) -> bool {
459 a.block == b.block && func[a.args] == func[b.args]
460}
461
462/// The clusters again, with stretches of single values turned into bit tests where that is fewer
463/// instructions.
464///
465/// This is section 24.3's grouping phase and it is the greedy one. Each position takes the longest
466/// stretch of single values that fits inside a word, asks whether a bit test over it is worth
467/// writing, and either takes the whole stretch or takes one cluster and moves on. The document says
468/// the optimal partition is quadratic and is only justified on large switches, which are exactly the
469/// switches where compile time is already the thing being spent, so the greedy one is what is here
470/// and the other one is recorded rather than written.
471///
472/// Only single values are grouped. A run is already one subtraction and one comparison however many
473/// values it holds, so folding it into a mask replaces two instructions with two instructions and
474/// spends a word of the span doing it. That is a loss on the run and a loss on whatever the span
475/// would otherwise have reached.
476fn group(func: &Func, clusters: Vec<Cluster>) -> Vec<Cluster> {
477 let mut out: Vec<Cluster> = Vec::with_capacity(clusters.len());
478 let mut at = 0;
479 while at < clusters.len() {
480 let reach = reach(&clusters, at);
481 match bits(func, &clusters[at..at + reach]) {
482 Some(cluster) => {
483 out.push(cluster);
484 at += reach;
485 }
486 None => {
487 out.push(clusters[at].clone());
488 at += 1;
489 }
490 }
491 }
492 out
493}
494
495/// How many single values starting here sit inside one word of the first of them.
496fn reach(clusters: &[Cluster], at: usize) -> usize {
497 let Cluster::One { value: first, .. } = clusters[at] else { return 0 };
498 let mut reach = 0;
499 while let Some(Cluster::One { value, .. }) = clusters.get(at + reach) {
500 if value - first >= WORD {
501 break;
502 }
503 reach += 1;
504 }
505 reach
506}
507
508/// The masks for a stretch of single values, or nothing when the compares are the cheaper answer.
509///
510/// One mask per destination rather than one per value, which is the whole point: `case 'a': case
511/// 'e': case 'i': case 'o': case 'u':` is five values and one destination, so it is one mask and one
512/// test rather than five compares.
513fn bits(func: &Func, group: &[Cluster]) -> Option<Cluster> {
514 let low = group.first()?.low();
515 let mut arms: Vec<(u64, BlockCall)> = Vec::new();
516 for cluster in group {
517 let Cluster::One { value, call } = *cluster else { return None };
518 // Shifting is safe because `reach` only gathered values inside one word of `low`.
519 let bit = 1u64 << (value - low);
520 match arms.iter_mut().find(|&&mut (_, mine)| same(func, mine, call)) {
521 Some((mask, _)) => *mask |= bit,
522 None => arms.push((bit, call)),
523 }
524 }
525 if group.len() < arms.len() + MARGIN {
526 return None;
527 }
528 Some(Cluster::Bits { low, high: group.last()?.high(), arms })
529}
530
531/// A binary search over the clusters, ending in a chain of tests at each leaf.
532///
533/// The split is at the middle of the list and the test is whether the operand is below the lowest
534/// value of the upper half. Everything the lower half holds is below that value because the list is
535/// sorted and the clusters are disjoint, so an operand that is below it and matches anything at all
536/// matches something in the lower half, and one that is not is either in the upper half or in
537/// neither. Either way it reaches a leaf that tests what is left, and the leaf sends it to the
538/// default when none of that matches.
539fn tree(func: &mut Func, of: &Lowering, at: Block, clusters: &[Cluster]) {
540 if clusters.len() <= LINEAR {
541 chain(func, of, at, clusters);
542 return;
543 }
544 let (below, above) = clusters.split_at(clusters.len() / 2);
545 let pivot = above[0].low();
546 let left = func.create_block();
547 let right = func.create_block();
548
549 let mut build = Builder::new(func, at).at(of.span);
550 let want = build.iconst(of.ty, pivot);
551 let under = build.icmp(IntPred::Slt, of.value, want);
552 build.br_if(under, left, &[], right, &[]);
553
554 tree(func, of, left, below);
555 tree(func, of, right, above);
556}
557
558/// The clusters tested one after another, each falling to the next and the last to the default.
559///
560/// The block this starts in gets the first test, and each test after the first gets a block of its
561/// own that the one before it falls to when its test failed. The last falls to the default, so the
562/// default is not a block anything is created for and a chain of `n` clusters costs `n` less one.
563fn chain(func: &mut Func, of: &Lowering, at: Block, clusters: &[Cluster]) {
564 // A leaf with nothing in it is a jump. It is what a `switch` whose only label is `default` is,
565 // and it is also what one whose cases a later pass folded away would be.
566 let Some((last, rest)) = clusters.split_last() else {
567 let args: Vec<Value> = func[of.default.args].to_vec();
568 Builder::new(func, at).at(of.span).jump(of.default.block, &args);
569 return;
570 };
571
572 let mut at = at;
573 for cluster in rest {
574 let next = func.create_block();
575 test(func, of, at, cluster, next, &[]);
576 at = next;
577 }
578 let onward: Vec<Value> = func[of.default.args].to_vec();
579 test(func, of, at, last, of.default.block, &onward);
580}
581
582/// One cluster, as the comparison that decides it and the branch that acts on it.
583fn test(
584 func: &mut Func,
585 of: &Lowering,
586 at: Block,
587 cluster: &Cluster,
588 next: Block,
589 onward: &[Value],
590) {
591 if matches!(cluster, Cluster::Bits { .. }) {
592 scattered(func, of, at, cluster, next, onward);
593 return;
594 }
595 if matches!(cluster, Cluster::Table { .. }) {
596 looked_up(func, of, at, cluster, next, onward);
597 return;
598 }
599 let call = match *cluster {
600 Cluster::One { call, .. } | Cluster::Run { call, .. } => call,
601 Cluster::Bits { .. } | Cluster::Table { .. } => unreachable!("dealt with above"),
602 };
603 let taken: Vec<Value> = func[call.args].to_vec();
604 let mut build = Builder::new(func, at).at(of.span);
605 let matched = match *cluster {
606 Cluster::One { value, .. } => {
607 let want = build.iconst(of.ty, value);
608 build.icmp(IntPred::Eq, of.value, want)
609 }
610 Cluster::Run { low, high, .. } => {
611 let base = shifted_down(&mut build, of, low);
612 let width = build.iconst(of.ty, high - low);
613 build.icmp(IntPred::Ule, base, width)
614 }
615 Cluster::Bits { .. } | Cluster::Table { .. } => unreachable!("dealt with above"),
616 };
617 build.br_if(matched, call.block, &taken, next, onward);
618}
619
620/// A dense stretch, as one range check and then a `switch` on the value less the lowest case,
621/// which `crate::lower` turns into a jump through a table.
622///
623/// The range check is the same one a run is, and it is what lets the `switch` behind it be a table
624/// with no check of its own: every value that gets past it has a cell. A value in the range that
625/// no case names goes to the default, for the reason [`scattered`] gives, and the `switch` says so
626/// by having the default as its own and no case for that value.
627///
628/// An arm that carries values into the block it goes to gets a block of its own in front of it
629/// that passes them, and the `switch` goes there with nothing on the edge. A jump through a
630/// register has nowhere to put the moves an edge with values on it needs, which is what
631/// `crate::split::indirect` works round for a computed `goto` and what one `switch` sending two
632/// cases to the same block with different values would get wrong, since a block reached from one
633/// jump gets one set of moves. A block per distinct edge is the same thing done before anything
634/// can go wrong, and it is where the moves would have been anyway.
635fn looked_up(
636 func: &mut Func,
637 of: &Lowering,
638 at: Block,
639 cluster: &Cluster,
640 next: Block,
641 onward: &[Value],
642) {
643 let Cluster::Table { low, high, arms } = cluster else {
644 unreachable!("only a table is written as one");
645 };
646 let (low, high) = (*low, *high);
647 let inside = func.create_block();
648 let mut hops: Vec<(BlockCall, Block)> = Vec::new();
649 let mut hop = |func: &mut Func, call: BlockCall| -> Block {
650 if func[call.args].is_empty() {
651 return call.block;
652 }
653 if let Some(&(_, block)) = hops.iter().find(|&&(mine, _)| same(func, mine, call)) {
654 return block;
655 }
656 let block = func.create_block();
657 hops.push((call, block));
658 block
659 };
660 let default = hop(func, of.default);
661 let cases: Vec<(i128, Block)> =
662 arms.iter().map(|&(value, call)| (value - low, hop(func, call))).collect();
663
664 let mut build = Builder::new(func, at).at(of.span);
665 let base = shifted_down(&mut build, of, low);
666 let width = build.iconst(of.ty, high - low);
667 let ok = build.icmp(IntPred::Ule, base, width);
668 build.br_if(ok, inside, &[], next, onward);
669
670 // In a word, because that is what an address is added up in. The range check above is what
671 // makes widening without the sign the right widening: what gets here is between zero and the
672 // width, read unsigned.
673 let word = Type::int(u64::BITS);
674 let mut build = Builder::new(func, inside).at(of.span);
675 let index = if of.ty == word { base } else { build.unary(Opcode::ZExt, base, word) };
676 build.switch(index, default, &cases);
677
678 for (call, block) in hops {
679 let args: Vec<Value> = func[call.args].to_vec();
680 Builder::new(func, block).at(of.span).jump(call.block, &args);
681 }
682}
683
684/// `x - low`, or `x` itself when the stretch starts at zero and there is nothing to take off it.
685///
686/// Compared unsigned against `high - low` this is one comparison covering both ends of a stretch: a
687/// value below the bottom wraps round to something enormous and fails the same test a value above
688/// the top fails. It is also what a bit test counts its bits from, which is why it is here rather
689/// than written out twice.
690fn shifted_down(build: &mut Builder<'_>, of: &Lowering, low: i128) -> Value {
691 if low == 0 {
692 return of.value;
693 }
694 let start = build.iconst(of.ty, low);
695 build.binary(Opcode::Sub, of.value, start, Flags::default())
696}
697
698/// A stretch of scattered values, as one range check and then one mask test per destination.
699///
700/// The shape is the one `gcc/tree-switch-conversion.h` states: `if ((1 << (x - low)) & mask)`. The
701/// range check comes first and is not an optimisation. It is what makes the shift defined, since a
702/// shift by the width of the word or more has no answer, and section 24.6 names this as the way a
703/// bit test goes wrong and the range check as the defence.
704///
705/// A value inside the range matching no mask goes to the default rather than on to the next test.
706/// The group is a stretch of clusters that were next to each other in the sorted list, so every case
707/// outside it is outside the range as well, and a value in the range that matched no mask has
708/// already been shown to match nothing at all.
709fn scattered(
710 func: &mut Func,
711 of: &Lowering,
712 at: Block,
713 cluster: &Cluster,
714 next: Block,
715 onward: &[Value],
716) {
717 let Cluster::Bits { low, high, arms } = cluster else {
718 unreachable!("only a bit test is written as one");
719 };
720 let (low, high) = (*low, *high);
721
722 // Every value in the range is named by some mask when the masks together cover it, and then the
723 // last destination needs no test of its own: it is where anything that got past the others goes.
724 // Asking for more than one destination is what keeps at least one test, and a lone destination
725 // covering a whole range is a run rather than a bit test anyway.
726 let all = arms.iter().fold(0u64, |seen, &(mask, _)| seen | mask);
727 let covered = arms.len() > 1 && all == span_mask(low, high);
728 let tests = arms.len() - usize::from(covered);
729 let (spare, onto_spare) = if covered {
730 let call = arms[arms.len() - 1].1;
731 (call.block, func[call.args].to_vec())
732 } else {
733 (of.default.block, func[of.default.args].to_vec())
734 };
735
736 // All of them before a builder exists, because a builder holds the function and a block cannot
737 // be made while it does.
738 let inside = func.create_block();
739 let mut blocks: Vec<Block> = vec![inside];
740 blocks.extend((1..tests).map(|_| func.create_block()));
741 let taken: Vec<Vec<Value>> = arms.iter().map(|&(_, call)| func[call.args].to_vec()).collect();
742
743 let mut build = Builder::new(func, at).at(of.span);
744 let base = shifted_down(&mut build, of, low);
745 let width = build.iconst(of.ty, high - low);
746 let ok = build.icmp(IntPred::Ule, base, width);
747 build.br_if(ok, inside, &[], next, onward);
748
749 // In a word, because that is the width the masks are and what the top of the range needs for a
750 // bit of its own. The range check above is what makes this shift amount a legal one.
751 let word = Type::int(u64::BITS);
752 let mut build = Builder::new(func, inside).at(of.span);
753 let amount = if of.ty == word { base } else { build.unary(Opcode::ZExt, base, word) };
754 let one = build.iconst(word, 1);
755 let bit = build.binary(Opcode::Shl, one, amount, Flags::default());
756
757 for (index, &(mask, call)) in arms[..tests].iter().enumerate() {
758 let want = build.iconst(word, i128::from(mask as i64));
759 let hit = build.binary(Opcode::And, bit, want, Flags::default());
760 let none = build.iconst(word, 0);
761 let matched = build.icmp(IntPred::Ne, hit, none);
762 let last = index + 1 == tests;
763 let onto = if last { spare } else { blocks[index + 1] };
764 let args = if last { &onto_spare[..] } else { &[][..] };
765 build.br_if(matched, call.block, &taken[index], onto, args);
766 if !last {
767 build = Builder::new(func, blocks[index + 1]).at(of.span);
768 }
769 }
770}
771
772/// The bits of a word that a range from `low` to `high` names, counted from `low`.
773///
774/// The width is one less than a word at most, because that is what [`reach`] gathers, so the shift
775/// below is a legal one and the answer is every bit the range can reach and no bit above it.
776fn span_mask(low: i128, high: i128) -> u64 {
777 let width = u32::try_from(high - low).expect("a group narrower than a word");
778 if width + 1 >= u64::BITS { u64::MAX } else { (1u64 << (width + 1)) - 1 }
779}
780
781/// The blocks a leaf chain of `n` clusters needs beyond the ones the program already had.
782///
783/// Here so that a test can say the number rather than count it, and so that whoever writes the jump
784/// table has one place to compare against. A `switch` that goes to a tree needs more than this,
785/// since the tree's own nodes are blocks too, and a test that cares about one of those counts them.
786#[must_use]
787pub fn blocks_for(clusters: usize) -> usize {
788 clusters.saturating_sub(1)
789}
790
791#[cfg(test)]
792mod tests {
793 use std::collections::HashMap;
794
795 use rucc_base::Interner;
796 use rucc_ir::{
797 Block, BlockCall, Builder, Extra, Func, Imm, InstData, IntPred, Module, Opcode, Signature,
798 SwitchInfo, Type, Value,
799 };
800 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
801
802 use super::{LINEAR, blocks_for, switches};
803
804 fn target() -> TargetInfo {
805 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
806 }
807
808 /// Where a `switch` over these cases can end up, as the arms in the order given and the default.
809 struct Built {
810 names: Interner,
811 func: Func,
812 operand: Value,
813 arms: Vec<Block>,
814 default: Block,
815 }
816
817 /// `int sw(int x) { switch (x) { case 1: return 10; ... default: return 0; } }` as the walk
818 /// builds it, which is the program in issue 275.
819 ///
820 /// Every arm is a block of its own even when two cases would naturally share one, because a
821 /// test that wants two cases going to one place says so by passing the same block twice, and
822 /// [`built_sharing`] is how it does that.
823 fn built(cases: &[i128]) -> Built {
824 let arms: Vec<usize> = (0..cases.len()).collect();
825 built_sharing(cases, &arms, Type::int(32))
826 }
827
828 /// The same, with `arms[i]` saying which arm case `i` goes to, so that several cases can share.
829 fn built_sharing(cases: &[i128], arms: &[usize], ty: Type) -> Built {
830 let mut names = Interner::new();
831 let int = Type::int(32);
832 let mut func =
833 Func::new(names.intern("sw"), Signature::new().with_params(&[ty]).with_returns(&[int]));
834 let entry = func.create_block();
835 let x = func.append_param(entry, ty);
836
837 let default = func.create_block();
838 let count = arms.iter().copied().max().map_or(0, |top| top + 1);
839 let blocks: Vec<Block> = (0..count).map(|_| func.create_block()).collect();
840 let table: Vec<(i128, Block)> =
841 cases.iter().copied().zip(arms.iter().map(|&at| blocks[at])).collect();
842 Builder::new(&mut func, entry).switch(x, default, &table);
843
844 for (index, &arm) in blocks.iter().enumerate() {
845 let mut build = Builder::new(&mut func, arm);
846 let what = i128::try_from(index).expect("a small number of arms");
847 let v = build.iconst(int, (what + 1) * 10);
848 build.ret(&[v]);
849 }
850 let mut build = Builder::new(&mut func, default);
851 let v = build.iconst(int, 0);
852 build.ret(&[v]);
853 Built { names, func, operand: x, arms: blocks, default }
854 }
855
856 fn count(func: &Func) -> usize {
857 func.blocks().count()
858 }
859
860 fn printed(func: &Func, names: &mut Interner) -> String {
861 let module = Module::new(names.intern("sw.c"), &target());
862 rucc_ir::print_func(&module, func, names)
863 }
864
865 fn verified(built: &mut Built) {
866 let module = Module::new(built.names.intern("sw.c"), &target());
867 rucc_ir::verify_func(&module, &built.func, &built.names)
868 .expect("the rewrite builds valid IR");
869 }
870
871 /// Where the operand `x` ends up, worked out by running what the lowering wrote.
872 ///
873 /// This is the test the shape actually needs. Counting compares says the tree is small and says
874 /// nothing about whether it is right, and a decision tree that sends one value down the wrong
875 /// side is a miscompilation that no amount of counting finds. So the blocks the lowering built
876 /// are interpreted for a concrete operand, and the answer is the block it arrives at.
877 ///
878 /// It understands the handful of things this module writes and nothing else, which is how it
879 /// knows it has arrived: an arm ends in a `return`, so the walk stops at the block whose
880 /// instructions it cannot follow.
881 ///
882 /// Every value is held as the number its own type says it is, sign extended, rather than at the
883 /// width of the operand. A bit test computes in a word whatever the operand's width is, so an
884 /// interpreter that assumed one width would get the mask wrong and would agree with itself
885 /// while doing it.
886 fn arrives(func: &Func, operand: Value, x: i128, ty: Type) -> Block {
887 let mut at = func.entry().expect("an entry block");
888 let mut held: HashMap<Value, i128> = HashMap::new();
889 held.insert(operand, Imm::int(x, ty).signed(ty));
890 loop {
891 let mut moved = None;
892 for inst in func.insts(at).collect::<Vec<_>>() {
893 let opcode = func[inst].opcode;
894 let extra = func[inst].extra;
895 let result = func[inst].first_result;
896 let args: Vec<i128> = func[func[inst].args]
897 .iter()
898 .map(|value| held.get(value).copied().unwrap_or(0))
899 .collect();
900 let wide = |value: Option<Value>| func[value.expect("a result")].ty;
901 let mut put = |value: Option<Value>, what: i128| {
902 let value = value.expect("a result");
903 let ty = func[value].ty;
904 held.insert(value, Imm::int(what, ty).signed(ty));
905 };
906 match opcode {
907 Opcode::IConst => {
908 let Extra::Imm(imm) = extra else { return at };
909 put(result, func[imm].signed(wide(result)));
910 }
911 Opcode::Sub => put(result, args[0] - args[1]),
912 Opcode::And => put(result, args[0] & args[1]),
913 Opcode::Shl => put(result, args[0] << args[1]),
914 Opcode::ZExt => {
915 let from = func[func[func[inst].args][0]].ty;
916 let raw = Imm::int(args[0], from).unsigned();
917 put(result, i128::try_from(raw).expect("a value narrower than a word"));
918 }
919 Opcode::ICmp => {
920 let Extra::IntPred(pred) = extra else { return at };
921 let of = func[func[func[inst].args][0]].ty;
922 let unsigned = |v: i128| Imm::int(v, of).unsigned();
923 let answer = match pred {
924 IntPred::Eq => args[0] == args[1],
925 IntPred::Ne => args[0] != args[1],
926 IntPred::Slt => args[0] < args[1],
927 IntPred::Ule => unsigned(args[0]) <= unsigned(args[1]),
928 other => panic!("the lowering does not write {}", other.name()),
929 };
930 held.insert(result.expect("a comparison has a result"), i128::from(answer));
931 }
932 Opcode::Jump => {
933 let call = func.successors(inst).next().expect("a jump has a target");
934 moved = Some(call.block);
935 }
936 Opcode::BrIf => {
937 let mut targets = func.successors(inst);
938 let taken = targets.next().expect("a branch has two targets");
939 let other = targets.next().expect("a branch has two targets");
940 moved = Some(if args[0] != 0 { taken.block } else { other.block });
941 }
942 // The one a table is left as, which is read the way the table will be: the
943 // arm whose case the index is, or the default when no case is.
944 Opcode::Switch => {
945 let Extra::Switch(info) = extra else { return at };
946 let of = func[func[func[inst].args][0]].ty;
947 let targets: Vec<BlockCall> = func.successors(inst).collect();
948 let found = func[func[info].cases]
949 .iter()
950 .position(|case| case.signed(of) == args[0])
951 .map_or(targets[0], |arm| targets[arm + 1]);
952 moved = Some(found.block);
953 }
954 _ => return at,
955 }
956 }
957 match moved {
958 Some(next) => at = next,
959 None => return at,
960 }
961 }
962 }
963
964 /// Every probe arrives where the case list says it should, whatever shape the lowering picked.
965 fn routes(built: &mut Built, cases: &[i128], arms: &[usize], probes: &[i128], ty: Type) {
966 switches(&mut built.func);
967 verified(built);
968 for &x in probes {
969 let wanted = cases
970 .iter()
971 .position(|&case| case == x)
972 .map_or(built.default, |at| built.arms[arms[at]]);
973 let got = arrives(&built.func, built.operand, x, ty);
974 assert_eq!(got, wanted, "the operand {x} went to the wrong block");
975 }
976 }
977
978 /// Every case value, both sides of every one of them, and the ends of the type.
979 fn around(cases: &[i128], ty: Type) -> Vec<i128> {
980 let mut probes: Vec<i128> = Vec::new();
981 for &case in cases {
982 probes.extend([case - 1, case, case + 1]);
983 }
984 let bits = ty.bits();
985 probes.extend([0, -1, 1, i128::from(i32::MIN) >> (32 - bits), (1 << (bits - 1)) - 1]);
986 probes.retain(|&x| Imm::int(x, ty).signed(ty) == x);
987 probes.sort_unstable();
988 probes.dedup();
989 probes
990 }
991
992 #[test]
993 fn a_small_switch_is_a_compare_and_a_branch_for_each_case() {
994 let mut built = built(&[1, 2]);
995 let before = count(&built.func);
996 switches(&mut built.func);
997 assert_eq!(count(&built.func), before + blocks_for(2));
998
999 let text = printed(&built.func, &mut built.names);
1000 assert!(!text.contains("switch"), "the switch is gone: {text}");
1001 assert_eq!(text.matches("icmp eq").count(), 2, "one compare per case: {text}");
1002 assert_eq!(text.matches("br_if").count(), 2, "one branch per case: {text}");
1003 }
1004
1005 #[test]
1006 fn the_last_case_falls_to_the_default_rather_than_to_a_block_of_its_own() {
1007 let mut built = built(&[7]);
1008 let before = count(&built.func);
1009 switches(&mut built.func);
1010 // One case needs no chain block at all: the one compare goes to the arm or to the default.
1011 assert_eq!(count(&built.func), before);
1012 assert_eq!(blocks_for(1), 0);
1013 }
1014
1015 #[test]
1016 fn a_switch_with_only_a_default_is_a_jump() {
1017 let mut built = built(&[]);
1018 switches(&mut built.func);
1019 let entry = built.func.entry().expect("an entry block");
1020 let term = built.func.terminator(entry).expect("a terminator");
1021 assert_eq!(built.func[term].opcode, Opcode::Jump);
1022 }
1023
1024 /// The rewrite has to leave a function the verifier still accepts, since every check it makes
1025 /// is one the rest of the back end assumes and none of them is rechecked after this runs.
1026 #[test]
1027 fn what_comes_out_is_valid_ir() {
1028 let mut built = built(&[1, 2, 3, 4]);
1029 switches(&mut built.func);
1030 verified(&mut built);
1031 }
1032
1033 /// Nothing else is touched, which matters because this runs over every function whether or not
1034 /// one has a `switch` in it.
1035 #[test]
1036 fn a_function_with_no_switch_is_left_exactly_as_it_was() {
1037 let mut names = Interner::new();
1038 let int = Type::int(32);
1039 let mut func =
1040 Func::new(names.intern("f"), Signature::new().with_params(&[int]).with_returns(&[int]));
1041 let entry = func.create_block();
1042 let x = func.append_param(entry, int);
1043 Builder::new(&mut func, entry).ret(&[x]);
1044
1045 let before = printed(&func, &mut names);
1046 switches(&mut func);
1047 assert_eq!(printed(&func, &mut names), before);
1048 }
1049
1050 #[test]
1051 fn a_run_of_cases_going_to_one_place_is_one_range_test() {
1052 let cases = [3, 4, 5, 6, 7, 8, 9, 10];
1053 let arms = [0; 8];
1054 let mut built = built_sharing(&cases, &arms, Type::int(32));
1055 switches(&mut built.func);
1056
1057 let text = printed(&built.func, &mut built.names);
1058 assert_eq!(text.matches("icmp").count(), 1, "eight cases, one test: {text}");
1059 assert_eq!(text.matches("icmp ule").count(), 1, "and the test is the range: {text}");
1060 assert_eq!(text.matches("sub").count(), 1, "one subtraction to bring it to zero: {text}");
1061 }
1062
1063 #[test]
1064 fn a_run_that_starts_at_zero_needs_no_subtraction() {
1065 let cases = [0, 1, 2, 3, 4];
1066 let arms = [0; 5];
1067 let mut built = built_sharing(&cases, &arms, Type::int(32));
1068 switches(&mut built.func);
1069
1070 let text = printed(&built.func, &mut built.names);
1071 assert_eq!(text.matches("icmp ule").count(), 1, "one range test: {text}");
1072 assert!(!text.contains("sub"), "nothing to subtract from zero: {text}");
1073 }
1074
1075 /// The clusters are what the tree is built over, so a `switch` of forty cases that fall into
1076 /// three runs is a `switch` of three tests and not a search.
1077 #[test]
1078 fn the_tree_is_built_over_the_clusters_and_not_over_the_cases() {
1079 let cases: Vec<i128> = (0..30).collect();
1080 let arms: Vec<usize> = (0..30).map(|at: usize| at / 10).collect();
1081 let mut built = built_sharing(&cases, &arms, Type::int(32));
1082 switches(&mut built.func);
1083
1084 let text = printed(&built.func, &mut built.names);
1085 assert_eq!(text.matches("icmp").count(), 3, "three runs, three tests: {text}");
1086 assert!(!text.contains("icmp slt"), "three clusters is under the leaf size: {text}");
1087 }
1088
1089 /// The number the whole thing is for. Forty scattered cases used to be forty comparisons on the
1090 /// way to the last of them, and a binary search is the difference between that and seven.
1091 #[test]
1092 fn a_long_sparse_switch_is_a_search_rather_than_a_walk() {
1093 // Four leaves' worth, so the tree is two splits deep and the bound below is a bound on
1094 // something rather than a restatement of the leaf size. Seventeen apart, which is too
1095 // sparse for a table, so the tree is what gets built.
1096 let count = 4 * LINEAR as i128;
1097 let cases: Vec<i128> = (0..count).map(|at| at * SPARSE).collect();
1098 let mut built = built(&cases);
1099 switches(&mut built.func);
1100
1101 let worst = deepest(&built.func);
1102 assert!(worst <= LINEAR + 2, "{count} cases in {worst} comparisons at worst");
1103 assert!(worst > LINEAR, "and the splits are being counted too");
1104 }
1105
1106 /// How far apart the cases of a test about the tree are, which is further than a table would
1107 /// cover: seventeen values for each comparison against the eight a table is allowed.
1108 const SPARSE: i128 = 17;
1109
1110 /// The most comparisons on any path from the entry to an arm.
1111 ///
1112 /// A depth first walk over the blocks the lowering wrote, which is a directed acyclic graph
1113 /// because every branch it writes goes forward, so no path is walked twice and nothing loops.
1114 fn deepest(func: &Func) -> usize {
1115 fn walk(func: &Func, at: Block, seen: &mut HashMap<Block, usize>) -> usize {
1116 if let Some(&known) = seen.get(&at) {
1117 return known;
1118 }
1119 let here = func.insts(at).filter(|&inst| func[inst].opcode == Opcode::ICmp).count();
1120 let term = func.terminator(at).expect("a terminator");
1121 let onward: Vec<Block> = match func[term].opcode {
1122 Opcode::Jump | Opcode::BrIf => {
1123 func.successors(term).map(|call| call.block).collect()
1124 }
1125 _ => Vec::new(),
1126 };
1127 let below =
1128 onward.into_iter().map(|block| walk(func, block, seen)).max().unwrap_or_default();
1129 seen.insert(at, here + below);
1130 here + below
1131 }
1132 walk(func, func.entry().expect("an entry block"), &mut HashMap::new())
1133 }
1134
1135 #[test]
1136 fn every_value_reaches_the_arm_its_case_named_in_a_small_switch() {
1137 let cases = [1, 2, 3];
1138 let arms = [0, 1, 2];
1139 let ty = Type::int(32);
1140 let mut built = built(&cases);
1141 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1142 }
1143
1144 #[test]
1145 fn every_value_reaches_the_arm_its_case_named_in_a_search() {
1146 let count = 3 * LINEAR;
1147 let cases: Vec<i128> = (0..count as i128).map(|at| at * SPARSE).collect();
1148 let arms: Vec<usize> = (0..count).collect();
1149 let ty = Type::int(32);
1150 let mut built = built(&cases);
1151 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1152 }
1153
1154 /// The one the signed sort and the signed split have to agree about. A `switch` whose cases sit
1155 /// on both sides of zero is where sorting one way and comparing the other goes wrong.
1156 #[test]
1157 fn every_value_reaches_its_arm_when_the_cases_straddle_zero() {
1158 let half = LINEAR as i128;
1159 let cases: Vec<i128> = (-half..half).map(|at| at * SPARSE).collect();
1160 let arms: Vec<usize> = (0..2 * LINEAR).collect();
1161 let ty = Type::int(32);
1162 let mut built = built(&cases);
1163 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1164 }
1165
1166 /// Runs and single values in the same statement, which is the partition the module is named
1167 /// after and the thing a design that picked one shape could not say.
1168 #[test]
1169 fn every_value_reaches_its_arm_when_runs_and_singles_are_mixed() {
1170 let cases: Vec<i128> =
1171 vec![-9, -8, -7, -6, 0, 5, 6, 7, 8, 9, 10, 40, 41, 90, 91, 92, 93, 94, 95, 200];
1172 let arms: Vec<usize> = vec![0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 3, 4, 5, 5, 5, 5, 5, 5, 6];
1173 let ty = Type::int(32);
1174 let mut built = built_sharing(&cases, &arms, ty);
1175 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1176 }
1177
1178 /// A run that covers a whole type, where the width of it is every bit set and the comparison
1179 /// against it is a test that is true of everything. Section 24.6 calls this out because the
1180 /// same arithmetic in the switch's own type overflows here rather than wrapping usefully.
1181 #[test]
1182 fn a_run_covering_the_whole_type_matches_everything() {
1183 let cases: Vec<i128> = (-128..128).collect();
1184 let arms = vec![0; cases.len()];
1185 let ty = Type::int(8);
1186 let mut built = built_sharing(&cases, &arms, ty);
1187 switches(&mut built.func);
1188 verified(&mut built);
1189
1190 let text = printed(&built.func, &mut built.names);
1191 assert_eq!(text.matches("icmp").count(), 1, "one run, one test: {text}");
1192
1193 let entry = built.func.entry().expect("an entry block");
1194 let operand = built.func[entry].params[0];
1195 for x in [-128, -1, 0, 1, 127] {
1196 assert_eq!(
1197 arrives(&built.func, operand, x, ty),
1198 built.arms[0],
1199 "every value of the type is in the run"
1200 );
1201 }
1202 }
1203
1204 /// C forbids one and the front end rejects one, and everything the clusters promise each other
1205 /// rests on that, so a duplicate that got this far stops the compiler rather than being guessed
1206 /// at. Section 24.6 asks for the assumption to be recorded, and this is the record.
1207 #[test]
1208 #[should_panic(expected = "two cases of the same value")]
1209 fn a_case_value_written_twice_stops_the_compiler() {
1210 let cases = [4, 9, 4];
1211 let arms = [0, 1, 2];
1212 let mut built = built_sharing(&cases, &arms, Type::int(32));
1213 switches(&mut built.func);
1214 }
1215
1216 /// Two consecutive cases whose arms are the same block but which pass it different arguments
1217 /// are two destinations, so they are two clusters and not one run. Nothing the front end writes
1218 /// produces this today, which is why the `switch` has to be built by hand, and the check is
1219 /// there because a run that merged them would hand the block one of the two values whichever
1220 /// case arrived.
1221 #[test]
1222 fn cases_that_share_a_block_but_not_its_arguments_are_not_a_run() {
1223 let mut names = Interner::new();
1224 let int = Type::int(32);
1225 let mut func = Func::new(
1226 names.intern("sw"),
1227 Signature::new().with_params(&[int]).with_returns(&[int]),
1228 );
1229 let entry = func.create_block();
1230 let x = func.append_param(entry, int);
1231 let default = func.create_block();
1232 let join = func.create_block();
1233 let param = func.append_param(join, int);
1234
1235 let mut build = Builder::new(&mut func, entry);
1236 let ten = build.iconst(int, 10);
1237 let twenty = build.iconst(int, 20);
1238 let none = func.push_values(&[]);
1239 let first = func.push_values(&[ten]);
1240 let second = func.push_values(&[twenty]);
1241 let targets = func.push_block_calls(&[
1242 BlockCall::new(default, none),
1243 BlockCall::new(join, first),
1244 BlockCall::new(join, second),
1245 ]);
1246 let cases = func.push_imms(&[Imm::int(1, int), Imm::int(2, int)]);
1247 let info = func.add_switch(SwitchInfo { targets, cases });
1248 let args = func.push_values(&[x]);
1249 let data = InstData { args, extra: Extra::Switch(info), ..InstData::new(Opcode::Switch) };
1250 Builder::new(&mut func, entry).inst(data, &[]);
1251
1252 let mut build = Builder::new(&mut func, join);
1253 build.ret(&[param]);
1254 let mut build = Builder::new(&mut func, default);
1255 let zero = build.iconst(int, 0);
1256 build.ret(&[zero]);
1257
1258 switches(&mut func);
1259 let text = printed(&func, &mut names);
1260 assert_eq!(text.matches("icmp eq").count(), 2, "two cases, two equality tests: {text}");
1261 assert!(!text.contains("icmp ule"), "and no range test over them: {text}");
1262 }
1263
1264 /// The leaf size is a number and not an accident, so it is worth one test that says what it is
1265 /// for: at the size itself nothing is built, and one past it the search starts.
1266 #[test]
1267 fn the_leaf_size_is_where_the_search_starts() {
1268 let flat: Vec<i128> = (0..LINEAR as i128).map(|at| at * SPARSE).collect();
1269 let mut walked = built(&flat);
1270 switches(&mut walked.func);
1271 assert!(
1272 !printed(&walked.func, &mut walked.names).contains("icmp slt"),
1273 "a leaf's worth of clusters is still a chain"
1274 );
1275
1276 let one_more: Vec<i128> = (0..LINEAR as i128 + 1).map(|at| at * SPARSE).collect();
1277 let mut split = built(&one_more);
1278 switches(&mut split.func);
1279 assert!(
1280 printed(&split.func, &mut split.names).contains("icmp slt"),
1281 "one more than a leaf splits"
1282 );
1283 }
1284
1285 /// The shape the bit test exists for. `case 'a': case 'e': case 'i': case 'o': case 'u':` is
1286 /// five values scattered through twenty one, all going to one place, and every one of them used
1287 /// to be a comparison of its own.
1288 #[test]
1289 fn scattered_cases_sharing_one_arm_are_one_mask_and_one_test() {
1290 let cases = [97, 101, 105, 111, 117];
1291 let arms = [0; 5];
1292 let mut built = built_sharing(&cases, &arms, Type::int(32));
1293 switches(&mut built.func);
1294
1295 let text = printed(&built.func, &mut built.names);
1296 assert!(!text.contains("icmp eq"), "no case is compared on its own: {text}");
1297 assert_eq!(text.matches("shl").count(), 1, "one bit is picked out: {text}");
1298 assert_eq!(text.matches("and").count(), 1, "and one mask is asked about it: {text}");
1299 assert_eq!(text.matches("icmp ule").count(), 1, "one bound before the shift: {text}");
1300 assert_eq!(text.matches("icmp ne").count(), 1, "one test for the one arm: {text}");
1301 }
1302
1303 /// What the counting above does not say. A mask with a bit in the wrong place still has one
1304 /// shift and one test in it, so the test that matters is where each value ends up.
1305 #[test]
1306 fn every_value_reaches_its_arm_through_a_bit_test() {
1307 let ty = Type::int(32);
1308 let cases = [97, 101, 105, 111, 117];
1309 let arms = [0; 5];
1310 let mut built = built_sharing(&cases, &arms, ty);
1311 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1312 }
1313
1314 /// One group can hold several destinations, each as the bits of a mask of its own, and they are
1315 /// asked about in turn. The shift is what is shared, and the shift is the expensive part.
1316 #[test]
1317 fn a_bit_test_carries_several_destinations_in_one_word() {
1318 let ty = Type::int(32);
1319 let cases: Vec<i128> = (0..9).map(|at| at * 3).collect();
1320 let arms: Vec<usize> = (0..9).map(|at: usize| at % 3).collect();
1321 let mut built = built_sharing(&cases, &arms, ty);
1322 switches(&mut built.func);
1323
1324 let text = printed(&built.func, &mut built.names);
1325 assert_eq!(text.matches("shl").count(), 1, "nine cases, one shift: {text}");
1326 assert_eq!(text.matches("icmp ne").count(), 3, "three arms, three masks: {text}");
1327 assert!(!text.contains("icmp eq"), "and no case compared on its own: {text}");
1328
1329 let mut built = built_sharing(&cases, &arms, ty);
1330 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1331 }
1332
1333 /// When the masks between them account for every value in the span, the last destination is
1334 /// where anything in range that matched nothing else has to go, so it needs no test of its own.
1335 #[test]
1336 fn the_last_destination_needs_no_test_when_the_masks_cover_the_span() {
1337 let ty = Type::int(32);
1338 let cases: Vec<i128> = (0..6).collect();
1339 let arms: Vec<usize> = (0..6).map(|at: usize| at % 2).collect();
1340 let mut built = built_sharing(&cases, &arms, ty);
1341 switches(&mut built.func);
1342
1343 let text = printed(&built.func, &mut built.names);
1344 assert_eq!(text.matches("icmp ne").count(), 1, "two arms, one mask asked about: {text}");
1345
1346 let mut built = built_sharing(&cases, &arms, ty);
1347 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1348 }
1349
1350 /// A bit test costs a bound, a shift and a test before the first mask is looked at, so a group
1351 /// that has barely more values in it than destinations is worse than the walk it replaces.
1352 #[test]
1353 fn a_group_that_does_not_pay_for_itself_stays_a_chain() {
1354 let cases = [0, 3, 6];
1355 let arms = [0, 1, 2];
1356 let mut built = built_sharing(&cases, &arms, Type::int(32));
1357 switches(&mut built.func);
1358
1359 let text = printed(&built.func, &mut built.names);
1360 assert!(!text.contains("shl"), "three values and three arms buys nothing: {text}");
1361 assert_eq!(text.matches("icmp eq").count(), 3, "so it stays a walk: {text}");
1362 }
1363
1364 /// The word is a correctness bound and not a tuning one. A mask holds sixty four bits, so a
1365 /// group stops at the last value within sixty four of its first, and what is left of the
1366 /// `switch` carries on without it.
1367 #[test]
1368 fn a_bit_test_never_spans_more_than_a_word() {
1369 let ty = Type::int(32);
1370 let cases = [0, 2, 4, 6, 64];
1371 let arms = [0; 5];
1372 let mut built = built_sharing(&cases, &arms, ty);
1373 switches(&mut built.func);
1374
1375 let text = printed(&built.func, &mut built.names);
1376 assert_eq!(text.matches("shl").count(), 1, "one group, not two: {text}");
1377 assert_eq!(text.matches("icmp eq").count(), 1, "and the value past it is compared: {text}");
1378
1379 let mut built = built_sharing(&cases, &arms, ty);
1380 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1381 }
1382
1383 /// The value sixty three above the first sets the top bit of the mask, which is the shift the
1384 /// span bound is there to keep legal and the one an interpreter that computed in the operand's
1385 /// width would get wrong.
1386 #[test]
1387 fn a_bit_test_reaches_the_top_of_its_word() {
1388 let ty = Type::int(32);
1389 let cases = [0, 2, 4, 63];
1390 let arms = [0; 4];
1391 let mut built = built_sharing(&cases, &arms, ty);
1392 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1393 }
1394
1395 /// A run is already one subtraction and one comparison however many values it holds, so folding
1396 /// it into a mask would replace two instructions with two instructions and spend a word of span
1397 /// doing it. Only single values are grouped.
1398 #[test]
1399 fn a_run_is_left_alone_rather_than_folded_into_a_mask() {
1400 let ty = Type::int(32);
1401 let cases = [0, 1, 2, 3, 10, 12, 14, 16];
1402 let arms = [0, 0, 0, 0, 1, 1, 1, 1];
1403 let mut built = built_sharing(&cases, &arms, ty);
1404 switches(&mut built.func);
1405
1406 let text = printed(&built.func, &mut built.names);
1407 assert_eq!(text.matches("icmp ule").count(), 2, "a run's bound and a group's: {text}");
1408 assert_eq!(text.matches("shl").count(), 1, "and only the group is a mask: {text}");
1409
1410 let mut built = built_sharing(&cases, &arms, ty);
1411 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1412 }
1413
1414 /// Negative cases are the ones a shift gets wrong if the span is measured with the wrong sign,
1415 /// so a group that starts below zero is worth its own routing check.
1416 #[test]
1417 fn every_value_reaches_its_arm_when_a_bit_test_starts_below_zero() {
1418 let ty = Type::int(32);
1419 let cases = [-20, -17, -14, -11, -8, -5];
1420 let arms = [0, 1, 0, 1, 0, 1];
1421 let mut built = built_sharing(&cases, &arms, ty);
1422 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1423 }
1424
1425 /// Cases packed closely enough, with enough places to go, are one bound and one lookup, which
1426 /// is what gcc writes for the same switch. Nothing is compared case by case.
1427 #[test]
1428 fn a_dense_switch_is_one_bound_and_a_table() {
1429 let cases: Vec<i128> = (0..13).collect();
1430 let mut built = built(&cases);
1431 switches(&mut built.func);
1432 verified(&mut built);
1433
1434 let text = printed(&built.func, &mut built.names);
1435 assert_eq!(text.matches("icmp ule").count(), 1, "one bound over the span: {text}");
1436 assert_eq!(text.matches("switch").count(), 1, "and one table inside it: {text}");
1437 assert!(!text.contains("icmp eq"), "and no case compared on its own: {text}");
1438 }
1439
1440 /// A table with holes in it sends the holes to the default, and the index is the case less the
1441 /// low end, so a table that starts away from zero is the one that shows an off-by-one.
1442 #[test]
1443 fn every_value_reaches_its_arm_through_a_table_with_holes() {
1444 let ty = Type::int(32);
1445 let cases = [3, 4, 5, 7, 8, 10, 11, 13, 14, 15, 19];
1446 let arms: Vec<usize> = (0..cases.len()).collect();
1447 let mut built = built_sharing(&cases, &arms, ty);
1448 routes(&mut built, &cases, &arms, &around(&cases, ty), ty);
1449 let text = printed(&built.func, &mut built.names);
1450 assert_eq!(text.matches("switch").count(), 1, "the cases are one table: {text}");
1451 }
1452
1453 /// A `signed char` switch that runs from below zero to above it. The index has to be taken
1454 /// after the subtraction and widened without its sign, or the negative cases read the wrong
1455 /// cell.
1456 #[test]
1457 fn every_value_reaches_its_arm_through_a_table_that_straddles_zero() {
1458 let ty = Type::int(8);
1459 let cases: Vec<i128> = (-6..7).filter(|x| x % 4 != 0).collect();
1460 let arms: Vec<usize> = (0..cases.len()).map(|at| at % 5).collect();
1461 let mut built = built_sharing(&cases, &arms, ty);
1462 let probes: Vec<i128> = (-128..128).collect();
1463 routes(&mut built, &cases, &arms, &probes, ty);
1464 let text = printed(&built.func, &mut built.names);
1465 assert_eq!(text.matches("switch").count(), 1, "the cases are one table: {text}");
1466 }
1467
1468 /// Three places to go are three masks, and gcc keeps that shape too, so a bit test is not
1469 /// traded for a table until there are more destinations than it handles well.
1470 #[test]
1471 fn a_few_destinations_stay_a_bit_test_and_more_become_a_table() {
1472 let ty = Type::int(32);
1473 let cases: Vec<i128> = (0..10).map(|at| at * 3).collect();
1474 let few: Vec<usize> = (0..10).map(|at: usize| at % 3).collect();
1475 let mut built = built_sharing(&cases, &few, ty);
1476 switches(&mut built.func);
1477 let text = printed(&built.func, &mut built.names);
1478 assert!(!text.contains("switch"), "three arms are masks: {text}");
1479
1480 let many: Vec<usize> = (0..10).map(|at: usize| at % 5).collect();
1481 let mut built = built_sharing(&cases, &many, ty);
1482 switches(&mut built.func);
1483 let text = printed(&built.func, &mut built.names);
1484 assert_eq!(text.matches("switch").count(), 1, "five arms are a table: {text}");
1485 let mut built = built_sharing(&cases, &many, ty);
1486 routes(&mut built, &cases, &many, &around(&cases, ty), ty);
1487 }
1488
1489 /// Below the smallest table the cases are compared, since a load and an indirect jump cost
1490 /// more than a few compares that predict well.
1491 #[test]
1492 fn too_few_cases_for_a_table_are_compared() {
1493 let cases: Vec<i128> = (0..7).collect();
1494 let mut built = built(&cases);
1495 switches(&mut built.func);
1496 let text = printed(&built.func, &mut built.names);
1497 assert!(!text.contains("switch"), "seven cases are not a table: {text}");
1498 }
1499
1500 /// An operand wider than a word has no index the machine can load with, so a dense switch over
1501 /// one is searched the way it was before tables.
1502 #[test]
1503 fn an_operand_wider_than_a_word_gets_no_table() {
1504 let ty = Type::int(128);
1505 let cases: Vec<i128> = (0..13).collect();
1506 let arms: Vec<usize> = (0..cases.len()).collect();
1507 let mut built = built_sharing(&cases, &arms, ty);
1508 let probes: Vec<i128> = (-2..16).collect();
1509 routes(&mut built, &cases, &arms, &probes, ty);
1510 let text = printed(&built.func, &mut built.names);
1511 assert!(!text.contains("switch"), "a wide operand is searched: {text}");
1512 }
1513
1514 /// Arms that hand the block they go to a value of their own cannot share a cell with an arm
1515 /// that hands it another. Each one is reached through a block of its own that makes the call,
1516 /// and the table points at those.
1517 #[test]
1518 fn arms_that_carry_values_are_reached_through_blocks_of_their_own() {
1519 let mut names = Interner::new();
1520 let int = Type::int(32);
1521 let mut func = Func::new(
1522 names.intern("sw"),
1523 Signature::new().with_params(&[int]).with_returns(&[int]),
1524 );
1525 let entry = func.create_block();
1526 let x = func.append_param(entry, int);
1527 let default = func.create_block();
1528 let join = func.create_block();
1529 let param = func.append_param(join, int);
1530
1531 let mut build = Builder::new(&mut func, entry);
1532 let values: Vec<Value> = (0..10).map(|at| build.iconst(int, 100 + at)).collect();
1533 let none = func.push_values(&[]);
1534 let mut calls = vec![BlockCall::new(default, none)];
1535 for &value in &values {
1536 let args = func.push_values(&[value]);
1537 calls.push(BlockCall::new(join, args));
1538 }
1539 let targets = func.push_block_calls(&calls);
1540 let imms: Vec<Imm> = (0..10).map(|at| Imm::int(at, int)).collect();
1541 let cases = func.push_imms(&imms);
1542 let info = func.add_switch(SwitchInfo { targets, cases });
1543 let args = func.push_values(&[x]);
1544 let data = InstData { args, extra: Extra::Switch(info), ..InstData::new(Opcode::Switch) };
1545 Builder::new(&mut func, entry).inst(data, &[]);
1546
1547 let mut build = Builder::new(&mut func, join);
1548 build.ret(&[param]);
1549 let mut build = Builder::new(&mut func, default);
1550 let zero = build.iconst(int, 0);
1551 build.ret(&[zero]);
1552
1553 switches(&mut func);
1554 let module = Module::new(names.intern("sw.c"), &target());
1555 rucc_ir::verify_func(&module, &func, &names).expect("the rewrite builds valid IR");
1556 let text = printed(&func, &mut names);
1557 assert_eq!(text.matches("switch").count(), 1, "the cases are one table: {text}");
1558 let table = func
1559 .blocks()
1560 .flat_map(|block| func.insts(block).collect::<Vec<_>>())
1561 .find(|&inst| func[inst].opcode == Opcode::Switch)
1562 .expect("a table");
1563 for call in func.successors(table).skip(1) {
1564 assert!(func[call.args].is_empty(), "a cell passes nothing itself: {text}");
1565 assert_ne!(call.block, join, "a cell goes to a block of its own: {text}");
1566 }
1567 for at in 0..10 {
1568 assert_eq!(arrives(&func, x, at, int), join, "case {at} reaches the join");
1569 }
1570 assert_eq!(arrives(&func, x, 10, int), default, "and a value past the end does not");
1571 }
1572}