1use rucc_cost::Goal;
51use rucc_ir::{Def, Extra, Flags, Func, Imm, Inst, Opcode, Type, Value};
52
53use crate::expand::{ahead, ahead_const, becomes};
54
55pub fn divisions(func: &mut Func, goal: Goal) {
57 if matches!(goal, Goal::Size) {
58 return;
59 }
60 let found: Vec<Inst> =
61 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
62 for inst in found {
63 let Some(division) = division(func, inst) else { continue };
64 let Some(program) = program(division) else { continue };
65 write(func, inst, &program);
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum Range {
72 Unsigned(u32),
74 Signed(u32),
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub struct Division {
81 pub signed: bool,
83 pub remainder: bool,
85 pub exact: bool,
87 pub width: u32,
89 pub range: Range,
91 pub divisor: i128,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub enum Step {
100 Const(i128, u32),
102 Op(Opcode, [usize; 2], u32),
105}
106
107#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct Program {
110 pub width: u32,
112 pub steps: Vec<Step>,
114}
115
116const DIVIDEND: usize = 0;
118
119impl Program {
120 fn new(width: u32) -> Self {
121 Self { width, steps: Vec::new() }
122 }
123
124 fn push(&mut self, step: Step) -> usize {
125 self.steps.push(step);
126 self.steps.len()
127 }
128
129 fn constant(&mut self, value: i128, bits: u32) -> usize {
130 self.push(Step::Const(value, bits))
131 }
132
133 fn by(&mut self, opcode: Opcode, value: usize, constant: i128, bits: u32) -> usize {
135 let constant = self.constant(constant, bits);
136 self.push(Step::Op(opcode, [value, constant], bits))
137 }
138
139 fn op(&mut self, opcode: Opcode, lhs: usize, rhs: usize, bits: u32) -> usize {
140 self.push(Step::Op(opcode, [lhs, rhs], bits))
141 }
142
143 fn convert(&mut self, opcode: Opcode, value: usize, bits: u32) -> usize {
144 self.push(Step::Op(opcode, [value, value], bits))
145 }
146
147 fn widened(&mut self, extend: Opcode) -> usize {
149 if self.width == 64 { DIVIDEND } else { self.convert(extend, DIVIDEND, 64) }
150 }
151
152 fn narrowed(&mut self, value: usize) -> usize {
154 if self.width == 64 { value } else { self.convert(Opcode::Trunc, value, self.width) }
155 }
156
157 fn negated(&mut self, value: usize) -> usize {
159 let zero = self.constant(0, self.width);
160 self.op(Opcode::Sub, zero, value, self.width)
161 }
162
163 fn left_over(&mut self, quotient: usize, divisor: i128) -> usize {
165 let back = self.by(Opcode::Mul, quotient, divisor, self.width);
166 self.op(Opcode::Sub, DIVIDEND, back, self.width)
167 }
168}
169
170#[must_use]
172pub fn program(division: Division) -> Option<Program> {
173 let Division { signed, remainder, exact, width, range, divisor } = division;
174 if !matches!(width, 8 | 16 | 32 | 64) || matches!(divisor, 0 | 1) || (signed && divisor == -1) {
175 return None;
176 }
177 let mut program = Program::new(width);
178 if exact && !remainder {
179 exactly(&mut program, signed, divisor);
180 return Some(program);
181 }
182 let size = divisor.unsigned_abs();
183 match range {
184 Range::Unsigned(bits) => {
185 let quotient = unsigned(&mut program, size, bits)?;
186 if remainder {
187 if size.is_power_of_two() {
188 let mask = i128::try_from(size - 1).ok()?;
189 program.by(Opcode::And, DIVIDEND, mask, width);
190 } else {
191 program.left_over(quotient, i128::try_from(size).ok()?);
192 }
193 } else if divisor < 0 {
194 program.negated(quotient);
195 }
196 }
197 Range::Signed(_) if size.is_power_of_two() => biased(&mut program, divisor, remainder),
198 Range::Signed(bits) => {
199 let quotient = rounded(&mut program, divisor, bits)?;
200 if remainder {
201 program.left_over(quotient, divisor);
202 }
203 }
204 }
205 Some(program)
206}
207
208#[must_use]
220pub fn multiplier(divisor: u128, bits: u32, precision: u32) -> (u128, u32) {
221 assert!(divisor > 0, "a divisor of zero has no reciprocal");
222 let up = 128 - (divisor - 1).leading_zeros();
223 assert!(bits + up < 128, "a divisor and a width this can work with");
224 let mut shift = up;
225 let low = (1u128 << (bits + up)) / divisor;
226 let high = ((1u128 << (bits + up)) + (1u128 << (bits + up - precision))) / divisor;
227 let (mut low, mut high) = (low, high);
228 while shift > 0 && low / 2 < high / 2 {
229 low /= 2;
230 high /= 2;
231 shift -= 1;
232 }
233 (high, shift)
234}
235
236fn unsigned(program: &mut Program, divisor: u128, bits: u32) -> Option<usize> {
244 let width = program.width;
245 if bits >= 128 || divisor >= 1u128 << bits {
246 return None;
247 }
248 if divisor.is_power_of_two() {
249 let shift = i128::from(divisor.trailing_zeros());
250 return Some(program.by(Opcode::LShr, DIVIDEND, shift, width));
251 }
252 if bits > 32 {
253 return None;
254 }
255 let (magic, shift) = multiplier(divisor, bits, bits);
256 let wide = program.widened(Opcode::ZExt);
257 let shifted = if magic < 1u128 << bits || bits < 32 {
258 let product = program.by(Opcode::Mul, wide, i128::try_from(magic).ok()?, 64);
259 program.by(Opcode::LShr, product, i128::from(bits + shift), 64)
260 } else {
261 let magic = i128::try_from(magic - (1u128 << 32)).ok()?;
262 let product = program.by(Opcode::Mul, wide, magic, 64);
263 let high = program.by(Opcode::LShr, product, 32, 64);
264 let sum = program.op(Opcode::Add, high, wide, 64);
265 if shift == 0 { sum } else { program.by(Opcode::LShr, sum, i128::from(shift), 64) }
266 };
267 Some(program.narrowed(shifted))
268}
269
270fn rounded(program: &mut Program, divisor: i128, bits: u32) -> Option<usize> {
278 let width = program.width;
279 let size = divisor.unsigned_abs();
280 if bits > 32 || size > 1u128 << (bits - 1) {
281 return None;
282 }
283 let (magic, shift) = multiplier(size, bits, bits - 1);
284 let wide = program.widened(Opcode::SExt);
285 let product = program.by(Opcode::Mul, wide, i128::try_from(magic).ok()?, 64);
286 let shifted = program.by(Opcode::AShr, product, i128::from(bits + shift), 64);
287 let low = program.narrowed(shifted);
288 let sign = program.by(Opcode::AShr, DIVIDEND, i128::from(width - 1), width);
289 Some(if divisor > 0 {
290 program.op(Opcode::Sub, low, sign, width)
291 } else {
292 program.op(Opcode::Sub, sign, low, width)
293 })
294}
295
296fn biased(program: &mut Program, divisor: i128, remainder: bool) {
303 let width = program.width;
304 let size = divisor.unsigned_abs();
305 let power = size.trailing_zeros();
306 let bias = if power == 1 {
307 program.by(Opcode::LShr, DIVIDEND, i128::from(width - 1), width)
308 } else {
309 let sign = program.by(Opcode::AShr, DIVIDEND, i128::from(width - 1), width);
310 program.by(Opcode::LShr, sign, i128::from(width - power), width)
311 };
312 let sum = program.op(Opcode::Add, DIVIDEND, bias, width);
313 if remainder {
314 let mask = (1i128 << power) - 1;
315 let low = program.by(Opcode::And, sum, mask, width);
316 program.op(Opcode::Sub, low, bias, width);
317 return;
318 }
319 let quotient = program.by(Opcode::AShr, sum, i128::from(power), width);
320 if divisor < 0 {
321 program.negated(quotient);
322 }
323}
324
325fn exactly(program: &mut Program, signed: bool, divisor: i128) {
334 let width = program.width;
335 let power = divisor.trailing_zeros();
336 let mut value = DIVIDEND;
337 if power > 0 {
338 let shift = if signed { Opcode::AShr } else { Opcode::LShr };
339 value = program.by(shift, DIVIDEND, i128::from(power), width);
340 }
341 let odd = divisor >> power;
342 if odd != 1 {
343 program.by(Opcode::Mul, value, signed_at(inverse(odd, width), width), width);
344 }
345}
346
347fn inverse(odd: i128, width: u32) -> u128 {
352 let odd = odd as u128;
353 let mut inverse = odd;
354 for _ in 0..6 {
355 inverse = inverse.wrapping_mul(2u128.wrapping_sub(odd.wrapping_mul(inverse)));
356 }
357 inverse & mask(width)
358}
359
360fn signed_at(bits: u128, width: u32) -> i128 {
362 let spare = 128 - width;
363 ((bits << spare) as i128) >> spare
364}
365
366fn mask(width: u32) -> u128 {
368 if width >= 128 { u128::MAX } else { (1u128 << width) - 1 }
369}
370
371fn division(func: &Func, inst: Inst) -> Option<Division> {
373 let data = &func[inst];
374 let (signed, remainder) = match data.opcode {
375 Opcode::SDiv => (true, false),
376 Opcode::UDiv => (false, false),
377 Opcode::SRem => (true, true),
378 Opcode::URem => (false, true),
379 _ => return None,
380 };
381 let &[dividend, by] = &func[data.args] else { return None };
382 let ty = func[dividend].ty;
383 if !ty.is_int() || ty.lanes() != 1 {
384 return None;
385 }
386 let imm = constant(func, by)?;
387 let divisor = if signed { imm.signed(ty) } else { i128::try_from(imm.unsigned()).ok()? };
388 Some(Division {
389 signed,
390 remainder,
391 exact: data.flags.contains(Flags::EXACT),
392 width: ty.bits(),
393 range: range(func, dividend, ty.bits(), signed),
394 divisor,
395 })
396}
397
398fn constant(func: &Func, value: Value) -> Option<Imm> {
400 let Def::Result { inst, .. } = func[value].def else { return None };
401 if func[inst].opcode != Opcode::IConst {
402 return None;
403 }
404 let Extra::Imm(imm) = func[inst].extra else { return None };
405 Some(func[imm])
406}
407
408fn range(func: &Func, value: Value, width: u32, signed: bool) -> Range {
414 let whole = if signed { Range::Signed(width) } else { Range::Unsigned(width) };
415 let Def::Result { inst, .. } = func[value].def else { return whole };
416 let Some(&from) = func[func[inst].args].first() else { return whole };
417 let bits = func[from].ty.bits();
418 if bits == 0 || bits >= width {
419 return whole;
420 }
421 match func[inst].opcode {
422 Opcode::ZExt => Range::Unsigned(bits),
423 Opcode::SExt if signed => Range::Signed(bits),
424 _ => whole,
425 }
426}
427
428fn write(func: &mut Func, inst: Inst, program: &Program) {
430 let Some((&Step::Op(opcode, args, _), before)) = program.steps.split_last() else { return };
431 let mut values = vec![func[func[inst].args][0]];
432 for &step in before {
433 let value = match step {
434 Step::Const(value, bits) => {
435 let ty = Type::int(bits);
436 ahead_const(func, inst, Imm::int(value, ty), ty)
437 }
438 Step::Op(opcode, args, bits) => {
439 let operands: Vec<Value> =
440 args[..arity(opcode)].iter().map(|&at| values[at]).collect();
441 ahead(func, inst, opcode, &operands, Type::int(bits))
442 }
443 };
444 values.push(value);
445 }
446 let operands: Vec<Value> = args[..arity(opcode)].iter().map(|&at| values[at]).collect();
447 becomes(func, inst, opcode, &operands);
448}
449
450fn arity(opcode: Opcode) -> usize {
452 match opcode {
453 Opcode::Trunc | Opcode::ZExt | Opcode::SExt => 1,
454 _ => 2,
455 }
456}
457
458#[cfg(test)]
459mod tests {
460 use rucc_base::Interner;
461 use rucc_cost::Goal;
462 use rucc_ir::{Builder, Flags, Func, Opcode, Signature, Type, Value};
463
464 use super::{Division, Program, Range, Step, divisions, mask, multiplier, program, signed_at};
465
466 fn run(program: &Program, dividend: u128, values: &mut Vec<(u128, u32)>) -> u128 {
469 values.clear();
470 values.push((dividend & mask(program.width), program.width));
471 for &step in &program.steps {
472 let value = match step {
473 Step::Const(value, bits) => (value as u128 & mask(bits), bits),
474 Step::Op(opcode, [lhs, rhs], bits) => {
475 let (a, from) = values[lhs];
476 let (b, other) = values[rhs];
477 let answer = match opcode {
478 Opcode::ZExt | Opcode::Trunc | Opcode::SExt => {
479 let right =
480 if opcode == Opcode::Trunc { from > bits } else { from < bits };
481 assert!(right, "{opcode:?} from {from} bits to {bits}");
482 if opcode == Opcode::SExt { signed_at(a, from) as u128 } else { a }
483 }
484 _ => {
485 assert_eq!((from, other), (bits, bits), "{opcode:?} at {bits} bits");
486 match opcode {
487 Opcode::Add => a.wrapping_add(b),
488 Opcode::Sub => a.wrapping_sub(b),
489 Opcode::Mul => a.wrapping_mul(b),
490 Opcode::And => a & b,
491 Opcode::LShr | Opcode::AShr => {
492 assert!(b < u128::from(bits), "a shift by {b} at {bits} bits");
493 if opcode == Opcode::LShr {
494 a >> b
495 } else {
496 (signed_at(a, bits) >> b) as u128
497 }
498 }
499 _ => panic!("{opcode:?} is not something a program writes"),
500 }
501 }
502 };
503 (answer & mask(bits), bits)
504 }
505 };
506 values.push(value);
507 }
508 let (answer, bits) = *values.last().expect("a program has steps");
509 assert_eq!(bits, program.width, "the answer is at the width of the division");
510 answer
511 }
512
513 fn truth(division: &Division, dividend: u128) -> u128 {
515 let answer = if division.signed {
516 let x = signed_at(dividend, division.width);
517 if division.remainder { x % division.divisor } else { x / division.divisor }
518 } else {
519 let d = division.divisor as u128;
520 (if division.remainder { dividend % d } else { dividend / d }) as i128
521 };
522 answer as u128 & mask(division.width)
523 }
524
525 fn ends(range: Range) -> (i128, i128) {
527 match range {
528 Range::Unsigned(bits) => (0, (1 << bits) - 1),
529 Range::Signed(bits) => (-(1 << (bits - 1)), (1 << (bits - 1)) - 1),
530 }
531 }
532
533 fn division(
534 signed: bool,
535 remainder: bool,
536 width: u32,
537 range: Range,
538 divisor: i128,
539 ) -> Division {
540 Division { signed, remainder, exact: false, width, range, divisor }
541 }
542
543 fn left(division: &Division) -> bool {
546 let size = division.divisor.unsigned_abs();
547 let (low, high) = ends(division.range);
548 let most = low.unsigned_abs().max(high.unsigned_abs());
549 let bits = match division.range {
550 Range::Unsigned(bits) | Range::Signed(bits) => bits,
551 };
552 let signed_range = matches!(division.range, Range::Signed(_));
553 matches!(division.divisor, 0 | 1)
554 || (division.signed && division.divisor == -1)
555 || (!signed_range && size > most)
556 || (signed_range && !size.is_power_of_two() && (size > most || bits > 32))
557 || (!signed_range && !size.is_power_of_two() && bits > 32)
558 }
559
560 fn check(
562 division: Division,
563 dividends: impl IntoIterator<Item = i128>,
564 values: &mut Vec<(u128, u32)>,
565 ) {
566 let Some(program) = program(division) else {
567 assert!(left(&division), "{division:?} was left as a div");
568 return;
569 };
570 assert!(!left(&division), "{division:?} was rewritten");
571 for x in dividends {
572 let x = x as u128 & mask(division.width);
573 assert_eq!(run(&program, x, values), truth(&division, x), "{division:?} of {x:#x}");
574 }
575 }
576
577 fn edges(range: Range, divisor: i128) -> Vec<i128> {
584 let (low, high) = ends(range);
585 let size = divisor.abs().max(1);
586 let mut all = vec![low, high, -1, 0, 1];
587 let mut at = 0;
588 while at <= high + 1 || -at >= low - 1 {
589 all.extend([at - 1, at, at + 1, -at - 1, -at, -at + 1]);
590 at += size;
591 }
592 all.retain(|&x| (low..=high).contains(&x));
593 all
594 }
595
596 fn random(state: &mut u64) -> u64 {
598 *state ^= *state << 13;
599 *state ^= *state >> 7;
600 *state ^= *state << 17;
601 *state
602 }
603
604 fn both() -> [(bool, bool); 4] {
605 [(false, false), (false, true), (true, false), (true, true)]
606 }
607
608 #[test]
609 fn the_magic_numbers_are_the_ones_gcc_writes() {
610 assert_eq!(multiplier(100, 32, 32), (1_374_389_535, 5));
612 assert_eq!(multiplier(7, 32, 32), ((1 << 32) + 613_566_757, 3));
614 assert_eq!(multiplier(7, 32, 31), (2_454_267_027, 2));
616 assert_eq!(multiplier(3, 32, 31), (1_431_655_766, 0));
618 assert_eq!(multiplier(10, 16, 16), (52_429, 3));
620 }
621
622 #[test]
625 fn every_eight_bit_division_by_every_divisor_is_right() {
626 let mut values = Vec::new();
627 for (signed, remainder) in both() {
628 for width in [8, 16, 32, 64] {
629 let mut ranges = vec![Range::Unsigned(8)];
630 if signed {
631 ranges.push(Range::Signed(8));
632 }
633 for range in ranges {
634 if width == 8
635 && range != (if signed { Range::Signed(8) } else { Range::Unsigned(8) })
636 {
637 continue;
638 }
639 let (low, high) = ends(range);
640 let divisors: Vec<i128> =
641 if signed { (-300..=300).collect() } else { (0..=300).collect() };
642 for divisor in divisors {
643 let divisor = match (width, signed) {
644 (8, true) => signed_at(divisor as u128 & 0xff, 8),
645 (8, false) => divisor & 0xff,
646 _ => divisor,
647 };
648 check(
649 division(signed, remainder, width, range, divisor),
650 low..=high,
651 &mut values,
652 );
653 }
654 }
655 }
656 }
657 }
658
659 #[test]
662 fn every_sixteen_bit_quotient_by_every_divisor_is_right() {
663 let mut values = Vec::new();
664 for signed in [false, true] {
665 for width in [16, 32] {
666 let range = if signed { Range::Signed(16) } else { Range::Unsigned(16) };
667 let divisors: Vec<i128> =
668 if signed { (-32_768..=32_767).collect() } else { (0..=65_535).collect() };
669 for divisor in divisors {
670 check(
671 division(signed, false, width, range, divisor),
672 edges(range, divisor),
673 &mut values,
674 );
675 }
676 }
677 }
678 let range = Range::Unsigned(16);
680 for divisor in -70_000..=70_000 {
681 check(division(true, false, 32, range, divisor), edges(range, divisor), &mut values);
682 }
683 }
684
685 #[test]
688 fn every_sixteen_bit_remainder_by_a_power_of_two_is_right() {
689 let mut values = Vec::new();
690 for (signed, remainder) in both() {
691 let range = if signed { Range::Signed(16) } else { Range::Unsigned(16) };
692 let (low, high) = ends(range);
693 for power in 1..16 {
694 for divisor in [1i128 << power, -(1i128 << power)] {
695 if !signed && divisor < 0 {
696 continue;
697 }
698 check(division(signed, remainder, 16, range, divisor), low..=high, &mut values);
699 }
700 }
701 }
702 }
703
704 #[test]
708 fn thirty_two_bit_divisions_are_right_at_the_edges_and_on_a_sample() {
709 let mut values = Vec::new();
710 let mut state = 0x9e37_79b9_7f4a_7c15;
711 let mut divisors: Vec<i128> = (-2_000..=2_000).collect();
712 for power in 1..=32 {
713 let at = 1i128 << power;
714 divisors.extend([at - 1, at, at + 1, -at + 1, -at, -at - 1]);
715 }
716 for _ in 0..2_000 {
717 divisors.push(i128::from(random(&mut state) as u32));
718 divisors.push(i128::from(random(&mut state) as i32));
719 }
720 for (signed, remainder) in both() {
721 for (width, range) in [
722 (32, if signed { Range::Signed(32) } else { Range::Unsigned(32) }),
723 (64, Range::Unsigned(32)),
724 (64, if signed { Range::Signed(32) } else { Range::Unsigned(32) }),
725 ] {
726 let (low, high) = ends(range);
727 for &divisor in &divisors {
728 let divisor = if width == 32 {
729 let bits = divisor as u128 & mask(32);
730 if signed { signed_at(bits, 32) } else { bits as i128 }
731 } else {
732 divisor
733 };
734 if !signed && divisor < 0 {
735 continue;
736 }
737 let size = divisor.abs().max(1);
738 let mut dividends = vec![low, low + 1, -1, 0, 1, 2, high - 1, high];
739 for end in [low, high] {
740 let near = end / size * size;
741 dividends.extend([near - 1, near, near + 1, near - size, near + size]);
742 }
743 for _ in 0..64 {
744 let x = random(&mut state) as u128 & mask(32);
745 dividends.push(if matches!(range, Range::Signed(_)) {
746 signed_at(x, 32)
747 } else {
748 x as i128
749 });
750 }
751 dividends.retain(|x| (low..=high).contains(x));
752 check(
753 division(signed, remainder, width, range, divisor),
754 dividends,
755 &mut values,
756 );
757 }
758 }
759 }
760 }
761
762 #[test]
766 fn sixty_four_bits_rewrite_only_a_power_of_two() {
767 let mut values = Vec::new();
768 let mut state = 0x2545_f491_4f6c_dd1d;
769 for (signed, remainder) in both() {
770 let range = if signed { Range::Signed(64) } else { Range::Unsigned(64) };
771 let (low, high) = ends(range);
772 let mut dividends = vec![low, low + 1, -1, 0, 1, high - 1, high];
773 for _ in 0..512 {
774 let x = u128::from(random(&mut state));
775 dividends.push(if signed { signed_at(x, 64) } else { x as i128 });
776 }
777 dividends.retain(|x| (low..=high).contains(x));
778 for power in 1..64 {
779 for divisor in [1i128 << power, -(1i128 << power)] {
780 if !signed && divisor < 0 {
781 continue;
782 }
783 check(
784 division(signed, remainder, 64, range, divisor),
785 dividends.clone(),
786 &mut values,
787 );
788 }
789 }
790 for divisor in [3, 7, 10, 1_000_000_007] {
791 check(
792 division(signed, remainder, 64, range, divisor),
793 dividends.clone(),
794 &mut values,
795 );
796 }
797 }
798 }
799
800 #[test]
803 fn an_exact_division_is_right_over_every_multiple_it_is_given() {
804 let mut values = Vec::new();
805 let mut state = 0x1234_5678_9abc_def1;
806 for signed in [false, true] {
807 for width in [8, 16, 32, 64] {
808 let range = if signed { Range::Signed(width) } else { Range::Unsigned(width) };
809 let (low, high) = ends(range);
810 for divisor in (-100i128..=100).chain([12, 24, 40, 56, 1 << 20, 3 << 30]) {
811 if (!signed && divisor < 0) || divisor < low || divisor > high {
812 continue;
813 }
814 let exact =
815 Division { exact: true, ..division(signed, false, width, range, divisor) };
816 let Some(program) = program(exact) else {
817 assert!(
818 matches!(divisor, 0 | 1) || (signed && divisor == -1),
819 "{exact:?} was left"
820 );
821 continue;
822 };
823 let size = divisor.abs();
824 let mut dividends: Vec<i128> = if width <= 16 {
825 (low / size..=high / size).map(|times| times * divisor).collect()
826 } else {
827 (0..256)
828 .map(|_| {
829 let times = signed_at(u128::from(random(&mut state)), 64)
830 % (high / size).max(1);
831 if signed { times * divisor } else { times.abs() * divisor }
832 })
833 .collect()
834 };
835 dividends.extend([0, divisor, high / size * divisor]);
836 dividends.retain(|x| (low..=high).contains(x));
837 for x in dividends {
838 let x = x as u128 & mask(width);
839 assert_eq!(
840 run(&program, x, &mut values),
841 truth(&exact, x),
842 "{exact:?} of {x:#x}"
843 );
844 }
845 }
846 }
847 }
848 }
849
850 fn one(param: Type, ret: Type, body: impl FnOnce(&mut Builder<'_>, Value) -> Value) -> Func {
852 let mut names = Interner::new();
853 let mut func = Func::new(
854 names.intern("f"),
855 Signature::new().with_params(&[param]).with_returns(&[ret]),
856 );
857 let entry = func.create_block();
858 let x = func.append_param(entry, param);
859 let mut build = Builder::new(&mut func, entry);
860 let answer = body(&mut build, x);
861 build.ret(&[answer]);
862 func
863 }
864
865 fn opcodes(func: &Func) -> Vec<Opcode> {
867 let entry = func.blocks().next().expect("an entry");
868 func.insts(entry).map(|inst| func[inst].opcode).filter(|&op| op != Opcode::IConst).collect()
869 }
870
871 fn divided(
872 opcode: Opcode,
873 divisor: i128,
874 flags: Flags,
875 ) -> impl FnOnce(&mut Builder<'_>, Value) -> Value {
876 move |build, x| {
877 let ty = build.func()[x].ty;
878 let by = build.iconst(ty, divisor);
879 build.binary(opcode, x, by, flags)
880 }
881 }
882
883 #[test]
884 fn an_unsigned_division_by_seven_is_a_multiply_an_add_and_two_shifts() {
885 let i32 = Type::int(32);
886 let mut func = one(i32, i32, divided(Opcode::UDiv, 7, Flags::NONE));
887 divisions(&mut func, Goal::Speed);
888 let want = [
889 Opcode::ZExt,
890 Opcode::Mul,
891 Opcode::LShr,
892 Opcode::Add,
893 Opcode::LShr,
894 Opcode::Trunc,
895 Opcode::Return,
896 ];
897 assert_eq!(opcodes(&func), want);
898 }
899
900 #[test]
901 fn a_signed_remainder_is_the_quotient_multiplied_back() {
902 let i32 = Type::int(32);
903 let mut func = one(i32, i32, divided(Opcode::SRem, 7, Flags::NONE));
904 divisions(&mut func, Goal::Speed);
905 let want = [
906 Opcode::SExt,
907 Opcode::Mul,
908 Opcode::AShr,
909 Opcode::Trunc,
910 Opcode::AShr,
911 Opcode::Sub,
912 Opcode::Mul,
913 Opcode::Sub,
914 Opcode::Return,
915 ];
916 assert_eq!(opcodes(&func), want);
917 }
918
919 #[test]
922 fn a_widened_unsigned_short_takes_the_short_number_and_no_correction() {
923 let (i16, i32) = (Type::int(16), Type::int(32));
924 let mut func = one(i16, i32, |build, x| {
925 let wide = build.unary(Opcode::ZExt, x, i32);
926 divided(Opcode::SDiv, 10, Flags::NONE)(build, wide)
927 });
928 divisions(&mut func, Goal::Speed);
929 let want =
930 [Opcode::ZExt, Opcode::ZExt, Opcode::Mul, Opcode::LShr, Opcode::Trunc, Opcode::Return];
931 assert_eq!(opcodes(&func), want);
932 }
933
934 #[test]
936 fn an_exact_division_is_a_shift_and_a_multiply() {
937 let i64 = Type::int(64);
938 let mut func = one(i64, i64, divided(Opcode::SDiv, 12, Flags::EXACT));
939 divisions(&mut func, Goal::Speed);
940 assert_eq!(opcodes(&func), [Opcode::AShr, Opcode::Mul, Opcode::Return]);
941 }
942
943 #[test]
944 fn size_a_variable_divisor_and_a_wide_dividend_keep_the_div() {
945 let i32 = Type::int(32);
946 let mut func = one(i32, i32, divided(Opcode::UDiv, 7, Flags::NONE));
947 divisions(&mut func, Goal::Size);
948 assert_eq!(opcodes(&func), [Opcode::UDiv, Opcode::Return]);
949
950 let mut func = one(i32, i32, |build, x| build.binary(Opcode::SDiv, x, x, Flags::NONE));
951 divisions(&mut func, Goal::Speed);
952 assert_eq!(opcodes(&func), [Opcode::SDiv, Opcode::Return]);
953
954 let i64 = Type::int(64);
955 let mut func = one(i64, i64, divided(Opcode::UDiv, 10, Flags::NONE));
956 divisions(&mut func, Goal::Speed);
957 assert_eq!(opcodes(&func), [Opcode::UDiv, Opcode::Return]);
958 }
959}