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