diff --git a/src/lib.rs b/src/lib.rs index 581189f..527f444 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,8 +18,6 @@ pub mod arm; #[cfg(test)] mod test; -use core::mem; - /// Trait for some basic operations on integers trait Int { fn bits() -> usize; @@ -27,16 +25,24 @@ trait Int { // TODO: Once i128/u128 support lands, we'll want to add impls for those as well impl Int for u32 { - fn bits() -> usize { 32 } + fn bits() -> usize { + 32 + } } impl Int for i32 { - fn bits() -> usize { 32 } + fn bits() -> usize { + 32 + } } impl Int for u64 { - fn bits() -> usize { 64 } + fn bits() -> usize { + 64 + } } impl Int for i64 { - fn bits() -> usize { 64 } + fn bits() -> usize { + 64 + } } /// Trait to convert an integer to/from smaller parts @@ -99,131 +105,124 @@ absv_i2!(__absvdi2: i64); // TODO(rust-lang/35118)? // absv_i2!(__absvti2, i128); +/// Return `n / d` and `*rem = n % d` #[no_mangle] -pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: Option<&mut u64>) -> u64 { +pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { + use core::ops::{Index, IndexMut, RangeFull}; + #[cfg(target_endian = "little")] #[repr(C)] - #[derive(Debug)] - struct words { + struct U64 { low: u32, high: u32, } #[cfg(target_endian = "big")] #[repr(C)] - #[derive(Debug)] - struct words { + struct U64 { high: u32, low: u32, } - impl words { - fn all(&mut self) -> &mut u64 { - unsafe { mem::transmute(self) } - } + impl Index for U64 { + type Output = u64; - fn u64(&self) -> u64 { - unsafe { *(self as *const _ as *const u64) } + fn index(&self, _: RangeFull) -> &u64 { + unsafe { &*(self as *const _ as *const u64) } } } - impl From for words { - fn from(x: u64) -> words { - unsafe { mem::transmute(x) } + impl IndexMut for U64 { + fn index_mut(&mut self, _: RangeFull) -> &mut u64 { + unsafe { &mut *(self as *const _ as *mut u64) } } } let u32_bits = u32::bits() as u32; let u64_bits = u64::bits() as u32; - let n = words::from(a); - let d = words::from(b); - // NOTE X is unknown, K != 0 - if n.high == 0 { - if d.high == 0 { + if n.high() == 0 { + if d.high() == 0 { // 0 X // --- // 0 X if let Some(rem) = rem { - *rem = u64::from(n.low % d.low); + *rem = u64::from(n.low() % d.low()); } - return u64::from(n.low / d.low); + return u64::from(n.low() / d.low()); } else - // d.high != 0 - { + // d.high() != 0 + { // 0 X // --- // K X if let Some(rem) = rem { - *rem = u64::from(n.low); + *rem = u64::from(n.low()); } return 0; }; } let mut sr; - // NOTE IMO it should be possible to leave these "uninitialized" (just declare them here) - // because these variables get initialized below, but if I do that the compiler complains about - // them being used before being initialized. - let mut q = words { low: 0, high: 0 }; - let mut r = words { low: 0, high: 0 }; + let mut q = U64 { low: 0, high: 0 }; + let mut r = U64 { low: 0, high: 0 }; - // n.high != 0 - if d.low == 0 { - if d.high == 0 { + // n.high() != 0 + if d.low() == 0 { + if d.high() == 0 { // K X // --- // 0 0 - // NOTE copied verbatim from compiler-rt, but does division by zero even make sense? + // NOTE copied verbatim from compiler-rt. This probably lets the intrinsic decide how to + // handle the division by zero (SIGFPE, 0, etc.). But this part shouldn't be reachable + // from safe code. if let Some(rem) = rem { - *rem = u64::from(n.high % d.low); + *rem = u64::from(n.high() % d.low()); } - return u64::from(n.high / d.low); + return u64::from(n.high() / d.low()); } - // d.high != 0 - if n.low == 0 { + // d.high() != 0 + if n.low() == 0 { // K 0 // --- // K 0 if let Some(rem) = rem { - *rem = words { - low: 0, - high: n.high % d.high, - } - .u64(); + *rem = U64 { + low: 0, + high: n.high() % d.high(), + }[..]; } - return u64::from(n.high / d.high); + return u64::from(n.high() / d.high()); } - // n.low != 0 + // n.low() != 0 // K K // --- // K 0 - if d.high.is_power_of_two() { + if d.high().is_power_of_two() { if let Some(rem) = rem { - *rem = words { - low: n.low, - high: n.high & (d.high - 1), - } - .u64() + *rem = U64 { + low: n.low(), + high: n.high() & (d.high() - 1), + }[..]; } - return u64::from(n.high >> d.high.trailing_zeros()); + return u64::from(n.high() >> d.high().trailing_zeros()); } - sr = d.high.leading_zeros().wrapping_sub(n.high.leading_zeros()); + sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); // D > N if sr > u32_bits - 2 { if let Some(rem) = rem { - *rem = n.u64(); + *rem = n; } return 0; } @@ -231,75 +230,74 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: Option<&mut u64>) -> u64 { sr = sr + 1; // 1 <= sr <= u32_bits - 1 - // *q.all() = n.u64() << (u64_bits - sr); + // q = n << (u64_bits - sr); q.low = 0; - q.high = n.low << (u32_bits - sr); - // *r.all() = n.u64() >> sr - r.high = n.high >> sr; - r.low = (n.high << (u32_bits - sr)) | (n.low >> sr); + q.high = n.low() << (u32_bits - sr); + // r = n >> sr + r.high = n.high() >> sr; + r.low = (n.high() << (u32_bits - sr)) | (n.low() >> sr); } else - // d.low != 0 + // d.low() != 0 { - if d.high == 0 { + if d.high() == 0 { // K X // --- // 0 K - if d.low.is_power_of_two() { + if d.low().is_power_of_two() { if let Some(rem) = rem { - *rem = u64::from(n.low & (d.low - 1)); + *rem = u64::from(n.low() & (d.low() - 1)); } - return if d.low == 1 { - n.u64() + if d.low() == 1 { + return n; } else { - let sr = d.low.trailing_zeros(); - words { - low: (n.high << (u32_bits - sr)) | (n.low >> sr), - high: n.high >> sr, - } - .u64() + let sr = d.low().trailing_zeros(); + return U64 { + low: (n.high() << (u32_bits - sr)) | (n.low() >> sr), + high: n.high() >> sr, + }[..]; }; } - sr = 1 + u32_bits + d.low.leading_zeros() - n.high.leading_zeros(); + sr = 1 + u32_bits + d.low().leading_zeros() - n.high().leading_zeros(); // 2 <= sr <= u64_bits - 1 - // *q.all() = n.u64() << (u64_bits - sr) - // *r.all() = n.u64() >> sr; + // q = n << (u64_bits - sr) + // r = n >> sr; if sr == u32_bits { q.low = 0; - q.high = n.low; + q.high = n.low(); r.high = 0; - r.low = n.high; + r.low = n.high(); } else if sr < u32_bits // 2 <= sr <= u32_bits - 1 { q.low = 0; - q.high = n.low << (u32_bits - sr); - r.high = n.high >> sr; - r.low = (n.high << (u32_bits - sr)) | (n.low >> sr); + q.high = n.low() << (u32_bits - sr); + r.high = n.high() >> sr; + r.low = (n.high() << (u32_bits - sr)) | (n.low() >> sr); } else // u32_bits + 1 <= sr <= u64_bits - 1 { - q.low = n.low << (u64_bits - sr); - q.high = (n.high << (u64_bits - sr)) | (n.low >> (sr - u32_bits)); + q.low = n.low() << (u64_bits - sr); + q.high = (n.high() << (u64_bits - sr)) | (n.low() >> (sr - u32_bits)); r.high = 0; - r.low = n.high >> (sr - u32_bits); + r.low = n.high() >> (sr - u32_bits); } } else - // d.high != 0 + // d.high() != 0 { // K X // --- // K K - sr = d.high.leading_zeros().wrapping_sub(n.high.leading_zeros()); + sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); // D > N if sr > u32_bits - 1 { if let Some(rem) = rem { - *rem = a; + *rem = n; return 0; } } @@ -307,61 +305,61 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: Option<&mut u64>) -> u64 { sr += 1; // 1 <= sr <= u32_bits - // *q.all() = n.u64() << (u64_bits - sr) + // q = n << (u64_bits - sr) q.low = 0; if sr == u32_bits { - q.high = n.low; + q.high = n.low(); r.high = 0; - r.low = n.high; + r.low = n.high(); } else { - q.high = n.low << (u32_bits - sr); - r.high = n.high >> sr; - r.low = (n.high << (u32_bits - sr)) | (n.low >> sr); + q.high = n.low() << (u32_bits - sr); + r.high = n.high() >> sr; + r.low = (n.high() << (u32_bits - sr)) | (n.low() >> sr); } } } // Not a special case // q and r are initialized with - // *q.all() = n.u64() << (u64_bits - sr) - // *.r.all() = n.u64() >> sr + // q = n << (u64_bits - sr) + // r = n >> sr // 1 <= sr <= u64_bits - 1 let mut carry = 0; for _ in 0..sr { // r:q = ((r:q) << 1) | carry - r.high = (r.high << 1) | (r.low >> (u32_bits - 1)); - r.low = (r.low << 1) | (q.high >> (u32_bits - 1)); - q.high = (q.high << 1) | (q.low >> (u32_bits - 1)); - q.low = (q.low << 1) | carry; + r[..] = (r[..] << 1) | (q[..] >> 63); + q[..] = (q[..] << 1) | carry as u64; // carry = 0 - // if r.u64() >= d.u64() { - // *r.all() -= d.u64(); + // if r >= d { + // r -= d; // carry = 1; // } - let s = (d.u64().wrapping_sub(r.u64()).wrapping_sub(1)) as i64 >> (u64_bits - 1); + let s = (d.wrapping_sub(r[..]).wrapping_sub(1)) as i64 >> (u64_bits - 1); carry = (s & 1) as u32; - *r.all() -= d.u64() & s as u64; + r[..] -= d & s as u64; } - *q.all() = (q.u64() << 1) | carry as u64; + q[..] = (q[..] << 1) | carry as u64; if let Some(rem) = rem { - *rem = r.u64(); + *rem = r[..]; } - q.u64() + q[..] } +/// Return `n / d` and `*rem = n % d` #[no_mangle] pub extern "C" fn __udivmodsi4(a: u32, b: u32, rem: Option<&mut u32>) -> u32 { let d = __udivsi3(a, b); if let Some(rem) = rem { - *rem = a - (d*b); + *rem = a - (d * b); } return d; } +/// Return `n / d` #[no_mangle] pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { let u32_bits = u32::bits() as u32;