diff --git a/README.md b/README.md index 9a847da3..a24c1273 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,9 @@ rely on CI. - [ ] arm/unordsf2vfp.S - [x] ashldi3.c - [x] ashrdi3.c +- [x] bswapdi2.c +- [x] bswapsi2.c +- [x] bswapti2.c - [x] comparedf2.c - [x] comparesf2.c - [x] divdf3.c diff --git a/build.rs b/build.rs index 0ecd3991..886898d7 100644 --- a/build.rs +++ b/build.rs @@ -161,6 +161,9 @@ fn configure_check_cfg() { "__ashlsi3", "__ashrdi3", "__ashrsi3", + "__bswapsi2", + "__bswapdi2", + "__bswapti2", "__clzsi2", "__divdi3", "__divsi3", diff --git a/src/int/bswap.rs b/src/int/bswap.rs new file mode 100644 index 00000000..4ad5877b --- /dev/null +++ b/src/int/bswap.rs @@ -0,0 +1,32 @@ +use crate::int::Int; + +fn bswap>(x: I) -> I { + let mut ret = I::ZERO; + for (i, byte) in x.to_be_bytes().into_iter().enumerate() { + ret |= I::from(byte) << (i as u32 * 8); + } + ret +} + +intrinsics! { + #[maybe_use_optimized_c_shim] + #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] + /// Swaps bytes in 32-bit number + pub extern "C" fn __bswapsi2(x: u32) -> u32 { + bswap(x) + } + + #[maybe_use_optimized_c_shim] + #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] + /// Swaps bytes in 64-bit number + pub extern "C" fn __bswapdi2(x: u64) -> u64 { + bswap(x) + } + + #[maybe_use_optimized_c_shim] + #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] + /// Swaps bytes in 128-bit number + pub extern "C" fn __bswapti2(x: u128) -> u128 { + bswap(x) + } +} diff --git a/src/int/mod.rs b/src/int/mod.rs index 45d38388..471db3ed 100644 --- a/src/int/mod.rs +++ b/src/int/mod.rs @@ -4,6 +4,7 @@ mod specialized_div_rem; pub mod addsub; mod big; +pub mod bswap; pub mod leading_zeros; pub mod mul; pub mod sdiv; @@ -62,6 +63,8 @@ pub(crate) trait Int: MinInt + ops::BitXor + ops::BitAnd { + type Bytes: IntoIterator; + /// LUT used for maximizing the space covered and minimizing the computational cost of fuzzing /// in `testcrate`. For example, Self = u128 produces [0,1,2,7,8,15,16,31,32,63,64,95,96,111, /// 112,119,120,125,126,127]. @@ -103,6 +106,8 @@ pub(crate) trait Int: MinInt fn overflowing_add(self, other: Self) -> (Self, bool); fn leading_zeros(self) -> u32; fn ilog2(self) -> u32; + + fn to_be_bytes(self) -> Self::Bytes; } } @@ -154,6 +159,8 @@ pub(crate) const fn make_fuzz_lengths(bits: u32) -> [u8; 20] { macro_rules! int_impl_common { ($ty:ty) => { + type Bytes = [u8; Self::BITS as usize / 8]; + fn from_bool(b: bool) -> Self { b as $ty } @@ -205,6 +212,10 @@ macro_rules! int_impl_common { fn ilog2(self) -> u32 { ::ilog2(self) } + + fn to_be_bytes(self) -> Self::Bytes { + self.to_be_bytes() + } }; } diff --git a/testcrate/tests/misc.rs b/testcrate/tests/misc.rs index e01223c7..362fbd96 100644 --- a/testcrate/tests/misc.rs +++ b/testcrate/tests/misc.rs @@ -92,6 +92,34 @@ fn leading_zeros() { }) } +#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] +#[test] +fn bswap() { + use compiler_builtins::int::bswap::{__bswapdi2, __bswapsi2, __bswapti2}; + fuzz(N, |x: u32| { + assert_eq!(x.swap_bytes(), __bswapsi2(x)); + }); + fuzz(N, |x: u64| { + assert_eq!(x.swap_bytes(), __bswapdi2(x)); + }); + fuzz(N, |x: u128| { + assert_eq!(x.swap_bytes(), __bswapti2(x)); + }); + + assert_eq!(__bswapsi2(0x12345678u32), 0x78563412u32); + assert_eq!(__bswapsi2(0x00000001u32), 0x01000000u32); + assert_eq!(__bswapdi2(0x123456789ABCDEF0u64), 0xF0DEBC9A78563412u64); + assert_eq!(__bswapdi2(0x0200000001000000u64), 0x0000000100000002u64); + assert_eq!( + __bswapti2(0x123456789ABCDEF013579BDF02468ACEu128), + 0xCE8A4602DF9B5713F0DEBC9A78563412u128 + ); + assert_eq!( + __bswapti2(0x04000000030000000200000001000000u128), + 0x00000001000000020000000300000004u128 + ); +} + // This is approximate because of issues related to // https://github.com/rust-lang/rust/issues/73920. // TODO how do we resolve this indeterminacy?