Skip to main content

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