div_rem.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. use rand_xoshiro::rand_core::{RngCore, SeedableRng};
  2. use rand_xoshiro::Xoshiro128StarStar;
  3. use compiler_builtins::int::sdiv::{__divmoddi4, __divmodsi4, __divti3, __modti3};
  4. use compiler_builtins::int::udiv::{__udivmoddi4, __udivmodsi4, __udivmodti4};
  5. // because `__divmodti4` does not exist, we synthesize it
  6. fn __divmodti4(a: i128, b: i128, rem: &mut i128) -> i128 {
  7. *rem = __modti3(a, b);
  8. __divti3(a, b)
  9. }
  10. /// Creates intensive test functions for division functions of a certain size
  11. macro_rules! test {
  12. (
  13. $n:expr, // the number of bits in a $iX or $uX
  14. $uX:ident, // unsigned integer that will be shifted
  15. $iX:ident, // signed version of $uX
  16. $test_name:ident, // name of the test function
  17. $unsigned_name:ident, // unsigned division function
  18. $signed_name:ident // signed division function
  19. ) => {
  20. #[test]
  21. fn $test_name() {
  22. fn assert_invariants(lhs: $uX, rhs: $uX) {
  23. let rem: &mut $uX = &mut 0;
  24. let quo: $uX = $unsigned_name(lhs, rhs, Some(rem));
  25. let rem = *rem;
  26. if rhs <= rem || (lhs != rhs.wrapping_mul(quo).wrapping_add(rem)) {
  27. panic!(
  28. "unsigned division function failed with lhs:{} rhs:{} \
  29. expected:({}, {}) found:({}, {})",
  30. lhs,
  31. rhs,
  32. lhs.wrapping_div(rhs),
  33. lhs.wrapping_rem(rhs),
  34. quo,
  35. rem
  36. );
  37. }
  38. // test the signed division function also
  39. let lhs = lhs as $iX;
  40. let rhs = rhs as $iX;
  41. let mut rem: $iX = 0;
  42. let quo: $iX = $signed_name(lhs, rhs, &mut rem);
  43. // We cannot just test that
  44. // `lhs == rhs.wrapping_mul(quo).wrapping_add(rem)`, but also
  45. // need to make sure the remainder isn't larger than the divisor
  46. // and has the correct sign.
  47. let incorrect_rem = if rem == 0 {
  48. false
  49. } else if rhs == $iX::MIN {
  50. // `rhs.wrapping_abs()` would overflow, so handle this case
  51. // separately.
  52. (lhs.is_negative() != rem.is_negative()) || (rem == $iX::MIN)
  53. } else {
  54. (lhs.is_negative() != rem.is_negative())
  55. || (rhs.wrapping_abs() <= rem.wrapping_abs())
  56. };
  57. if incorrect_rem || lhs != rhs.wrapping_mul(quo).wrapping_add(rem) {
  58. panic!(
  59. "signed division function failed with lhs:{} rhs:{} \
  60. expected:({}, {}) found:({}, {})",
  61. lhs,
  62. rhs,
  63. lhs.wrapping_div(rhs),
  64. lhs.wrapping_rem(rhs),
  65. quo,
  66. rem
  67. );
  68. }
  69. }
  70. // Specially designed random fuzzer
  71. let mut rng = Xoshiro128StarStar::seed_from_u64(0);
  72. let mut lhs: $uX = 0;
  73. let mut rhs: $uX = 0;
  74. // all ones constant
  75. let ones: $uX = !0;
  76. // Alternating ones and zeros (e.x. 0b1010101010101010). This catches second-order
  77. // problems that might occur for algorithms with two modes of operation (potentially
  78. // there is some invariant that can be broken for large `duo` and maintained via
  79. // alternating between modes, breaking the algorithm when it reaches the end).
  80. let mut alt_ones: $uX = 1;
  81. for _ in 0..($n / 2) {
  82. alt_ones <<= 2;
  83. alt_ones |= 1;
  84. }
  85. // creates a mask for indexing the bits of the type
  86. let bit_indexing_mask = $n - 1;
  87. for _ in 0..1_000_000 {
  88. // Randomly OR, AND, and XOR randomly sized and shifted continuous strings of
  89. // ones with `lhs` and `rhs`. This results in excellent fuzzing entropy such as:
  90. // lhs:10101010111101000000000100101010 rhs: 1010101010000000000000001000001
  91. // lhs:10101010111101000000000101001010 rhs: 1010101010101010101010100010100
  92. // lhs:10101010111101000000000101001010 rhs:11101010110101010101010100001110
  93. // lhs:10101010000000000000000001001010 rhs:10100010100000000000000000001010
  94. // lhs:10101010000000000000000001001010 rhs: 10101010101010101000
  95. // lhs:10101010000000000000000001100000 rhs:11111111111101010101010101001111
  96. // lhs:10101010000000101010101011000000 rhs:11111111111101010101010100000111
  97. // lhs:10101010101010101010101011101010 rhs: 1010100000000000000
  98. // lhs:11111111110101101010101011010111 rhs: 1010100000000000000
  99. // The msb is set half of the time by the fuzzer, but `assert_invariants` tests
  100. // both the signed and unsigned functions.
  101. let r0: u32 = bit_indexing_mask & rng.next_u32();
  102. let r1: u32 = bit_indexing_mask & rng.next_u32();
  103. let mask = ones.wrapping_shr(r0).rotate_left(r1);
  104. match rng.next_u32() % 8 {
  105. 0 => lhs |= mask,
  106. 1 => lhs &= mask,
  107. // both 2 and 3 to make XORs as common as ORs and ANDs combined, otherwise
  108. // the entropy gets destroyed too often
  109. 2 | 3 => lhs ^= mask,
  110. 4 => rhs |= mask,
  111. 5 => rhs &= mask,
  112. _ => rhs ^= mask,
  113. }
  114. // do the same for alternating ones and zeros
  115. let r0: u32 = bit_indexing_mask & rng.next_u32();
  116. let r1: u32 = bit_indexing_mask & rng.next_u32();
  117. let mask = alt_ones.wrapping_shr(r0).rotate_left(r1);
  118. match rng.next_u32() % 8 {
  119. 0 => lhs |= mask,
  120. 1 => lhs &= mask,
  121. // both 2 and 3 to make XORs as common as ORs and ANDs combined, otherwise
  122. // the entropy gets destroyed too often
  123. 2 | 3 => lhs ^= mask,
  124. 4 => rhs |= mask,
  125. 5 => rhs &= mask,
  126. _ => rhs ^= mask,
  127. }
  128. if rhs != 0 {
  129. assert_invariants(lhs, rhs);
  130. }
  131. }
  132. }
  133. };
  134. }
  135. test!(32, u32, i32, div_rem_si4, __udivmodsi4, __divmodsi4);
  136. test!(64, u64, i64, div_rem_di4, __udivmoddi4, __divmoddi4);
  137. test!(128, u128, i128, div_rem_ti4, __udivmodti4, __divmodti4);