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