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