1use std::cmp::Ordering;
124use std::collections::HashSet;
125
126use rucc_base::Symbol;
127use rucc_ir::{
128 Block, BlockCall, Builder, Def, Extra, Flags, Func, Imm, Inst, InstData, MemInfo, MemOrder,
129 Opcode, Restrict, Type, Value,
130};
131
132use rucc_cost::Goal;
133use rucc_cost::heuristics::SWITCH_CONVERSION_MAX_GROWTH;
134
135use crate::cfg::Cfg;
136use crate::{Analyses, Fuel, Pass, Preserved, ReadOnly, Stats};
137
138const CONVERTED: &str = "switch replaced by a range check and the arithmetic its arms were doing";
140
141const TABLED: &str = "switch replaced by a range check and a load from a table of its answers";
143
144const PLACED: &str =
146 "switch replaced by a range check and a load from a table of how far away its answers are";
147
148const NO_FUEL: &str = "switch left alone, the pass ran out of fuel";
150
151const TOO_FEW: &str = "switch left alone, it has too few labels for arithmetic to be cheaper";
153
154const NOT_CONSECUTIVE: &str = "switch left alone, its labels are not consecutive";
156
157const ARM_IS_SHARED: &str = "switch left alone, an arm is reached from somewhere other than it";
159
160const ARM_DOES_WORK: &str = "switch left alone, an arm does more than work out a constant";
162
163const ARMS_DIFFER: &str = "switch left alone, its arms do not all hand on the same thing";
165
166const NOT_AFFINE: &str = "switch left alone, its answers are not a fixed multiple of the label \
168 plus a constant";
169
170const WIDTHS_DIFFER: &str = "switch left alone, its answers are not as wide as its labels";
172
173const TOO_SPARSE: &str = "switch left alone, a table of its answers would be mostly holes";
175
176const LABEL_TOO_WIDE: &str = "switch left alone, its label is wider than a word";
178
179const CELL_IS_ODD: &str =
181 "switch left alone, its answers are not a whole number of bytes of integer";
182
183const PLACE_IS_ODD: &str =
185 "switch left alone, its answers are not all addresses of read only data this file defines";
186
187const LABELS: usize = 3;
189
190const GROWTH: i128 = SWITCH_CONVERSION_MAX_GROWTH as i128;
192
193#[derive(Debug)]
195pub struct SwitchConv;
196
197impl Pass for SwitchConv {
198 fn name(&self) -> &'static str {
199 "switch-conv"
200 }
201
202 fn describe(&self) -> &'static str {
203 "a switch whose arms give constants becomes a range check and arithmetic or a table load"
204 }
205
206 fn preserves(&self) -> Preserved {
207 Preserved::NONE
209 }
210
211 fn run(&self, func: &mut Func, an: &mut Analyses, fuel: &mut Fuel) -> Stats {
212 convert(func, an, fuel, None)
213 }
214
215 fn run_emitting(
216 &self,
217 func: &mut Func,
218 an: &mut Analyses,
219 fuel: &mut Fuel,
220 data: &mut ReadOnly<'_>,
221 ) -> Stats {
222 convert(func, an, fuel, Some(data))
223 }
224}
225
226fn convert(
230 func: &mut Func,
231 an: &mut Analyses,
232 fuel: &mut Fuel,
233 mut data: Option<&mut ReadOnly<'_>>,
234) -> Stats {
235 let mut stats = Stats::new();
236 if func.entry().is_none() {
237 return stats;
238 }
239 let cfg = an.cfg(func);
240 let found: Vec<Inst> = func
241 .blocks()
242 .filter_map(|block| func.terminator(block))
243 .filter(|&inst| func[inst].opcode == Opcode::Switch)
244 .collect();
245
246 let index_bits = data.as_ref().map(|data| data.pointer_bits());
247 let near = near(func, an, data.as_deref());
248 let small = an.machine().goal() == Goal::Size;
249 let mut plans = Vec::new();
250 for inst in found {
251 match plan(func, cfg, inst, index_bits, small, &near) {
252 Ok(plan) => plans.push(plan),
253 Err(why) => stats.missed(why),
254 }
255 }
256
257 let mut changed = false;
258 for plan in plans {
259 if !fuel.take() {
260 stats.missed(NO_FUEL);
261 continue;
262 }
263 let table = match (&plan.how, data.as_deref_mut()) {
264 (How::Table { cell, cells, .. }, Some(data)) => {
265 Some(data.table(cell.ty, cells.clone()))
266 }
267 (How::Distances { to, .. }, Some(data)) => Some(data.distances(to)),
268 _ => None,
269 };
270 stats.optimized(match (&plan.how, table) {
271 (_, None) => CONVERTED,
272 (How::Distances { .. }, Some(_)) => PLACED,
273 (_, Some(_)) => TABLED,
274 });
275 apply(func, &plan, table);
276 changed = true;
277 }
278 if changed {
279 an.clear();
280 }
281 stats
282}
283
284fn near(func: &Func, an: &Analyses, data: Option<&ReadOnly<'_>>) -> HashSet<Symbol> {
290 if !data.is_some_and(ReadOnly::measures) {
291 return HashSet::new();
292 }
293 let images = an.images();
294 func.blocks()
295 .flat_map(|block| func.insts(block))
296 .filter_map(|inst| match func[inst] {
297 InstData { opcode: Opcode::GlobalAddr, extra: Extra::Symbol(name), .. } => Some(name),
298 _ => None,
299 })
300 .filter(|&name| images.holds(name))
301 .collect()
302}
303
304#[derive(Clone, Copy, Debug, PartialEq, Eq)]
306enum Hands {
307 On(Block),
309 Back,
311}
312
313#[derive(Debug)]
315struct Plan {
316 inst: Inst,
318 value: Value,
320 ty: Type,
322 hands: Hands,
324 args: Vec<Value>,
327 answer: usize,
329 how: How,
331 arms: Vec<Block>,
333 holes: Vec<i128>,
336}
337
338#[derive(Debug)]
340enum How {
341 Line {
343 scale: i128,
345 offset: i128,
347 },
348 Table {
350 low: i128,
352 ty: Type,
354 cell: Cell,
356 cells: Vec<i128>,
358 index_bits: u32,
360 },
361 Distances {
363 low: i128,
365 to: Vec<Option<(Symbol, i128)>>,
368 index_bits: u32,
370 },
371}
372
373#[derive(Clone, Copy, Debug, PartialEq, Eq)]
375struct Cell {
376 ty: Type,
378 signed: bool,
380}
381
382fn plan(
388 func: &Func,
389 cfg: &Cfg,
390 inst: Inst,
391 index_bits: Option<u32>,
392 small: bool,
393 near: &HashSet<Symbol>,
394) -> Result<Plan, &'static str> {
395 let Extra::Switch(info) = func[inst].extra else { return Err(ARMS_DIFFER) };
396 let info = func[info];
397 let Some(&value) = func[func[inst].args].first() else { return Err(ARMS_DIFFER) };
398 let ty = func[value].ty;
399 if !ty.is_int() {
400 return Err(WIDTHS_DIFFER);
401 }
402 let calls: Vec<BlockCall> = func[info.targets].to_vec();
403 let labels: Vec<i128> = func[info.cases].iter().map(|imm| imm.signed(ty)).collect();
404 let Some((&default, arms)) = calls.split_first() else { return Err(ARMS_DIFFER) };
405 if arms.len() != labels.len() || arms.len() < LABELS {
406 return Err(TOO_FEW);
407 }
408 if arms.iter().any(|call| call.block == default.block) {
412 return Err(ARM_IS_SHARED);
413 }
414
415 let consecutive = labels.windows(2).all(|pair| pair[1].checked_sub(pair[0]) == Some(1));
421 if !consecutive && index_bits.is_none() {
422 return Err(NOT_CONSECUTIVE);
423 }
424
425 let mut hands = None;
428 let mut shared: Option<Vec<Value>> = None;
429 let mut answer = None;
430 let mut handed = Vec::new();
431 for call in arms {
432 if !call.args.is_empty() {
433 return Err(ARM_DOES_WORK);
434 }
435 if cfg.predecessors(call.block).len() != 1 {
436 return Err(ARM_IS_SHARED);
437 }
438 if func.block_name(call.block).is_some() {
441 return Err(ARM_IS_SHARED);
442 }
443 let (way, args) = tail(func, call.block)?;
444 if *hands.get_or_insert(way) != way {
445 return Err(ARMS_DIFFER);
446 }
447 let previous = shared.get_or_insert_with(|| args.clone());
448 if previous.len() != args.len() {
449 return Err(ARMS_DIFFER);
450 }
451 for (index, (&mine, &theirs)) in previous.iter().zip(&args).enumerate() {
454 if mine == theirs {
455 continue;
456 }
457 if *answer.get_or_insert(index) != index {
458 return Err(ARMS_DIFFER);
459 }
460 }
461 handed.push(args);
462 }
463 let (Some(hands), Some(args)) = (hands, shared) else { return Err(ARMS_DIFFER) };
464 let answer = answer.ok_or(NOT_AFFINE)?;
465 let kind = func[args[answer]].ty;
466 let (how, holes) = if kind.is_ptr() {
472 let index_bits = index_bits.ok_or(PLACE_IS_ODD)?;
473 let mut places = Vec::with_capacity(handed.len());
474 for args in &handed {
475 places.push(place(func, args[answer], near).ok_or(PLACE_IS_ODD)?);
476 }
477 let fill = fallback(func, default, hands, &args, answer)
478 .and_then(|given| place(func, given, near));
479 distances(&labels, &places, ty, index_bits, fill)?
480 } else {
481 let mut answers = Vec::with_capacity(handed.len());
482 for args in &handed {
483 let Some(number) = constant(func, args[answer]) else { return Err(NOT_AFFINE) };
484 answers.push(number);
485 }
486 let line = if consecutive && kind == ty { line(&labels, &answers, ty) } else { None };
487 match (line, index_bits) {
488 (Some((scale, offset)), _) => (How::Line { scale, offset }, Vec::new()),
489 (None, Some(index_bits)) => {
490 let fill = fallback(func, default, hands, &args, answer)
491 .and_then(|given| constant(func, given));
492 let shape = Shape { ty, kind, index_bits, small };
493 table(&labels, &answers, shape, fill)?
494 }
495 (None, None) if kind != ty => return Err(WIDTHS_DIFFER),
496 (None, None) => return Err(NOT_AFFINE),
497 }
498 };
499 Ok(Plan {
500 inst,
501 value,
502 ty,
503 hands,
504 args,
505 answer,
506 how,
507 arms: arms.iter().map(|call| call.block).collect(),
508 holes,
509 })
510}
511
512fn fallback(
524 func: &Func,
525 default: BlockCall,
526 hands: Hands,
527 args: &[Value],
528 answer: usize,
529) -> Option<Value> {
530 let theirs = if default.args.is_empty() {
531 let (way, theirs) = tail(func, default.block).ok()?;
532 if way != hands {
533 return None;
534 }
535 theirs
536 } else if hands == Hands::On(default.block) {
537 func[default.args].to_vec()
538 } else {
539 return None;
540 };
541 if theirs.len() != args.len() {
542 return None;
543 }
544 let agrees =
545 args.iter().zip(&theirs).enumerate().all(|(at, (mine, it))| at == answer || mine == it);
546 if !agrees {
547 return None;
548 }
549 Some(theirs[answer])
550}
551
552#[derive(Clone, Copy, Debug)]
555struct Shape {
556 ty: Type,
558 kind: Type,
560 index_bits: u32,
562 small: bool,
564}
565
566fn table(
574 labels: &[i128],
575 answers: &[i128],
576 shape: Shape,
577 fill: Option<i128>,
578) -> Result<(How, Vec<i128>), &'static str> {
579 let Shape { ty, kind, index_bits, small } = shape;
580 if ty.bits() > 64 {
581 return Err(LABEL_TOO_WIDE);
582 }
583 if !kind.is_int() || !matches!(kind.bits(), 8 | 16 | 32 | 64) {
584 return Err(CELL_IS_ODD);
585 }
586 let (low, cells) = spread(labels, answers)?;
587 let holes = if fill.is_some() { holes(low, &cells) } else { Vec::new() };
588 let cells: Vec<i128> = cells.into_iter().map(|cell| cell.or(fill).unwrap_or(0)).collect();
589 let cell = if small { narrowest(&cells, kind) } else { Cell { ty: kind, signed: false } };
590 Ok((How::Table { low, ty: kind, cell, cells, index_bits }, holes))
591}
592
593fn distances(
600 labels: &[i128],
601 places: &[(Symbol, i128)],
602 ty: Type,
603 index_bits: u32,
604 fill: Option<(Symbol, i128)>,
605) -> Result<(How, Vec<i128>), &'static str> {
606 if ty.bits() > 64 {
607 return Err(LABEL_TOO_WIDE);
608 }
609 let (low, to) = spread(labels, places)?;
610 let holes = if fill.is_some() { holes(low, &to) } else { Vec::new() };
611 let to = to.into_iter().map(|cell| cell.or(fill)).collect();
612 Ok((How::Distances { low, to, index_bits }, holes))
613}
614
615fn spread<T: Copy>(labels: &[i128], answers: &[T]) -> Result<(i128, Vec<Option<T>>), &'static str> {
621 let (Some(&low), Some(&high)) = (labels.iter().min(), labels.iter().max()) else {
622 return Err(TOO_FEW);
623 };
624 let span = high - low + 1;
625 if span > GROWTH * labels.len() as i128 {
626 return Err(TOO_SPARSE);
627 }
628 let mut cells = vec![None; usize::try_from(span).map_err(|_| TOO_SPARSE)?];
629 for (&label, &answer) in labels.iter().zip(answers) {
630 let at = usize::try_from(label - low).map_err(|_| TOO_SPARSE)?;
631 cells[at] = Some(answer);
632 }
633 Ok((low, cells))
634}
635
636fn holes<T>(low: i128, cells: &[Option<T>]) -> Vec<i128> {
638 (low..).zip(cells).filter(|(_, cell)| cell.is_none()).map(|(label, _)| label).collect()
639}
640
641fn narrowest(answers: &[i128], kind: Type) -> Cell {
647 let whole = 1i128 << kind.bits();
648 for bits in [8u32, 16, 32] {
649 if bits >= kind.bits() {
650 break;
651 }
652 let half = 1i128 << (bits - 1);
653 if answers.iter().all(|&answer| (-half..half).contains(&answer)) {
654 return Cell { ty: Type::int(bits), signed: true };
655 }
656 if answers.iter().all(|&answer| answer.rem_euclid(whole) < half * 2) {
657 return Cell { ty: Type::int(bits), signed: false };
658 }
659 }
660 Cell { ty: kind, signed: false }
661}
662
663fn tail(func: &Func, block: Block) -> Result<(Hands, Vec<Value>), &'static str> {
671 let Some(last) = func.terminator(block) else { return Err(ARM_DOES_WORK) };
672 for inst in func.insts(block) {
673 let opcode = func[inst].opcode;
674 if inst != last && !matches!(opcode, Opcode::IConst | Opcode::GlobalAddr | Opcode::PtrAdd) {
675 return Err(ARM_DOES_WORK);
676 }
677 }
678 let args: Vec<Value> = match func[last].opcode {
679 Opcode::Jump => {
680 let Some(call) = func.successors(last).next() else { return Err(ARM_DOES_WORK) };
681 let args = func[call.args].to_vec();
682 return Ok((Hands::On(call.block), args));
683 }
684 Opcode::Return => func[func[last].args].to_vec(),
685 _ => return Err(ARM_DOES_WORK),
686 };
687 Ok((Hands::Back, args))
688}
689
690fn arithmetic(builder: &mut Builder<'_>, plan: &Plan, scale: i128, offset: i128) -> Value {
692 let scaled = match scale {
693 0 => builder.iconst(plan.ty, offset),
694 1 => plan.value,
695 scale => {
696 let by = builder.iconst(plan.ty, scale);
697 builder.binary(Opcode::Mul, plan.value, by, Flags::NONE)
698 }
699 };
700 if offset == 0 || scale == 0 {
701 scaled
702 } else {
703 let by = builder.iconst(plan.ty, offset);
704 builder.binary(Opcode::Add, scaled, by, Flags::NONE)
705 }
706}
707
708fn look_up(
714 builder: &mut Builder<'_>,
715 plan: &Plan,
716 name: Symbol,
717 low: i128,
718 ty: Type,
719 index_bits: u32,
720) -> (Value, Value) {
721 let from = if low == 0 {
722 plan.value
723 } else {
724 let by = builder.iconst(plan.ty, low);
725 builder.binary(Opcode::Sub, plan.value, by, Flags::NONE)
726 };
727 let word = Type::int(index_bits);
728 let index = match plan.ty.bits().cmp(&index_bits) {
729 Ordering::Less => builder.unary(Opcode::ZExt, from, word),
730 Ordering::Greater => builder.unary(Opcode::Trunc, from, word),
731 Ordering::Equal => from,
732 };
733 let bytes = ty.bits() / 8;
734 let distance = if bytes == 1 {
735 index
736 } else {
737 let by = builder.iconst(word, i128::from(bytes));
738 builder.binary(Opcode::Mul, index, by, Flags::NONE)
739 };
740 let base = builder.value(
741 InstData { extra: Extra::Symbol(name), ..InstData::new(Opcode::GlobalAddr) },
742 Type::PTR,
743 );
744 let cell = builder.binary(Opcode::PtrAdd, base, distance, Flags::NONE);
745 let info = MemInfo {
746 size: u64::from(bytes),
747 align: bytes,
748 order: MemOrder::NotAtomic,
749 tbaa: None,
750 owns: 0,
751 restrict: Restrict::NONE,
752 };
753 (base, builder.load(ty, cell, info, Flags::NONE))
754}
755
756fn far(builder: &mut Builder<'_>, plan: &Plan, name: Symbol, low: i128, index_bits: u32) -> Value {
762 let (base, read) = look_up(builder, plan, name, low, Type::int(32), index_bits);
763 let word = Type::int(index_bits);
764 let wider = index_bits > 32; let away = if wider { builder.unary(Opcode::SExt, read, word) } else { read };
766 let start = builder.unary(Opcode::PtrToInt, base, word);
767 let at = builder.binary(Opcode::Add, start, away, Flags::NONE);
768 builder.unary(Opcode::IntToPtr, at, Type::PTR)
769}
770
771fn place(func: &Func, value: Value, near: &HashSet<Symbol>) -> Option<(Symbol, i128)> {
777 let Def::Result { inst, .. } = func[value].def else { return None };
778 let data = func[inst];
779 match (data.opcode, data.extra) {
780 (Opcode::GlobalAddr, Extra::Symbol(name)) if near.contains(&name) => Some((name, 0)),
781 (Opcode::PtrAdd, _) => {
782 let args = &func[data.args];
783 let (name, bytes) = place(func, args[0], near)?;
784 let bytes = bytes.checked_add(constant(func, args[1])?)?;
785 i32::try_from(bytes).is_ok().then_some((name, bytes))
786 }
787 _ => None,
788 }
789}
790
791fn constant(func: &Func, value: Value) -> Option<i128> {
793 crate::discharge::constant(func, value)
794}
795
796fn line(labels: &[i128], answers: &[i128], ty: Type) -> Option<(i128, i128)> {
803 let [first, second, ..] = *labels else { return None };
804 let [low, high, ..] = *answers else { return None };
805 debug_assert_eq!(second - first, 1, "the labels were checked to be consecutive");
806 let scale = high.checked_sub(low)?;
807 let offset = low.checked_sub(scale.checked_mul(first)?)?;
808 for (&label, &answer) in labels.iter().zip(answers) {
809 let want = scale.checked_mul(label)?.checked_add(offset)?;
810 if wrap(want, ty) != answer {
811 return None;
812 }
813 }
814 Some((scale, offset))
815}
816
817fn wrap(value: i128, ty: Type) -> i128 {
822 Imm::int(value, ty).signed(ty)
823}
824
825fn apply(func: &mut Func, plan: &Plan, table: Option<Symbol>) {
829 let span = func.span(plan.inst);
830 let hit = func.create_block();
831 let mut builder = Builder::new(func, hit).at(span);
832 let answer = match (&plan.how, table) {
833 (&How::Line { scale, offset }, _) => arithmetic(&mut builder, plan, scale, offset),
834 (&How::Table { low, ty, cell, index_bits, .. }, Some(name)) => {
835 let (_, read) = look_up(&mut builder, plan, name, low, cell.ty, index_bits);
836 match (cell.ty == ty, cell.signed) {
837 (true, _) => read,
838 (false, true) => builder.unary(Opcode::SExt, read, ty),
839 (false, false) => builder.unary(Opcode::ZExt, read, ty),
840 }
841 }
842 (&How::Distances { low, index_bits, .. }, Some(name)) => {
843 far(&mut builder, plan, name, low, index_bits)
844 }
845 (How::Table { .. } | How::Distances { .. }, None) => {
846 unreachable!("a table was planned with nowhere to put it")
847 }
848 };
849 let mut args = plan.args.clone();
850 args[plan.answer] = answer;
851 match plan.hands {
852 Hands::On(block) => builder.jump(block, &args),
853 Hands::Back => builder.ret(&args),
854 };
855
856 let Extra::Switch(info) = func[plan.inst].extra else { return };
859 let empty = func.push_values(&[]);
860 let mut calls: Vec<BlockCall> = func[func[info].targets].to_vec();
861 for call in &mut calls[1..] {
862 *call = BlockCall::new(hit, empty);
865 }
866 let mut cases: Vec<Imm> = func[func[info].cases].to_vec();
867 for &hole in &plan.holes {
868 calls.push(BlockCall::new(hit, empty));
869 cases.push(Imm::int(hole, plan.ty));
870 }
871 let targets = func.push_block_calls(&calls);
872 let cases = func.push_imms(&cases);
873 let info = func.add_switch(rucc_ir::SwitchInfo { targets, cases });
874 func[plan.inst].extra = Extra::Switch(info);
875
876 let mut gone = HashSet::new();
879 for &arm in &plan.arms {
880 if gone.insert(arm) {
881 func.remove_block(arm);
882 }
883 }
884}
885
886#[cfg(test)]
887mod tests {
888 use std::collections::{HashMap, HashSet};
889
890 use std::sync::Arc;
891
892 use rucc_base::{Interner, Symbol};
893 use rucc_cost::Goal;
894 use rucc_ir::{
895 Block, Builder, Datum, Extra, Flags, Func, Global, InstData, Linkage, Module, Opcode, Pic,
896 Signature, Type, Value,
897 };
898 use rucc_target::{TargetInfo, Triple};
899
900 use super::{PLACE_IS_ODD, SwitchConv};
901 use crate::image::Images;
902 use crate::stats::Kind;
903 use crate::{Fuel, Pass, ReadOnly, Stats, Table};
904
905 fn i32() -> Type {
907 Type::int(32)
908 }
909
910 fn convert(func: &mut Func) -> Stats {
912 SwitchConv.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
913 }
914
915 fn tabled(func: &mut Func) -> (Stats, Vec<Table>) {
918 tabled_for(func, Goal::Speed)
919 }
920
921 fn tabled_for(func: &mut Func, goal: Goal) -> (Stats, Vec<Table>) {
923 let mut names = Interner::new();
924 let taken = HashSet::new();
925 let mut data = ReadOnly::new(&mut names, &taken, 64, 0);
926 let mut an = crate::Analyses::new(crate::Machine::with(None, goal));
927 let stats = SwitchConv.run_emitting(func, &mut an, &mut Fuel::unlimited(), &mut data);
928 (stats, data.into_tables())
929 }
930
931 fn returning(ty: Type, labels: &[i128], answers: &[i128]) -> Func {
936 let mut names = Interner::new();
937 let mut func = Func::new(names.intern("f"), Signature::new());
938 let head = func.create_block();
939 let value = func.append_param(head, ty);
940 let default = func.create_block();
941 let arms: Vec<Block> = answers.iter().map(|_| func.create_block()).collect();
942 for (&arm, &answer) in arms.iter().zip(answers) {
943 let mut build = Builder::new(&mut func, arm);
944 let it = build.iconst(ty, answer);
945 build.ret(&[it]);
946 }
947 let mut build = Builder::new(&mut func, default);
948 let it = build.iconst(ty, 999);
949 build.ret(&[it]);
950 let cases: Vec<(i128, Block)> = labels.iter().copied().zip(arms.iter().copied()).collect();
951 Builder::new(&mut func, head).switch(value, default, &cases);
952 func
953 }
954
955 fn cases(func: &Func) -> Vec<usize> {
957 let head = func.entry().expect("a function with blocks in it");
958 let term = func.terminator(head).expect("a head block has one");
959 func.successors(term).skip(1).map(|call| call.block.index()).collect()
960 }
961
962 fn arm(func: &Func) -> Block {
964 let blocks = cases(func);
965 let first = blocks[0];
966 assert!(blocks.iter().all(|&block| block == first), "the case edges did not all move");
967 Block::from_usize(first)
968 }
969
970 fn opcodes(func: &Func, block: Block) -> Vec<Opcode> {
972 func.insts(block).map(|inst| func[inst].opcode).collect()
973 }
974
975 fn answer(func: &Func, block: Block, label: i128) -> i128 {
981 looked_up(func, block, label, &[])
982 }
983
984 fn looked_up(func: &Func, block: Block, label: i128, tables: &[Table]) -> i128 {
989 let head = func.entry().expect("a function with blocks in it");
990 let mut values: HashMap<Value, i128> = HashMap::new();
991 values.insert(func[head].params[0], label);
992 for inst in func.insts(block) {
993 let data = func[inst];
994 let Some(result) = data.first_result else {
995 let args = func[data.args].to_vec();
996 let handed = match data.opcode {
997 Opcode::Return => args[0],
998 Opcode::Jump => {
999 func[func.successors(inst).next().expect("a jump goes").args][0]
1000 }
1001 other => panic!("a block this pass wrote ends in {other:?}"),
1002 };
1003 return values[&handed];
1004 };
1005 let args: Vec<i128> = func[data.args].iter().map(|arg| values[arg]).collect();
1006 let it = match data.opcode {
1007 Opcode::IConst => {
1008 let (imm, ty) = crate::fold::constant(func, result).expect("a constant is one");
1009 imm.signed(ty)
1010 }
1011 Opcode::Mul => args[0].wrapping_mul(args[1]),
1012 Opcode::Add => args[0].wrapping_add(args[1]),
1013 Opcode::Sub => args[0].wrapping_sub(args[1]),
1014 Opcode::ZExt => {
1016 let from = func[func[data.args][0]].ty;
1017 super::wrap(args[0], from).rem_euclid(1 << from.bits())
1018 }
1019 Opcode::SExt | Opcode::PtrToInt | Opcode::IntToPtr => args[0],
1020 Opcode::GlobalAddr => 0,
1021 Opcode::PtrAdd => args[0] + args[1],
1022 Opcode::Load => {
1025 assert_eq!(tables.len(), 1, "a load with no single table to read");
1026 let table = &tables[0];
1027 let bytes = i128::from(table.ty.bits() / 8);
1028 assert_eq!(args[0] % bytes, 0, "a load between two cells");
1029 let at = usize::try_from(args[0] / bytes).expect("a load before the table");
1030 let cell = *table.cells.get(at).expect("a load after the table");
1031 cell + table.to.get(at).copied().flatten().map_or(0, spot)
1032 }
1033 other => panic!("this pass does not write {other:?}"),
1034 };
1035 let ty = func[result].ty;
1037 values.insert(result, if ty.is_int() { super::wrap(it, ty) } else { it });
1038 }
1039 panic!("a block with no terminator");
1040 }
1041
1042 fn spot(name: Symbol) -> i128 {
1044 1000 * (i128::from(name.raw()) + 1)
1045 }
1046
1047 fn fired(stats: &Stats) -> bool {
1049 stats.total(Kind::Optimized) > 0
1050 }
1051
1052 #[test]
1053 fn labels_that_run_with_their_answers_become_one_addition() {
1054 let mut func = returning(i32(), &[0, 1, 2, 3], &[1, 2, 3, 4]);
1055 assert!(fired(&convert(&mut func)));
1056 let arm = arm(&func);
1057 assert_eq!(opcodes(&func, arm), [Opcode::IConst, Opcode::Add, Opcode::Return]);
1058 for label in 0..4 {
1059 assert_eq!(answer(&func, arm, label), label + 1);
1060 }
1061 }
1062
1063 #[test]
1064 fn answers_that_are_a_multiple_of_the_label_become_a_multiplication() {
1065 let mut func = returning(i32(), &[3, 4, 5, 6], &[30, 40, 50, 60]);
1066 assert!(fired(&convert(&mut func)));
1067 let arm = arm(&func);
1068 assert_eq!(opcodes(&func, arm), [Opcode::IConst, Opcode::Mul, Opcode::Return]);
1069 for label in 3..7 {
1070 assert_eq!(answer(&func, arm, label), label * 10);
1071 }
1072 }
1073
1074 #[test]
1075 fn answers_that_are_all_the_same_become_the_constant_they_all_were() {
1076 let mut func = returning(i32(), &[7, 8, 9, 10], &[9, 9, 9, 9]);
1077 assert!(fired(&convert(&mut func)));
1078 let arm = arm(&func);
1079 assert_eq!(opcodes(&func, arm), [Opcode::IConst, Opcode::Return]);
1080 assert_eq!(answer(&func, arm, 8), 9);
1081 }
1082
1083 #[test]
1084 fn labels_that_run_below_zero_are_a_run_like_any_other() {
1085 let mut func = returning(i32(), &[-2, -1, 0, 1], &[-4, -2, 0, 2]);
1086 assert!(fired(&convert(&mut func)));
1087 let arm = arm(&func);
1088 for label in -2..2 {
1089 assert_eq!(answer(&func, arm, label), label * 2);
1090 }
1091 }
1092
1093 #[test]
1100 fn a_line_that_only_holds_by_wrapping_still_holds() {
1101 let ty = Type::int(8);
1102 let mut func = returning(ty, &[0, 1, 2], &[0, 100, -56]);
1103 assert!(fired(&convert(&mut func)));
1104 let arm = arm(&func);
1105 assert_eq!(answer(&func, arm, 2), -56);
1106 }
1107
1108 #[test]
1109 fn labels_with_a_hole_in_them_are_left_alone_where_no_table_can_be_made() {
1110 let mut func = returning(i32(), &[0, 1, 3], &[1, 2, 4]);
1111 assert!(!fired(&convert(&mut func)));
1112 assert_eq!(cases(&func).len(), 3);
1113 }
1114
1115 #[test]
1116 fn answers_that_are_not_a_line_are_left_alone_where_no_table_can_be_made() {
1117 let mut func = returning(i32(), &[0, 1, 2], &[5, 9, 2]);
1118 assert!(!fired(&convert(&mut func)));
1119 }
1120
1121 #[test]
1122 fn two_labels_are_not_enough_to_pay_for_the_arithmetic() {
1123 let mut func = returning(i32(), &[0, 1], &[1, 2]);
1124 assert!(!fired(&convert(&mut func)));
1125 }
1126
1127 #[test]
1128 fn an_answer_wider_than_its_label_is_left_alone_where_no_table_can_be_made() {
1129 let mut names = Interner::new();
1130 let mut func = Func::new(names.intern("f"), Signature::new());
1131 let head = func.create_block();
1132 let value = func.append_param(head, i32());
1133 let default = func.create_block();
1134 let arms: Vec<Block> = (0..3).map(|_| func.create_block()).collect();
1135 for (index, &arm) in arms.iter().enumerate() {
1136 let mut build = Builder::new(&mut func, arm);
1137 let it = build.iconst(Type::int(64), index as i128 + 1);
1138 build.ret(&[it]);
1139 }
1140 let mut build = Builder::new(&mut func, default);
1141 let it = build.iconst(Type::int(64), 0);
1142 build.ret(&[it]);
1143 let cases: Vec<(i128, Block)> = (0..3).zip(arms.iter().copied()).collect();
1144 Builder::new(&mut func, head).switch(value, default, &cases);
1145 assert!(!fired(&convert(&mut func)));
1146 }
1147
1148 #[test]
1149 fn an_arm_something_else_reaches_is_left_alone() {
1150 let mut func = returning(i32(), &[0, 1, 2], &[1, 2, 3]);
1151 let default = Block::from_usize(1);
1154 let arm = Block::from_usize(2);
1155 let term = func.terminator(default).expect("the default returns");
1156 func.remove_inst(term);
1157 Builder::new(&mut func, default).jump(arm, &[]);
1158 assert!(!fired(&convert(&mut func)));
1159 }
1160
1161 #[test]
1162 fn an_arm_that_is_also_the_default_is_left_alone() {
1163 let mut names = Interner::new();
1164 let mut func = Func::new(names.intern("f"), Signature::new());
1165 let head = func.create_block();
1166 let value = func.append_param(head, i32());
1167 let shared = func.create_block();
1168 let mut build = Builder::new(&mut func, shared);
1169 let it = build.iconst(i32(), 1);
1170 build.ret(&[it]);
1171 let others: Vec<Block> = (0..2).map(|_| func.create_block()).collect();
1172 for (index, &arm) in others.iter().enumerate() {
1173 let mut build = Builder::new(&mut func, arm);
1174 let it = build.iconst(i32(), index as i128 + 2);
1175 build.ret(&[it]);
1176 }
1177 let cases = [(0, shared), (1, others[0]), (2, others[1])];
1178 Builder::new(&mut func, head).switch(value, shared, &cases);
1179 assert!(!fired(&convert(&mut func)));
1180 }
1181
1182 #[test]
1183 fn arms_that_join_keep_what_they_pass_beside_the_answer() {
1184 let mut names = Interner::new();
1185 let mut func = Func::new(names.intern("f"), Signature::new());
1186 let head = func.create_block();
1187 let value = func.append_param(head, i32());
1188 let alongside = func.append_param(head, i32());
1189 let join = func.create_block();
1190 let handed = func.append_param(join, i32());
1191 let carried = func.append_param(join, i32());
1192 Builder::new(&mut func, join).ret(&[handed, carried]);
1193 let default = func.create_block();
1194 let mut build = Builder::new(&mut func, default);
1195 let it = build.iconst(i32(), 999);
1196 build.jump(join, &[it, alongside]);
1197 let arms: Vec<Block> = (0..3).map(|_| func.create_block()).collect();
1198 for (index, &arm) in arms.iter().enumerate() {
1199 let mut build = Builder::new(&mut func, arm);
1200 let it = build.iconst(i32(), index as i128 + 1);
1201 build.jump(join, &[it, alongside]);
1202 }
1203 let cases: Vec<(i128, Block)> = (0..3).zip(arms.iter().copied()).collect();
1204 Builder::new(&mut func, head).switch(value, default, &cases);
1205 assert!(fired(&convert(&mut func)));
1206
1207 let arm = arm(&func);
1208 assert_eq!(answer(&func, arm, 2), 3);
1209 let term = func.terminator(arm).expect("the block ends in a jump");
1211 let call = func.successors(term).next().expect("a jump goes somewhere");
1212 assert_eq!(func[call.args][1], alongside);
1213 }
1214
1215 #[test]
1216 fn arms_that_hand_on_two_different_things_are_left_alone() {
1217 let mut names = Interner::new();
1218 let mut func = Func::new(names.intern("f"), Signature::new());
1219 let head = func.create_block();
1220 let value = func.append_param(head, i32());
1221 let join = func.create_block();
1222 let first = func.append_param(join, i32());
1223 let second = func.append_param(join, i32());
1224 Builder::new(&mut func, join).ret(&[first, second]);
1225 let default = func.create_block();
1226 let mut build = Builder::new(&mut func, default);
1227 let it = build.iconst(i32(), 999);
1228 build.jump(join, &[it, it]);
1229 let arms: Vec<Block> = (0..3).map(|_| func.create_block()).collect();
1230 for (index, &arm) in arms.iter().enumerate() {
1231 let mut build = Builder::new(&mut func, arm);
1232 let one = build.iconst(i32(), index as i128 + 1);
1233 let two = build.iconst(i32(), index as i128 + 10);
1234 build.jump(join, &[one, two]);
1235 }
1236 let cases: Vec<(i128, Block)> = (0..3).zip(arms.iter().copied()).collect();
1237 Builder::new(&mut func, head).switch(value, default, &cases);
1238 assert!(!fired(&convert(&mut func)));
1239 }
1240
1241 #[test]
1242 fn an_arm_that_does_something_is_left_alone() {
1243 let mut names = Interner::new();
1244 let mut func = Func::new(names.intern("f"), Signature::new());
1245 let head = func.create_block();
1246 let value = func.append_param(head, i32());
1247 let default = func.create_block();
1248 let mut build = Builder::new(&mut func, default);
1249 let it = build.iconst(i32(), 999);
1250 build.ret(&[it]);
1251 let arms: Vec<Block> = (0..3).map(|_| func.create_block()).collect();
1252 for (index, &arm) in arms.iter().enumerate() {
1253 let mut build = Builder::new(&mut func, arm);
1254 let it = build.iconst(i32(), index as i128 + 1);
1255 let sum = build.binary(Opcode::Add, it, value, Flags::NONE);
1257 build.ret(&[sum]);
1258 }
1259 let cases: Vec<(i128, Block)> = (0..3).zip(arms.iter().copied()).collect();
1260 Builder::new(&mut func, head).switch(value, default, &cases);
1261 assert!(!fired(&convert(&mut func)));
1262 }
1263
1264 #[test]
1265 fn the_default_goes_where_it_went() {
1266 let mut func = returning(i32(), &[0, 1, 2, 3], &[1, 2, 3, 4]);
1267 let head = func.entry().expect("a function with blocks in it");
1268 let before = func.terminator(head).expect("a head block has one");
1269 let was = func.successors(before).next().expect("a switch has a default").block;
1270 assert!(fired(&convert(&mut func)));
1271 let after = func.terminator(head).expect("a head block has one");
1272 let now = func.successors(after).next().expect("a switch has a default").block;
1273 assert_eq!(was, now, "the default moved");
1274 }
1275
1276 const LOOKUP: [Opcode; 6] = [
1278 Opcode::ZExt,
1279 Opcode::IConst,
1280 Opcode::Mul,
1281 Opcode::GlobalAddr,
1282 Opcode::PtrAdd,
1283 Opcode::Load,
1284 ];
1285
1286 #[test]
1287 fn answers_that_are_not_a_line_are_one_load_from_a_table() {
1288 let mut func = returning(i32(), &[0, 1, 2, 3], &[5, 9, 2, 7]);
1289 let (stats, tables) = tabled(&mut func);
1290 assert!(fired(&stats));
1291 assert_eq!(tables.len(), 1);
1292 assert_eq!(tables[0].ty, i32());
1293 assert_eq!(tables[0].cells, [5, 9, 2, 7]);
1294 let arm = arm(&func);
1295 let mut want = LOOKUP.to_vec();
1296 want.push(Opcode::Return);
1297 assert_eq!(opcodes(&func, arm), want);
1298 for (label, answer) in [(0, 5), (1, 9), (2, 2), (3, 7)] {
1299 assert_eq!(looked_up(&func, arm, label, &tables), answer);
1300 }
1301 }
1302
1303 #[test]
1308 fn a_hole_is_filled_with_what_a_default_that_only_answers_gives() {
1309 let mut func = returning(i32(), &[1, 2, 4, 5], &[10, 20, 40, 55]);
1310 let head = func.entry().expect("a function with blocks in it");
1311 let before = func.terminator(head).expect("a head block has one");
1312 let default = func.successors(before).next().expect("a switch has a default").block;
1313 let (stats, tables) = tabled(&mut func);
1314 assert!(fired(&stats));
1315 assert_eq!(tables[0].cells, [10, 20, 999, 40, 55]);
1316 assert_eq!(cases(&func).len(), 5, "the hole was not given a case");
1317 let after = func.terminator(head).expect("a head block has one");
1318 assert_eq!(func.successors(after).next().map(|call| call.block), Some(default));
1319 let arm = arm(&func);
1320 for (label, answer) in [(1, 10), (2, 20), (3, 999), (4, 40), (5, 55)] {
1321 assert_eq!(looked_up(&func, arm, label, &tables), answer);
1322 }
1323 }
1324
1325 #[test]
1330 fn a_hole_still_goes_to_a_default_that_does_more_than_answer() {
1331 let mut func = returning(i32(), &[1, 2, 4, 5], &[10, 20, 40, 55]);
1332 let head = func.entry().expect("a function with blocks in it");
1333 let before = func.terminator(head).expect("a head block has one");
1334 let default = func.successors(before).next().expect("a switch has a default").block;
1335 let label = func[func[before].args][0];
1336 let ret = func.terminator(default).expect("the default returns");
1337 func.remove_inst(ret);
1338 Builder::new(&mut func, default).ret(&[label]);
1339 let (stats, tables) = tabled(&mut func);
1340 assert!(fired(&stats));
1341 assert_eq!(tables[0].cells, [10, 20, 0, 40, 55]);
1342 assert_eq!(cases(&func).len(), 4, "a hole was given a case");
1343 let after = func.terminator(head).expect("a head block has one");
1344 assert_eq!(func.successors(after).next().map(|call| call.block), Some(default));
1345 let arm = arm(&func);
1346 for (label, answer) in [(1, 10), (2, 20), (4, 40), (5, 55)] {
1347 assert_eq!(looked_up(&func, arm, label, &tables), answer);
1348 }
1349 }
1350
1351 #[test]
1356 fn labels_below_zero_index_from_the_lowest_of_them() {
1357 let ty = Type::int(8);
1358 let labels = [-128, -3, -1, 0, 2, 127];
1359 let answers = [7, -5, 11, 3, -100, 42];
1360 let mut func = returning(ty, &labels, &answers);
1361 let (stats, _) = tabled(&mut func);
1364 assert!(!fired(&stats), "a table of mostly holes was made");
1365
1366 let labels = [-3, -2, -1, 0, 2];
1367 let answers = [7, -5, 11, 3, -100];
1368 let mut func = returning(ty, &labels, &answers);
1369 let (stats, tables) = tabled(&mut func);
1370 assert!(fired(&stats));
1371 assert_eq!(tables[0].cells, [7, -5, 11, 3, -25, -100]);
1373 let arm = arm(&func);
1374 assert_eq!(opcodes(&func, arm)[..2], [Opcode::IConst, Opcode::Sub]);
1375 for (&label, &answer) in labels.iter().zip(&answers).chain([(&1, &-25)]) {
1376 assert_eq!(looked_up(&func, arm, label, &tables), answer);
1377 }
1378 }
1379
1380 #[test]
1382 fn an_answer_wider_than_its_label_is_a_table_of_the_wider_type() {
1383 let mut names = Interner::new();
1384 let answers = [1i128 << 40, 3, -1, 1 << 33];
1385 let mut func = Func::new(names.intern("f"), Signature::new());
1386 let head = func.create_block();
1387 let value = func.append_param(head, i32());
1388 let default = func.create_block();
1389 let arms: Vec<Block> = answers.iter().map(|_| func.create_block()).collect();
1390 for (&arm, &answer) in arms.iter().zip(&answers) {
1391 let mut build = Builder::new(&mut func, arm);
1392 let it = build.iconst(Type::int(64), answer);
1393 build.ret(&[it]);
1394 }
1395 let mut build = Builder::new(&mut func, default);
1396 let it = build.iconst(Type::int(64), 0);
1397 build.ret(&[it]);
1398 let cases: Vec<(i128, Block)> = (10..14).zip(arms.iter().copied()).collect();
1399 Builder::new(&mut func, head).switch(value, default, &cases);
1400 let (stats, tables) = tabled(&mut func);
1401 assert!(fired(&stats));
1402 assert_eq!(tables[0].ty, Type::int(64));
1403 let arm = arm(&func);
1404 for (label, &answer) in (10..14).zip(&answers) {
1405 assert_eq!(looked_up(&func, arm, label, &tables), answer);
1406 }
1407 }
1408
1409 #[test]
1410 fn labels_too_far_apart_for_a_table_are_left_alone() {
1411 let mut func = returning(i32(), &[0, 100, 200], &[1, 5, 3]);
1412 let (stats, tables) = tabled(&mut func);
1413 assert!(!fired(&stats));
1414 assert!(tables.is_empty());
1415 }
1416
1417 #[test]
1418 fn a_line_is_still_arithmetic_where_a_table_could_be_made() {
1419 let mut func = returning(i32(), &[0, 1, 2, 3], &[1, 2, 3, 4]);
1420 let (stats, tables) = tabled(&mut func);
1421 assert!(fired(&stats));
1422 assert!(tables.is_empty(), "a table was made for a line");
1423 }
1424
1425 #[test]
1426 fn a_label_wider_than_a_word_gets_no_table() {
1427 let mut func = returning(Type::int(128), &[0, 1, 2, 3], &[5, 9, 2, 7]);
1428 let (stats, tables) = tabled(&mut func);
1429 assert!(!fired(&stats));
1430 assert!(tables.is_empty());
1431 }
1432
1433 #[test]
1440 fn the_answer_is_read_from_the_place_the_arms_disagree_about() {
1441 let mut names = Interner::new();
1442 let mut func = Func::new(names.intern("f"), Signature::new());
1443 let head = func.create_block();
1444 let value = func.append_param(head, i32());
1445 let join = func.create_block();
1446 let first = func.append_param(join, i32());
1447 let second = func.append_param(join, i32());
1448 Builder::new(&mut func, join).ret(&[second, first]);
1449 let default = func.create_block();
1450 let arms: Vec<Block> = (0..3).map(|_| func.create_block()).collect();
1451 let mut build = Builder::new(&mut func, head);
1452 let one = build.iconst(i32(), 1);
1453 let cases: Vec<(i128, Block)> = (0..3).zip(arms.iter().copied()).collect();
1454 build.switch(value, default, &cases);
1455 let mut build = Builder::new(&mut func, default);
1456 let it = build.iconst(i32(), 999);
1457 build.jump(join, &[one, it]);
1458 for (&arm, answer) in arms.iter().zip([10, 2, 3]) {
1459 let mut build = Builder::new(&mut func, arm);
1460 let it = build.iconst(i32(), answer);
1461 build.jump(join, &[one, it]);
1462 }
1463 assert!(!fired(&convert(&mut func)), "ten, two and three were taken for a line");
1464 let (stats, tables) = tabled(&mut func);
1465 assert!(fired(&stats));
1466 assert_eq!(tables[0].cells, [10, 2, 3]);
1467 }
1468
1469 #[test]
1474 fn a_table_for_size_has_cells_as_narrow_as_its_answers() {
1475 let mut func = returning(i32(), &[0, 1, 2, 3], &[5, -9, 2, 7]);
1476 let (stats, tables) = tabled_for(&mut func, Goal::Size);
1477 assert!(fired(&stats));
1478 assert_eq!(tables[0].ty, Type::int(8));
1479 let at = arm(&func);
1480 assert!(opcodes(&func, at).contains(&Opcode::SExt));
1481 for (label, answer) in [(0, 5), (1, -9), (2, 2), (3, 7)] {
1482 assert_eq!(looked_up(&func, at, label, &tables), answer);
1483 }
1484
1485 let mut func = returning(i32(), &[0, 1, 2, 3], &[5, 200, 2, 255]);
1486 let (_, tables) = tabled_for(&mut func, Goal::Size);
1487 assert_eq!(tables[0].ty, Type::int(8));
1488 let at = arm(&func);
1489 assert!(opcodes(&func, at).contains(&Opcode::ZExt));
1490 for (label, answer) in [(0, 5), (1, 200), (2, 2), (3, 255)] {
1491 assert_eq!(looked_up(&func, at, label, &tables), answer);
1492 }
1493
1494 let mut func = returning(i32(), &[0, 1, 2, 3], &[5, -300, 2, 40000]);
1495 let (_, tables) = tabled_for(&mut func, Goal::Size);
1496 assert_eq!(tables[0].ty, i32(), "a cell narrower than an answer that needs all of it");
1497
1498 let mut func = returning(i32(), &[0, 1, 2, 3], &[5, -9, 2, 7]);
1499 let (_, tables) = tabled_for(&mut func, Goal::Speed);
1500 assert_eq!(tables[0].ty, i32());
1501 }
1502
1503 struct Pointing {
1506 names: Interner,
1507 module: Module,
1508 func: Func,
1509 places: Vec<Symbol>,
1510 }
1511
1512 fn pointing(labels: &[i128], written: Option<usize>, into: i128) -> Pointing {
1520 let mut names = Interner::new();
1521 let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
1522 let mut module = Module::new(names.intern("t.c"), &target);
1523 let places: Vec<Symbol> =
1524 (0..=labels.len()).map(|k| names.intern(&format!("s{k}"))).collect();
1525 for (k, &name) in places.iter().enumerate() {
1526 let mut global = Global::new(name, 4, 1);
1527 global.linkage = Linkage::Internal;
1528 global.constant = written != Some(k);
1529 global.init = Some(module.push_data(&[Datum::Zero(4)]));
1530 module.add_global(global);
1531 }
1532 let mut func = Func::new(names.intern("f"), Signature::new());
1533 let head = func.create_block();
1534 let value = func.append_param(head, i32());
1535 let blocks: Vec<Block> = places.iter().map(|_| func.create_block()).collect();
1536 for ((k, &block), &name) in blocks.iter().enumerate().zip(&places) {
1537 let mut build = Builder::new(&mut func, block);
1538 let data = InstData { extra: Extra::Symbol(name), ..InstData::new(Opcode::GlobalAddr) };
1539 let mut it = build.value(data, Type::PTR);
1540 if into != 0 && k < labels.len() {
1541 let bytes = build.iconst(Type::int(64), into * k as i128);
1542 it = build.binary(Opcode::PtrAdd, it, bytes, Flags::NONE);
1543 }
1544 build.ret(&[it]);
1545 }
1546 let (&default, arms) = blocks.split_last().expect("a default");
1547 let cases: Vec<(i128, Block)> = labels.iter().copied().zip(arms.iter().copied()).collect();
1548 Builder::new(&mut func, head).switch(value, default, &cases);
1549 Pointing { names, module, func, places }
1550 }
1551
1552 fn placed(pointing: &mut Pointing, measures: bool) -> (Stats, Vec<Table>) {
1555 let taken = HashSet::new();
1556 let mut data = ReadOnly::new(&mut pointing.names, &taken, 64, 0).measuring(measures);
1557 let images = Arc::new(Images::of(&pointing.module, Pic::Executable));
1558 let mut an = crate::Analyses::new(crate::Machine::with(None, Goal::Speed)).reading(images);
1559 let stats =
1560 SwitchConv.run_emitting(&mut pointing.func, &mut an, &mut Fuel::unlimited(), &mut data);
1561 (stats, data.into_tables())
1562 }
1563
1564 #[test]
1565 fn answers_that_are_addresses_are_a_table_of_how_far_they_are_from_it() {
1566 let mut pointing = pointing(&[0, 1, 2, 3], None, 0);
1567 let (stats, tables) = placed(&mut pointing, true);
1568 assert!(fired(&stats));
1569 assert_eq!(tables.len(), 1);
1570 assert_eq!(tables[0].ty, i32());
1571 assert_eq!(tables[0].cells, [0, 0, 0, 0]);
1572 let want: Vec<Option<Symbol>> = pointing.places[..4].iter().copied().map(Some).collect();
1573 assert_eq!(tables[0].to, want);
1574 assert_eq!(tables[0].cells, [0, 0, 0, 0]);
1575 let func = &pointing.func;
1576 let arm = arm(func);
1577 let mut want = LOOKUP.to_vec();
1578 want.extend([
1579 Opcode::SExt,
1580 Opcode::PtrToInt,
1581 Opcode::Add,
1582 Opcode::IntToPtr,
1583 Opcode::Return,
1584 ]);
1585 assert_eq!(opcodes(func, arm), want);
1586 for label in 0..4 {
1587 let place = pointing.places[label as usize];
1588 assert_eq!(looked_up(func, arm, label, &tables), spot(place));
1589 }
1590 }
1591
1592 #[test]
1595 fn an_address_part_way_into_a_name_is_the_distance_to_it_and_the_bytes_in() {
1596 let mut pointing = pointing(&[0, 1, 2, 3], None, 4);
1597 let (stats, tables) = placed(&mut pointing, true);
1598 assert!(fired(&stats));
1599 assert_eq!(tables[0].cells, [0, 4, 8, 12]);
1600 let arm = arm(&pointing.func);
1601 for label in 0..4 {
1602 let place = pointing.places[label as usize];
1603 let got = looked_up(&pointing.func, arm, label, &tables);
1604 assert_eq!(got, spot(place) + 4 * label, "{label}");
1605 }
1606 }
1607
1608 #[test]
1611 fn a_hole_in_a_table_of_addresses_is_where_the_default_points() {
1612 let mut pointing = pointing(&[1, 2, 4, 5], None, 0);
1613 let (stats, tables) = placed(&mut pointing, true);
1614 assert!(fired(&stats));
1615 let places = &pointing.places;
1616 let want = [places[0], places[1], places[4], places[2], places[3]].map(Some);
1617 assert_eq!(tables[0].to, want);
1618 assert_eq!(cases(&pointing.func).len(), 5, "the hole was not given a case");
1619 let arm = arm(&pointing.func);
1620 for (label, place) in [(1, 0), (2, 1), (3, 4), (4, 2), (5, 3)] {
1621 let got = looked_up(&pointing.func, arm, label, &tables);
1622 assert_eq!(got, spot(places[place]), "{label}");
1623 }
1624 }
1625
1626 #[test]
1629 fn an_answer_that_is_not_read_only_data_keeps_its_switch() {
1630 let mut pointing = pointing(&[0, 1, 2, 3], Some(2), 0);
1631 let (stats, tables) = placed(&mut pointing, true);
1632 assert!(!fired(&stats));
1633 assert!(tables.is_empty());
1634 assert_eq!(stats.count(Kind::Missed, PLACE_IS_ODD), 1, "{stats:?}");
1635 }
1636
1637 #[test]
1638 fn a_target_with_no_four_byte_distance_keeps_its_switch() {
1639 let mut pointing = pointing(&[0, 1, 2, 3], None, 0);
1640 let (stats, tables) = placed(&mut pointing, false);
1641 assert!(!fired(&stats));
1642 assert!(tables.is_empty());
1643 }
1644}