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