1use rucc_cost::Goal;
56use rucc_ir::{Def, Extra, Flags, Func, Imm, Inst, Opcode, Type, Value};
57
58use crate::expand::{ahead, ahead_const, becomes};
59
60pub fn divisions(func: &mut Func, goal: Goal) {
62 if matches!(goal, Goal::Size) {
63 return;
64 }
65 let found: Vec<Inst> =
66 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
67 for inst in found {
68 let Some(division) = division(func, inst) else { continue };
69 let Some(program) = program(division) else { continue };
70 write(func, inst, &program);
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum Range {
77 Unsigned(u32),
79 Signed(u32),
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub struct Division {
86 pub signed: bool,
88 pub remainder: bool,
90 pub exact: bool,
92 pub width: u32,
94 pub range: Range,
96 pub divisor: i128,
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub enum Step {
105 Const(i128, u32),
107 Op(Opcode, [usize; 2], u32),
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct Program {
115 pub width: u32,
117 pub steps: Vec<Step>,
119}
120
121const DIVIDEND: usize = 0;
123
124impl Program {
125 fn new(width: u32) -> Self {
126 Self { width, steps: Vec::new() }
127 }
128
129 fn push(&mut self, step: Step) -> usize {
130 self.steps.push(step);
131 self.steps.len()
132 }
133
134 fn constant(&mut self, value: i128, bits: u32) -> usize {
135 self.push(Step::Const(value, bits))
136 }
137
138 fn by(&mut self, opcode: Opcode, value: usize, constant: i128, bits: u32) -> usize {
140 let constant = self.constant(constant, bits);
141 self.push(Step::Op(opcode, [value, constant], bits))
142 }
143
144 fn op(&mut self, opcode: Opcode, lhs: usize, rhs: usize, bits: u32) -> usize {
145 self.push(Step::Op(opcode, [lhs, rhs], bits))
146 }
147
148 fn shifted(&mut self, opcode: Opcode, value: usize, by: u32) -> usize {
150 if by == 0 { value } else { self.by(opcode, value, i128::from(by), self.width) }
151 }
152
153 fn convert(&mut self, opcode: Opcode, value: usize, bits: u32) -> usize {
154 self.push(Step::Op(opcode, [value, value], bits))
155 }
156
157 fn widened(&mut self, extend: Opcode) -> usize {
159 if self.width == 64 { DIVIDEND } else { self.convert(extend, DIVIDEND, 64) }
160 }
161
162 fn narrowed(&mut self, value: usize) -> usize {
164 if self.width == 64 { value } else { self.convert(Opcode::Trunc, value, self.width) }
165 }
166
167 fn negated(&mut self, value: usize) -> usize {
169 let zero = self.constant(0, self.width);
170 self.op(Opcode::Sub, zero, value, self.width)
171 }
172
173 fn left_over(&mut self, quotient: usize, divisor: i128) -> usize {
175 let back = self.by(Opcode::Mul, quotient, divisor, self.width);
176 self.op(Opcode::Sub, DIVIDEND, back, self.width)
177 }
178}
179
180#[must_use]
182pub fn program(division: Division) -> Option<Program> {
183 let Division { signed, remainder, exact, width, range, divisor } = division;
184 if !matches!(width, 8 | 16 | 32 | 64) || matches!(divisor, 0 | 1) || (signed && divisor == -1) {
185 return None;
186 }
187 let mut program = Program::new(width);
188 if exact && !remainder {
189 exactly(&mut program, signed, divisor);
190 return Some(program);
191 }
192 let size = divisor.unsigned_abs();
193 match range {
194 Range::Unsigned(bits) => {
195 let quotient = unsigned(&mut program, size, bits)?;
196 if remainder {
197 if size.is_power_of_two() {
198 let mask = i128::try_from(size - 1).ok()?;
199 program.by(Opcode::And, DIVIDEND, mask, width);
200 } else {
201 program.left_over(quotient, i128::try_from(size).ok()?);
202 }
203 } else if divisor < 0 {
204 program.negated(quotient);
205 }
206 }
207 Range::Signed(_) if size.is_power_of_two() => biased(&mut program, divisor, remainder),
208 Range::Signed(bits) => {
209 let quotient = rounded(&mut program, divisor, bits)?;
210 if remainder {
211 program.left_over(quotient, divisor);
212 }
213 }
214 }
215 Some(program)
216}
217
218#[must_use]
230pub fn multiplier(divisor: u128, bits: u32, precision: u32) -> (u128, u32) {
231 assert!(divisor > 0, "a divisor of zero has no reciprocal");
232 let up = 128 - (divisor - 1).leading_zeros();
233 assert!(bits + up < 128, "a divisor and a width this can work with");
234 let mut shift = up;
235 let low = (1u128 << (bits + up)) / divisor;
236 let high = ((1u128 << (bits + up)) + (1u128 << (bits + up - precision))) / divisor;
237 let (mut low, mut high) = (low, high);
238 while shift > 0 && low / 2 < high / 2 {
239 low /= 2;
240 high /= 2;
241 shift -= 1;
242 }
243 (high, shift)
244}
245
246fn unsigned(program: &mut Program, divisor: u128, bits: u32) -> Option<usize> {
254 let width = program.width;
255 if bits >= 128 || divisor >= 1u128 << bits {
256 return None;
257 }
258 if divisor.is_power_of_two() {
259 let shift = i128::from(divisor.trailing_zeros());
260 return Some(program.by(Opcode::LShr, DIVIDEND, shift, width));
261 }
262 if bits > 32 {
263 return unsigned_high(program, divisor, bits);
264 }
265 let (magic, shift) = multiplier(divisor, bits, bits);
266 let wide = program.widened(Opcode::ZExt);
267 let shifted = if magic < 1u128 << bits || bits < 32 {
268 let product = program.by(Opcode::Mul, wide, i128::try_from(magic).ok()?, 64);
269 program.by(Opcode::LShr, product, i128::from(bits + shift), 64)
270 } else {
271 let magic = i128::try_from(magic - (1u128 << 32)).ok()?;
272 let product = program.by(Opcode::Mul, wide, magic, 64);
273 let high = program.by(Opcode::LShr, product, 32, 64);
274 let sum = program.op(Opcode::Add, high, wide, 64);
275 if shift == 0 { sum } else { program.by(Opcode::LShr, sum, i128::from(shift), 64) }
276 };
277 Some(program.narrowed(shifted))
278}
279
280fn unsigned_high(program: &mut Program, divisor: u128, bits: u32) -> Option<usize> {
290 if program.width != 64 || bits != 64 || divisor > 1u128 << 63 {
291 return None;
292 }
293 let (magic, shift) = multiplier(divisor, 64, 64);
294 if magic < 1u128 << 64 {
295 let high = program.by(Opcode::UMulHigh, DIVIDEND, signed_at(magic, 64), 64);
296 return Some(program.shifted(Opcode::LShr, high, shift));
297 }
298 let power = divisor.trailing_zeros();
299 if power > 0 {
300 let (magic, shift) = multiplier(divisor >> power, 64, 64 - power);
301 if magic < 1u128 << 64 {
302 let value = program.by(Opcode::LShr, DIVIDEND, i128::from(power), 64);
303 let high = program.by(Opcode::UMulHigh, value, signed_at(magic, 64), 64);
304 return Some(program.shifted(Opcode::LShr, high, shift));
305 }
306 }
307 let low_bits = signed_at(magic - (1u128 << 64), 64);
308 let high = program.by(Opcode::UMulHigh, DIVIDEND, low_bits, 64);
309 let difference = program.op(Opcode::Sub, DIVIDEND, high, 64);
310 let half = program.by(Opcode::LShr, difference, 1, 64);
311 let sum = program.op(Opcode::Add, half, high, 64);
312 Some(program.shifted(Opcode::LShr, sum, shift.checked_sub(1)?))
313}
314
315fn rounded(program: &mut Program, divisor: i128, bits: u32) -> Option<usize> {
323 let width = program.width;
324 let size = divisor.unsigned_abs();
325 if size > 1u128 << (bits - 1) {
326 return None;
327 }
328 let low = if bits > 32 {
329 signed_high(program, size, bits)?
330 } else {
331 let (magic, shift) = multiplier(size, bits, bits - 1);
332 let wide = program.widened(Opcode::SExt);
333 let product = program.by(Opcode::Mul, wide, i128::try_from(magic).ok()?, 64);
334 let shifted = program.by(Opcode::AShr, product, i128::from(bits + shift), 64);
335 program.narrowed(shifted)
336 };
337 let sign = program.by(Opcode::AShr, DIVIDEND, i128::from(width - 1), width);
338 Some(if divisor > 0 {
339 program.op(Opcode::Sub, low, sign, width)
340 } else {
341 program.op(Opcode::Sub, sign, low, width)
342 })
343}
344
345fn signed_high(program: &mut Program, size: u128, bits: u32) -> Option<usize> {
351 if program.width != 64 || bits != 64 {
352 return None;
353 }
354 let (magic, shift) = multiplier(size, 64, 63);
355 let high = program.by(Opcode::SMulHigh, DIVIDEND, signed_at(magic, 64), 64);
356 let high = if magic < 1u128 << 63 { high } else { program.op(Opcode::Add, high, DIVIDEND, 64) };
357 Some(program.shifted(Opcode::AShr, high, shift))
358}
359
360fn biased(program: &mut Program, divisor: i128, remainder: bool) {
367 let width = program.width;
368 let size = divisor.unsigned_abs();
369 let power = size.trailing_zeros();
370 let bias = if power == 1 {
371 program.by(Opcode::LShr, DIVIDEND, i128::from(width - 1), width)
372 } else {
373 let sign = program.by(Opcode::AShr, DIVIDEND, i128::from(width - 1), width);
374 program.by(Opcode::LShr, sign, i128::from(width - power), width)
375 };
376 let sum = program.op(Opcode::Add, DIVIDEND, bias, width);
377 if remainder {
378 let mask = (1i128 << power) - 1;
379 let low = program.by(Opcode::And, sum, mask, width);
380 program.op(Opcode::Sub, low, bias, width);
381 return;
382 }
383 let quotient = program.by(Opcode::AShr, sum, i128::from(power), width);
384 if divisor < 0 {
385 program.negated(quotient);
386 }
387}
388
389fn exactly(program: &mut Program, signed: bool, divisor: i128) {
398 let width = program.width;
399 let power = divisor.trailing_zeros();
400 let mut value = DIVIDEND;
401 if power > 0 {
402 let shift = if signed { Opcode::AShr } else { Opcode::LShr };
403 value = program.by(shift, DIVIDEND, i128::from(power), width);
404 }
405 let odd = divisor >> power;
406 if odd != 1 {
407 program.by(Opcode::Mul, value, signed_at(inverse(odd, width), width), width);
408 }
409}
410
411fn inverse(odd: i128, width: u32) -> u128 {
416 let odd = odd as u128;
417 let mut inverse = odd;
418 for _ in 0..6 {
419 inverse = inverse.wrapping_mul(2u128.wrapping_sub(odd.wrapping_mul(inverse)));
420 }
421 inverse & mask(width)
422}
423
424fn signed_at(bits: u128, width: u32) -> i128 {
426 let spare = 128 - width;
427 ((bits << spare) as i128) >> spare
428}
429
430fn mask(width: u32) -> u128 {
432 if width >= 128 { u128::MAX } else { (1u128 << width) - 1 }
433}
434
435fn division(func: &Func, inst: Inst) -> Option<Division> {
437 let data = &func[inst];
438 let (signed, remainder) = match data.opcode {
439 Opcode::SDiv => (true, false),
440 Opcode::UDiv => (false, false),
441 Opcode::SRem => (true, true),
442 Opcode::URem => (false, true),
443 _ => return None,
444 };
445 let &[dividend, by] = &func[data.args] else { return None };
446 let ty = func[dividend].ty;
447 if !ty.is_int() || ty.lanes() != 1 {
448 return None;
449 }
450 let imm = constant(func, by)?;
451 let divisor = if signed { imm.signed(ty) } else { i128::try_from(imm.unsigned()).ok()? };
452 Some(Division {
453 signed,
454 remainder,
455 exact: data.flags.contains(Flags::EXACT),
456 width: ty.bits(),
457 range: range(func, dividend, ty.bits(), signed),
458 divisor,
459 })
460}
461
462fn constant(func: &Func, value: Value) -> Option<Imm> {
464 let Def::Result { inst, .. } = func[value].def else { return None };
465 if func[inst].opcode != Opcode::IConst {
466 return None;
467 }
468 let Extra::Imm(imm) = func[inst].extra else { return None };
469 Some(func[imm])
470}
471
472fn range(func: &Func, value: Value, width: u32, signed: bool) -> Range {
478 let whole = if signed { Range::Signed(width) } else { Range::Unsigned(width) };
479 let Def::Result { inst, .. } = func[value].def else { return whole };
480 let Some(&from) = func[func[inst].args].first() else { return whole };
481 let bits = func[from].ty.bits();
482 if bits == 0 || bits >= width {
483 return whole;
484 }
485 match func[inst].opcode {
486 Opcode::ZExt => Range::Unsigned(bits),
487 Opcode::SExt if signed => Range::Signed(bits),
488 _ => whole,
489 }
490}
491
492fn write(func: &mut Func, inst: Inst, program: &Program) {
494 let Some((&Step::Op(opcode, args, _), before)) = program.steps.split_last() else { return };
495 let mut values = vec![func[func[inst].args][0]];
496 for &step in before {
497 let value = match step {
498 Step::Const(value, bits) => {
499 let ty = Type::int(bits);
500 ahead_const(func, inst, Imm::int(value, ty), ty)
501 }
502 Step::Op(opcode, args, bits) => {
503 let operands: Vec<Value> =
504 args[..arity(opcode)].iter().map(|&at| values[at]).collect();
505 ahead(func, inst, opcode, &operands, Type::int(bits))
506 }
507 };
508 values.push(value);
509 }
510 let operands: Vec<Value> = args[..arity(opcode)].iter().map(|&at| values[at]).collect();
511 becomes(func, inst, opcode, &operands);
512}
513
514fn arity(opcode: Opcode) -> usize {
516 match opcode {
517 Opcode::Trunc | Opcode::ZExt | Opcode::SExt => 1,
518 _ => 2,
519 }
520}
521
522#[cfg(test)]
523mod tests {
524 use rucc_base::Interner;
525 use rucc_cost::Goal;
526 use rucc_ir::{Builder, Flags, Func, Opcode, Signature, Type, Value};
527
528 use super::{Division, Program, Range, Step, divisions, mask, multiplier, program, signed_at};
529
530 fn run(program: &Program, dividend: u128, values: &mut Vec<(u128, u32)>) -> u128 {
533 values.clear();
534 values.push((dividend & mask(program.width), program.width));
535 for &step in &program.steps {
536 let value = match step {
537 Step::Const(value, bits) => (value as u128 & mask(bits), bits),
538 Step::Op(opcode, [lhs, rhs], bits) => {
539 let (a, from) = values[lhs];
540 let (b, other) = values[rhs];
541 let answer = match opcode {
542 Opcode::ZExt | Opcode::Trunc | Opcode::SExt => {
543 let right =
544 if opcode == Opcode::Trunc { from > bits } else { from < bits };
545 assert!(right, "{opcode:?} from {from} bits to {bits}");
546 if opcode == Opcode::SExt { signed_at(a, from) as u128 } else { a }
547 }
548 _ => {
549 assert_eq!((from, other), (bits, bits), "{opcode:?} at {bits} bits");
550 match opcode {
551 Opcode::Add => a.wrapping_add(b),
552 Opcode::Sub => a.wrapping_sub(b),
553 Opcode::Mul => a.wrapping_mul(b),
554 Opcode::And => a & b,
555 Opcode::UMulHigh => {
556 assert_eq!(bits, 64, "a high multiply at {bits} bits");
557 (a * b) >> 64
558 }
559 Opcode::SMulHigh => {
560 assert_eq!(bits, 64, "a high multiply at {bits} bits");
561 ((signed_at(a, 64) * signed_at(b, 64)) >> 64) as u128
562 }
563 Opcode::LShr | Opcode::AShr => {
564 assert!(b < u128::from(bits), "a shift by {b} at {bits} bits");
565 if opcode == Opcode::LShr {
566 a >> b
567 } else {
568 (signed_at(a, bits) >> b) as u128
569 }
570 }
571 _ => panic!("{opcode:?} is not something a program writes"),
572 }
573 }
574 };
575 (answer & mask(bits), bits)
576 }
577 };
578 values.push(value);
579 }
580 let (answer, bits) = *values.last().expect("a program has steps");
581 assert_eq!(bits, program.width, "the answer is at the width of the division");
582 answer
583 }
584
585 fn truth(division: &Division, dividend: u128) -> u128 {
587 let answer = if division.signed {
588 let x = signed_at(dividend, division.width);
589 if division.remainder { x % division.divisor } else { x / division.divisor }
590 } else {
591 let d = division.divisor as u128;
592 (if division.remainder { dividend % d } else { dividend / d }) as i128
593 };
594 answer as u128 & mask(division.width)
595 }
596
597 fn ends(range: Range) -> (i128, i128) {
599 match range {
600 Range::Unsigned(bits) => (0, (1 << bits) - 1),
601 Range::Signed(bits) => (-(1 << (bits - 1)), (1 << (bits - 1)) - 1),
602 }
603 }
604
605 fn division(
606 signed: bool,
607 remainder: bool,
608 width: u32,
609 range: Range,
610 divisor: i128,
611 ) -> Division {
612 Division { signed, remainder, exact: false, width, range, divisor }
613 }
614
615 fn left(division: &Division) -> bool {
618 let size = division.divisor.unsigned_abs();
619 let (low, high) = ends(division.range);
620 let most = low.unsigned_abs().max(high.unsigned_abs());
621 let bits = match division.range {
622 Range::Unsigned(bits) | Range::Signed(bits) => bits,
623 };
624 let signed_range = matches!(division.range, Range::Signed(_));
625 matches!(division.divisor, 0 | 1)
626 || (division.signed && division.divisor == -1)
627 || (!signed_range && size > most)
628 || (signed_range && !size.is_power_of_two() && size > most)
629 || (!signed_range && !size.is_power_of_two() && bits > 32 && size > 1 << 63)
630 }
631
632 fn check(
634 division: Division,
635 dividends: impl IntoIterator<Item = i128>,
636 values: &mut Vec<(u128, u32)>,
637 ) {
638 let Some(program) = program(division) else {
639 assert!(left(&division), "{division:?} was left as a div");
640 return;
641 };
642 assert!(!left(&division), "{division:?} was rewritten");
643 for x in dividends {
644 let x = x as u128 & mask(division.width);
645 assert_eq!(run(&program, x, values), truth(&division, x), "{division:?} of {x:#x}");
646 }
647 }
648
649 fn edges(range: Range, divisor: i128) -> Vec<i128> {
656 let (low, high) = ends(range);
657 let size = divisor.abs().max(1);
658 let mut all = vec![low, high, -1, 0, 1];
659 let mut at = 0;
660 while at <= high + 1 || -at >= low - 1 {
661 all.extend([at - 1, at, at + 1, -at - 1, -at, -at + 1]);
662 at += size;
663 }
664 all.retain(|&x| (low..=high).contains(&x));
665 all
666 }
667
668 fn random(state: &mut u64) -> u64 {
670 *state ^= *state << 13;
671 *state ^= *state >> 7;
672 *state ^= *state << 17;
673 *state
674 }
675
676 fn both() -> [(bool, bool); 4] {
677 [(false, false), (false, true), (true, false), (true, true)]
678 }
679
680 #[test]
681 fn the_magic_numbers_are_the_ones_gcc_writes() {
682 assert_eq!(multiplier(100, 32, 32), (1_374_389_535, 5));
684 assert_eq!(multiplier(7, 32, 32), ((1 << 32) + 613_566_757, 3));
686 assert_eq!(multiplier(7, 32, 31), (2_454_267_027, 2));
688 assert_eq!(multiplier(3, 32, 31), (1_431_655_766, 0));
690 assert_eq!(multiplier(10, 16, 16), (52_429, 3));
692 assert_eq!(multiplier(10, 64, 64), (0xcccc_cccc_cccc_cccd, 3));
694 assert_eq!(multiplier(7, 64, 64), ((1 << 64) + 0x2492_4924_9249_2493, 3));
697 assert_eq!(multiplier(10, 64, 63), (0x6666_6666_6666_6667, 2));
699 assert_eq!(multiplier(7, 64, 63), (0x4924_9249_2492_4925, 1));
701 }
702
703 #[test]
706 fn every_eight_bit_division_by_every_divisor_is_right() {
707 let mut values = Vec::new();
708 for (signed, remainder) in both() {
709 for width in [8, 16, 32, 64] {
710 let mut ranges = vec![Range::Unsigned(8)];
711 if signed {
712 ranges.push(Range::Signed(8));
713 }
714 for range in ranges {
715 if width == 8
716 && range != (if signed { Range::Signed(8) } else { Range::Unsigned(8) })
717 {
718 continue;
719 }
720 let (low, high) = ends(range);
721 let divisors: Vec<i128> =
722 if signed { (-300..=300).collect() } else { (0..=300).collect() };
723 for divisor in divisors {
724 let divisor = match (width, signed) {
725 (8, true) => signed_at(divisor as u128 & 0xff, 8),
726 (8, false) => divisor & 0xff,
727 _ => divisor,
728 };
729 check(
730 division(signed, remainder, width, range, divisor),
731 low..=high,
732 &mut values,
733 );
734 }
735 }
736 }
737 }
738 }
739
740 #[test]
743 fn every_sixteen_bit_quotient_by_every_divisor_is_right() {
744 let mut values = Vec::new();
745 for signed in [false, true] {
746 for width in [16, 32] {
747 let range = if signed { Range::Signed(16) } else { Range::Unsigned(16) };
748 let divisors: Vec<i128> =
749 if signed { (-32_768..=32_767).collect() } else { (0..=65_535).collect() };
750 for divisor in divisors {
751 check(
752 division(signed, false, width, range, divisor),
753 edges(range, divisor),
754 &mut values,
755 );
756 }
757 }
758 }
759 let range = Range::Unsigned(16);
761 for divisor in -70_000..=70_000 {
762 check(division(true, false, 32, range, divisor), edges(range, divisor), &mut values);
763 }
764 }
765
766 #[test]
769 fn every_sixteen_bit_remainder_by_a_power_of_two_is_right() {
770 let mut values = Vec::new();
771 for (signed, remainder) in both() {
772 let range = if signed { Range::Signed(16) } else { Range::Unsigned(16) };
773 let (low, high) = ends(range);
774 for power in 1..16 {
775 for divisor in [1i128 << power, -(1i128 << power)] {
776 if !signed && divisor < 0 {
777 continue;
778 }
779 check(division(signed, remainder, 16, range, divisor), low..=high, &mut values);
780 }
781 }
782 }
783 }
784
785 #[test]
789 fn thirty_two_bit_divisions_are_right_at_the_edges_and_on_a_sample() {
790 let mut values = Vec::new();
791 let mut state = 0x9e37_79b9_7f4a_7c15;
792 let mut divisors: Vec<i128> = (-2_000..=2_000).collect();
793 for power in 1..=32 {
794 let at = 1i128 << power;
795 divisors.extend([at - 1, at, at + 1, -at + 1, -at, -at - 1]);
796 }
797 for _ in 0..2_000 {
798 divisors.push(i128::from(random(&mut state) as u32));
799 divisors.push(i128::from(random(&mut state) as i32));
800 }
801 for (signed, remainder) in both() {
802 for (width, range) in [
803 (32, if signed { Range::Signed(32) } else { Range::Unsigned(32) }),
804 (64, Range::Unsigned(32)),
805 (64, if signed { Range::Signed(32) } else { Range::Unsigned(32) }),
806 ] {
807 let (low, high) = ends(range);
808 for &divisor in &divisors {
809 let divisor = if width == 32 {
810 let bits = divisor as u128 & mask(32);
811 if signed { signed_at(bits, 32) } else { bits as i128 }
812 } else {
813 divisor
814 };
815 if !signed && divisor < 0 {
816 continue;
817 }
818 let size = divisor.abs().max(1);
819 let mut dividends = vec![low, low + 1, -1, 0, 1, 2, high - 1, high];
820 for end in [low, high] {
821 let near = end / size * size;
822 dividends.extend([near - 1, near, near + 1, near - size, near + size]);
823 }
824 for _ in 0..64 {
825 let x = random(&mut state) as u128 & mask(32);
826 dividends.push(if matches!(range, Range::Signed(_)) {
827 signed_at(x, 32)
828 } else {
829 x as i128
830 });
831 }
832 dividends.retain(|x| (low..=high).contains(x));
833 check(
834 division(signed, remainder, width, range, divisor),
835 dividends,
836 &mut values,
837 );
838 }
839 }
840 }
841 }
842
843 #[test]
848 fn sixty_four_bit_divisions_are_right_at_the_edges_and_on_a_sample() {
849 let mut values = Vec::new();
850 let mut state = 0x2545_f491_4f6c_dd1d;
851 let mut divisors: Vec<i128> = (-2_000..=2_000).collect();
852 for power in 1..=64 {
853 let at = 1i128 << power;
854 divisors.extend([at - 1, at, at + 1, -at + 1, -at, -at - 1]);
855 }
856 divisors.extend([1_000_000_007, 10_000_000_000_000_000_000, 0xcccc_cccc_cccc_cccd]);
857 for _ in 0..2_000 {
858 divisors.push(i128::from(random(&mut state)));
859 divisors.push(i128::from(random(&mut state) >> (random(&mut state) % 64)));
860 }
861 for (signed, remainder) in both() {
862 let range = if signed { Range::Signed(64) } else { Range::Unsigned(64) };
863 let (low, high) = ends(range);
864 for &divisor in &divisors {
865 let bits = divisor as u128 & mask(64);
866 let divisor = if signed { signed_at(bits, 64) } else { bits as i128 };
867 let size = divisor.abs().max(1);
868 let mut dividends = vec![low, low + 1, -1, 0, 1, 2, high - 1, high];
869 for end in [low, high] {
870 let near = end / size * size;
871 dividends.extend([near - 1, near, near + 1, near - size, near + size]);
872 }
873 for _ in 0..64 {
874 let x = u128::from(random(&mut state));
875 dividends.push(if signed { signed_at(x, 64) } else { x as i128 });
876 }
877 dividends.retain(|x| (low..=high).contains(x));
878 check(division(signed, remainder, 64, range, divisor), dividends, &mut values);
879 }
880 }
881 }
882
883 #[test]
886 fn an_exact_division_is_right_over_every_multiple_it_is_given() {
887 let mut values = Vec::new();
888 let mut state = 0x1234_5678_9abc_def1;
889 for signed in [false, true] {
890 for width in [8, 16, 32, 64] {
891 let range = if signed { Range::Signed(width) } else { Range::Unsigned(width) };
892 let (low, high) = ends(range);
893 for divisor in (-100i128..=100).chain([12, 24, 40, 56, 1 << 20, 3 << 30]) {
894 if (!signed && divisor < 0) || divisor < low || divisor > high {
895 continue;
896 }
897 let exact =
898 Division { exact: true, ..division(signed, false, width, range, divisor) };
899 let Some(program) = program(exact) else {
900 assert!(
901 matches!(divisor, 0 | 1) || (signed && divisor == -1),
902 "{exact:?} was left"
903 );
904 continue;
905 };
906 let size = divisor.abs();
907 let mut dividends: Vec<i128> = if width <= 16 {
908 (low / size..=high / size).map(|times| times * divisor).collect()
909 } else {
910 (0..256)
911 .map(|_| {
912 let times = signed_at(u128::from(random(&mut state)), 64)
913 % (high / size).max(1);
914 if signed { times * divisor } else { times.abs() * divisor }
915 })
916 .collect()
917 };
918 dividends.extend([0, divisor, high / size * divisor]);
919 dividends.retain(|x| (low..=high).contains(x));
920 for x in dividends {
921 let x = x as u128 & mask(width);
922 assert_eq!(
923 run(&program, x, &mut values),
924 truth(&exact, x),
925 "{exact:?} of {x:#x}"
926 );
927 }
928 }
929 }
930 }
931 }
932
933 fn one(param: Type, ret: Type, body: impl FnOnce(&mut Builder<'_>, Value) -> Value) -> Func {
935 let mut names = Interner::new();
936 let mut func = Func::new(
937 names.intern("f"),
938 Signature::new().with_params(&[param]).with_returns(&[ret]),
939 );
940 let entry = func.create_block();
941 let x = func.append_param(entry, param);
942 let mut build = Builder::new(&mut func, entry);
943 let answer = body(&mut build, x);
944 build.ret(&[answer]);
945 func
946 }
947
948 fn opcodes(func: &Func) -> Vec<Opcode> {
950 let entry = func.blocks().next().expect("an entry");
951 func.insts(entry).map(|inst| func[inst].opcode).filter(|&op| op != Opcode::IConst).collect()
952 }
953
954 fn divided(
955 opcode: Opcode,
956 divisor: i128,
957 flags: Flags,
958 ) -> impl FnOnce(&mut Builder<'_>, Value) -> Value {
959 move |build, x| {
960 let ty = build.func()[x].ty;
961 let by = build.iconst(ty, divisor);
962 build.binary(opcode, x, by, flags)
963 }
964 }
965
966 #[test]
967 fn an_unsigned_division_by_seven_is_a_multiply_an_add_and_two_shifts() {
968 let i32 = Type::int(32);
969 let mut func = one(i32, i32, divided(Opcode::UDiv, 7, Flags::NONE));
970 divisions(&mut func, Goal::Speed);
971 let want = [
972 Opcode::ZExt,
973 Opcode::Mul,
974 Opcode::LShr,
975 Opcode::Add,
976 Opcode::LShr,
977 Opcode::Trunc,
978 Opcode::Return,
979 ];
980 assert_eq!(opcodes(&func), want);
981 }
982
983 #[test]
984 fn a_signed_remainder_is_the_quotient_multiplied_back() {
985 let i32 = Type::int(32);
986 let mut func = one(i32, i32, divided(Opcode::SRem, 7, Flags::NONE));
987 divisions(&mut func, Goal::Speed);
988 let want = [
989 Opcode::SExt,
990 Opcode::Mul,
991 Opcode::AShr,
992 Opcode::Trunc,
993 Opcode::AShr,
994 Opcode::Sub,
995 Opcode::Mul,
996 Opcode::Sub,
997 Opcode::Return,
998 ];
999 assert_eq!(opcodes(&func), want);
1000 }
1001
1002 #[test]
1005 fn a_widened_unsigned_short_takes_the_short_number_and_no_correction() {
1006 let (i16, i32) = (Type::int(16), Type::int(32));
1007 let mut func = one(i16, i32, |build, x| {
1008 let wide = build.unary(Opcode::ZExt, x, i32);
1009 divided(Opcode::SDiv, 10, Flags::NONE)(build, wide)
1010 });
1011 divisions(&mut func, Goal::Speed);
1012 let want =
1013 [Opcode::ZExt, Opcode::ZExt, Opcode::Mul, Opcode::LShr, Opcode::Trunc, Opcode::Return];
1014 assert_eq!(opcodes(&func), want);
1015 }
1016
1017 #[test]
1019 fn an_exact_division_is_a_shift_and_a_multiply() {
1020 let i64 = Type::int(64);
1021 let mut func = one(i64, i64, divided(Opcode::SDiv, 12, Flags::EXACT));
1022 divisions(&mut func, Goal::Speed);
1023 assert_eq!(opcodes(&func), [Opcode::AShr, Opcode::Mul, Opcode::Return]);
1024 }
1025
1026 #[test]
1028 fn a_sixty_four_bit_unsigned_division_is_a_high_multiply_and_a_shift() {
1029 let i64 = Type::int(64);
1030 let mut func = one(i64, i64, divided(Opcode::UDiv, 10, Flags::NONE));
1031 divisions(&mut func, Goal::Speed);
1032 assert_eq!(opcodes(&func), [Opcode::UMulHigh, Opcode::LShr, Opcode::Return]);
1033 }
1034
1035 #[test]
1037 fn a_sixty_four_bit_unsigned_division_by_seven_adds_the_dividend_back() {
1038 let i64 = Type::int(64);
1039 let mut func = one(i64, i64, divided(Opcode::UDiv, 7, Flags::NONE));
1040 divisions(&mut func, Goal::Speed);
1041 let want = [
1042 Opcode::UMulHigh,
1043 Opcode::Sub,
1044 Opcode::LShr,
1045 Opcode::Add,
1046 Opcode::LShr,
1047 Opcode::Return,
1048 ];
1049 assert_eq!(opcodes(&func), want);
1050 }
1051
1052 #[test]
1055 fn a_sixty_four_bit_unsigned_division_by_an_even_number_shifts_first() {
1056 let i64 = Type::int(64);
1057 let mut func = one(i64, i64, divided(Opcode::UDiv, 14, Flags::NONE));
1058 divisions(&mut func, Goal::Speed);
1059 let want = [Opcode::LShr, Opcode::UMulHigh, Opcode::LShr, Opcode::Return];
1060 assert_eq!(opcodes(&func), want);
1061 }
1062
1063 #[test]
1065 fn a_sixty_four_bit_signed_division_is_a_high_multiply_and_the_sign_taken_off() {
1066 let i64 = Type::int(64);
1067 let mut func = one(i64, i64, divided(Opcode::SDiv, 10, Flags::NONE));
1068 divisions(&mut func, Goal::Speed);
1069 let want = [Opcode::SMulHigh, Opcode::AShr, Opcode::AShr, Opcode::Sub, Opcode::Return];
1070 assert_eq!(opcodes(&func), want);
1071 }
1072
1073 #[test]
1074 fn size_a_variable_divisor_and_a_huge_divisor_keep_the_div() {
1075 let i32 = Type::int(32);
1076 let mut func = one(i32, i32, divided(Opcode::UDiv, 7, Flags::NONE));
1077 divisions(&mut func, Goal::Size);
1078 assert_eq!(opcodes(&func), [Opcode::UDiv, Opcode::Return]);
1079
1080 let mut func = one(i32, i32, |build, x| build.binary(Opcode::SDiv, x, x, Flags::NONE));
1081 divisions(&mut func, Goal::Speed);
1082 assert_eq!(opcodes(&func), [Opcode::SDiv, Opcode::Return]);
1083
1084 let i64 = Type::int(64);
1086 let huge = i128::from(i64::MIN) + 1;
1087 let mut func = one(i64, i64, divided(Opcode::UDiv, huge, Flags::NONE));
1088 divisions(&mut func, Goal::Speed);
1089 assert_eq!(opcodes(&func), [Opcode::UDiv, Opcode::Return]);
1090 }
1091}