#![feature(f128)] #![allow(unused_macros)] use builtins_test::*; use compiler_builtins::int::sdiv::{__divmoddi4, __divmodsi4, __divmodti4}; use compiler_builtins::int::udiv::{__udivmoddi4, __udivmodsi4, __udivmodti4, u128_divide_sparc}; // Division algorithms have by far the nastiest and largest number of edge cases, and experience shows // that sometimes 100_000 iterations of the random fuzzer is needed. /// Creates intensive test functions for division functions of a certain size macro_rules! test { ( $n:expr, // the number of bits in a $iX or $uX $uX:ident, // unsigned integer that will be shifted $iX:ident, // signed version of $uX $test_name:ident, // name of the test function $unsigned_name:ident, // unsigned division function $signed_name:ident // signed division function ) => { #[test] fn $test_name() { fuzz_2(N, |lhs, rhs| { if rhs == 0 { return; } let mut rem: $uX = 0; let quo: $uX = $unsigned_name(lhs, rhs, Some(&mut rem)); if rhs <= rem || (lhs != rhs.wrapping_mul(quo).wrapping_add(rem)) { panic!( "unsigned division function failed with lhs:{} rhs:{} \ std:({}, {}) builtins:({}, {})", lhs, rhs, lhs.wrapping_div(rhs), lhs.wrapping_rem(rhs), quo, rem ); } // test the signed division function also let lhs = lhs as $iX; let rhs = rhs as $iX; let mut rem: $iX = 0; let quo: $iX = $signed_name(lhs, rhs, &mut rem); // We cannot just test that // `lhs == rhs.wrapping_mul(quo).wrapping_add(rem)`, but also // need to make sure the remainder isn't larger than the divisor // and has the correct sign. let incorrect_rem = if rem == 0 { false } else if rhs == $iX::MIN { // `rhs.wrapping_abs()` would overflow, so handle this case // separately. (lhs.is_negative() != rem.is_negative()) || (rem == $iX::MIN) } else { (lhs.is_negative() != rem.is_negative()) || (rhs.wrapping_abs() <= rem.wrapping_abs()) }; if incorrect_rem || lhs != rhs.wrapping_mul(quo).wrapping_add(rem) { panic!( "signed division function failed with lhs:{} rhs:{} \ std:({}, {}) builtins:({}, {})", lhs, rhs, lhs.wrapping_div(rhs), lhs.wrapping_rem(rhs), quo, rem ); } }); } }; } test!(32, u32, i32, div_rem_si4, __udivmodsi4, __divmodsi4); test!(64, u64, i64, div_rem_di4, __udivmoddi4, __divmoddi4); test!(128, u128, i128, div_rem_ti4, __udivmodti4, __divmodti4); #[test] fn divide_sparc() { fuzz_2(N, |lhs, rhs| { if rhs == 0 { return; } let mut rem: u128 = 0; let quo: u128 = u128_divide_sparc(lhs, rhs, &mut rem); if rhs <= rem || (lhs != rhs.wrapping_mul(quo).wrapping_add(rem)) { panic!( "u128_divide_sparc({}, {}): \ std:({}, {}), builtins:({}, {})", lhs, rhs, lhs.wrapping_div(rhs), lhs.wrapping_rem(rhs), quo, rem ); } }); } macro_rules! float { ($($f:ty, $fn:ident, $apfloat_ty:ident, $sys_available:meta);*;) => { $( #[test] fn $fn() { use compiler_builtins::float::{div::$fn, Float}; use core::ops::Div; fuzz_float_2(N, |x: $f, y: $f| { let quo0: $f = apfloat_fallback!($f, $apfloat_ty, $sys_available, Div::div, x, y); let quo1: $f = $fn(x, y); // ARM SIMD instructions always flush subnormals to zero if cfg!(target_arch = "arm") && ((Float::is_subnormal(quo0)) || Float::is_subnormal(quo1)) { return; } if !Float::eq_repr(quo0, quo1) { panic!( "{}({:?}, {:?}): std: {:?}, builtins: {:?}", stringify!($fn), x, y, quo0, quo1 ); } }); } )* }; } #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] mod float_div { use super::*; float! { f32, __divsf3, Single, all(); f64, __divdf3, Double, all(); } #[cfg(not(feature = "no-f16-f128"))] #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] float! { f128, __divtf3, Quad, // FIXME(llvm): there is a bug in LLVM rt. // See . not(any(feature = "no-sys-f128", all(target_arch = "aarch64", target_os = "linux"))); } #[cfg(not(feature = "no-f16-f128"))] #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] float! { f128, __divkf3, Quad, not(feature = "no-sys-f128"); } }