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