Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functions for reversing the bit pattern in an integer #48573

Merged
merged 3 commits into from
Mar 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,10 @@ extern "rust-intrinsic" {
/// Reverses the bytes in an integer type `T`.
pub fn bswap<T>(x: T) -> T;

/// Reverses the bits in an integer type `T`.
#[cfg(not(stage0))]
pub fn bitreverse<T>(x: T) -> T;

/// Performs checked integer addition.
/// The stabilized versions of this intrinsic are available on the integer
/// primitives via the `overflowing_add` method. For example,
Expand Down
54 changes: 54 additions & 0 deletions src/libcore/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,33 @@ $EndFeature, "
(self as $UnsignedT).swap_bytes() as Self
}

/// Reverses the bit pattern of the integer.
///
/// # Examples
///
/// Please note that this example is shared between integer types.
/// Which explains why `i16` is used here.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GuillaumeGomez @QuietMisdreavus I feel like I've recently heard something about us being able to generate dedicated examples with the right types for primitives...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep we did here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just copied the docs for swap_bytes and made minor adjustments.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, in this case it is very complicated to be able to write a fully generic example.

///
/// Basic usage:
///
/// ```
/// #![feature(reverse_bits)]
///
/// let n: i16 = 0b0000000_01010101;
/// assert_eq!(n, 85);
///
/// let m = n.reverse_bits();
///
/// assert_eq!(m as u16, 0b10101010_00000000);
/// assert_eq!(m, -22016);
/// ```
#[unstable(feature = "reverse_bits", issue = "48763")]
#[cfg(not(stage0))]
#[inline]
pub fn reverse_bits(self) -> Self {
(self as $UnsignedT).reverse_bits() as Self
}

doc_comment! {
concat!("Converts an integer from big endian to the target's endianness.

Expand Down Expand Up @@ -1773,6 +1800,33 @@ assert_eq!(n.trailing_zeros(), 3);", $EndFeature, "
unsafe { intrinsics::bswap(self as $ActualT) as Self }
}

/// Reverses the bit pattern of the integer.
///
/// # Examples
///
/// Basic usage:
///
/// Please note that this example is shared between integer types.
/// Which explains why `u16` is used here.
///
/// ```
/// #![feature(reverse_bits)]
///
/// let n: u16 = 0b0000000_01010101;
/// assert_eq!(n, 85);
///
/// let m = n.reverse_bits();
///
/// assert_eq!(m, 0b10101010_00000000);
/// assert_eq!(m, 43520);
/// ```
#[unstable(feature = "reverse_bits", issue = "48763")]
#[cfg(not(stage0))]
#[inline]
pub fn reverse_bits(self) -> Self {
unsafe { intrinsics::bitreverse(self as $ActualT) as Self }
}

doc_comment! {
concat!("Converts an integer from big endian to the target's endianness.

Expand Down
1 change: 1 addition & 0 deletions src/libcore/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#![feature(try_trait)]
#![feature(exact_chunks)]
#![feature(atomic_nand)]
#![feature(reverse_bits)]

extern crate core;
extern crate test;
Expand Down
11 changes: 11 additions & 0 deletions src/libcore/tests/num/uint_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ mod tests {
assert_eq!(_1.swap_bytes(), _1);
}

#[test]
fn test_reverse_bits() {
assert_eq!(A.reverse_bits().reverse_bits(), A);
assert_eq!(B.reverse_bits().reverse_bits(), B);
assert_eq!(C.reverse_bits().reverse_bits(), C);

// Swapping these should make no difference
assert_eq!(_0.reverse_bits(), _0);
assert_eq!(_1.reverse_bits(), _1);
}

#[test]
fn test_le() {
assert_eq!($T::from_le(A.to_le()), A);
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_trans/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,12 @@ fn declare_intrinsic(cx: &CodegenCx, key: &str) -> Option<ValueRef> {
ifn!("llvm.bswap.i64", fn(t_i64) -> t_i64);
ifn!("llvm.bswap.i128", fn(t_i128) -> t_i128);

ifn!("llvm.bitreverse.i8", fn(t_i8) -> t_i8);
ifn!("llvm.bitreverse.i16", fn(t_i16) -> t_i16);
ifn!("llvm.bitreverse.i32", fn(t_i32) -> t_i32);
ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64);
ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128);

ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1});
ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1});
ifn!("llvm.sadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1});
Expand Down
8 changes: 6 additions & 2 deletions src/librustc_trans/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,8 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>,
], None)
},
"ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "ctpop" | "bswap" |
"add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" |
"overflowing_add" | "overflowing_sub" | "overflowing_mul" |
"bitreverse" | "add_with_overflow" | "sub_with_overflow" |
"mul_with_overflow" | "overflowing_add" | "overflowing_sub" | "overflowing_mul" |
"unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" => {
let ty = arg_tys[0];
match int_type_width_signed(ty, cx) {
Expand All @@ -315,6 +315,10 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>,
&[args[0].immediate()], None)
}
}
"bitreverse" => {
bx.call(cx.get_intrinsic(&format!("llvm.bitreverse.i{}", width)),
&[args[0].immediate()], None)
}
"add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => {
let intrinsic = format!("llvm.{}{}.with.overflow.i{}",
if signed { 's' } else { 'u' },
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_typeck/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,8 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
"volatile_store" =>
(1, vec![ tcx.mk_mut_ptr(param(0)), param(0) ], tcx.mk_nil()),

"ctpop" | "ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "bswap" =>
"ctpop" | "ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" |
"bswap" | "bitreverse" =>
(1, vec![param(0)], param(0)),

"add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" =>
Expand Down
37 changes: 36 additions & 1 deletion src/test/run-pass/intrinsics-integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(intrinsics)]
#![feature(intrinsics, i128_type)]

mod rusti {
extern "rust-intrinsic" {
Expand All @@ -18,6 +18,7 @@ mod rusti {
pub fn cttz<T>(x: T) -> T;
pub fn cttz_nonzero<T>(x: T) -> T;
pub fn bswap<T>(x: T) -> T;
pub fn bitreverse<T>(x: T) -> T;
}
}

Expand All @@ -29,106 +30,127 @@ pub fn main() {
assert_eq!(ctpop(0u16), 0); assert_eq!(ctpop(0i16), 0);
assert_eq!(ctpop(0u32), 0); assert_eq!(ctpop(0i32), 0);
assert_eq!(ctpop(0u64), 0); assert_eq!(ctpop(0i64), 0);
assert_eq!(ctpop(0u128), 0); assert_eq!(ctpop(0i128), 0);

assert_eq!(ctpop(1u8), 1); assert_eq!(ctpop(1i8), 1);
assert_eq!(ctpop(1u16), 1); assert_eq!(ctpop(1i16), 1);
assert_eq!(ctpop(1u32), 1); assert_eq!(ctpop(1i32), 1);
assert_eq!(ctpop(1u64), 1); assert_eq!(ctpop(1i64), 1);
assert_eq!(ctpop(1u128), 1); assert_eq!(ctpop(1i128), 1);

assert_eq!(ctpop(10u8), 2); assert_eq!(ctpop(10i8), 2);
assert_eq!(ctpop(10u16), 2); assert_eq!(ctpop(10i16), 2);
assert_eq!(ctpop(10u32), 2); assert_eq!(ctpop(10i32), 2);
assert_eq!(ctpop(10u64), 2); assert_eq!(ctpop(10i64), 2);
assert_eq!(ctpop(10u128), 2); assert_eq!(ctpop(10i128), 2);

assert_eq!(ctpop(100u8), 3); assert_eq!(ctpop(100i8), 3);
assert_eq!(ctpop(100u16), 3); assert_eq!(ctpop(100i16), 3);
assert_eq!(ctpop(100u32), 3); assert_eq!(ctpop(100i32), 3);
assert_eq!(ctpop(100u64), 3); assert_eq!(ctpop(100i64), 3);
assert_eq!(ctpop(100u128), 3); assert_eq!(ctpop(100i128), 3);

assert_eq!(ctpop(-1i8 as u8), 8); assert_eq!(ctpop(-1i8), 8);
assert_eq!(ctpop(-1i16 as u16), 16); assert_eq!(ctpop(-1i16), 16);
assert_eq!(ctpop(-1i32 as u32), 32); assert_eq!(ctpop(-1i32), 32);
assert_eq!(ctpop(-1i64 as u64), 64); assert_eq!(ctpop(-1i64), 64);
assert_eq!(ctpop(-1i128 as u128), 128); assert_eq!(ctpop(-1i128), 128);

assert_eq!(ctlz(0u8), 8); assert_eq!(ctlz(0i8), 8);
assert_eq!(ctlz(0u16), 16); assert_eq!(ctlz(0i16), 16);
assert_eq!(ctlz(0u32), 32); assert_eq!(ctlz(0i32), 32);
assert_eq!(ctlz(0u64), 64); assert_eq!(ctlz(0i64), 64);
assert_eq!(ctlz(0u128), 128); assert_eq!(ctlz(0i128), 128);

assert_eq!(ctlz(1u8), 7); assert_eq!(ctlz(1i8), 7);
assert_eq!(ctlz(1u16), 15); assert_eq!(ctlz(1i16), 15);
assert_eq!(ctlz(1u32), 31); assert_eq!(ctlz(1i32), 31);
assert_eq!(ctlz(1u64), 63); assert_eq!(ctlz(1i64), 63);
assert_eq!(ctlz(1u128), 127); assert_eq!(ctlz(1i128), 127);

assert_eq!(ctlz(10u8), 4); assert_eq!(ctlz(10i8), 4);
assert_eq!(ctlz(10u16), 12); assert_eq!(ctlz(10i16), 12);
assert_eq!(ctlz(10u32), 28); assert_eq!(ctlz(10i32), 28);
assert_eq!(ctlz(10u64), 60); assert_eq!(ctlz(10i64), 60);
assert_eq!(ctlz(10u128), 124); assert_eq!(ctlz(10i128), 124);

assert_eq!(ctlz(100u8), 1); assert_eq!(ctlz(100i8), 1);
assert_eq!(ctlz(100u16), 9); assert_eq!(ctlz(100i16), 9);
assert_eq!(ctlz(100u32), 25); assert_eq!(ctlz(100i32), 25);
assert_eq!(ctlz(100u64), 57); assert_eq!(ctlz(100i64), 57);
assert_eq!(ctlz(100u128), 121); assert_eq!(ctlz(100i128), 121);

assert_eq!(ctlz_nonzero(1u8), 7); assert_eq!(ctlz_nonzero(1i8), 7);
assert_eq!(ctlz_nonzero(1u16), 15); assert_eq!(ctlz_nonzero(1i16), 15);
assert_eq!(ctlz_nonzero(1u32), 31); assert_eq!(ctlz_nonzero(1i32), 31);
assert_eq!(ctlz_nonzero(1u64), 63); assert_eq!(ctlz_nonzero(1i64), 63);
assert_eq!(ctlz_nonzero(1u128), 127); assert_eq!(ctlz_nonzero(1i128), 127);

assert_eq!(ctlz_nonzero(10u8), 4); assert_eq!(ctlz_nonzero(10i8), 4);
assert_eq!(ctlz_nonzero(10u16), 12); assert_eq!(ctlz_nonzero(10i16), 12);
assert_eq!(ctlz_nonzero(10u32), 28); assert_eq!(ctlz_nonzero(10i32), 28);
assert_eq!(ctlz_nonzero(10u64), 60); assert_eq!(ctlz_nonzero(10i64), 60);
assert_eq!(ctlz_nonzero(10u128), 124); assert_eq!(ctlz_nonzero(10i128), 124);

assert_eq!(ctlz_nonzero(100u8), 1); assert_eq!(ctlz_nonzero(100i8), 1);
assert_eq!(ctlz_nonzero(100u16), 9); assert_eq!(ctlz_nonzero(100i16), 9);
assert_eq!(ctlz_nonzero(100u32), 25); assert_eq!(ctlz_nonzero(100i32), 25);
assert_eq!(ctlz_nonzero(100u64), 57); assert_eq!(ctlz_nonzero(100i64), 57);
assert_eq!(ctlz_nonzero(100u128), 121); assert_eq!(ctlz_nonzero(100i128), 121);

assert_eq!(cttz(-1i8 as u8), 0); assert_eq!(cttz(-1i8), 0);
assert_eq!(cttz(-1i16 as u16), 0); assert_eq!(cttz(-1i16), 0);
assert_eq!(cttz(-1i32 as u32), 0); assert_eq!(cttz(-1i32), 0);
assert_eq!(cttz(-1i64 as u64), 0); assert_eq!(cttz(-1i64), 0);
assert_eq!(cttz(-1i128 as u128), 0); assert_eq!(cttz(-1i128), 0);

assert_eq!(cttz(0u8), 8); assert_eq!(cttz(0i8), 8);
assert_eq!(cttz(0u16), 16); assert_eq!(cttz(0i16), 16);
assert_eq!(cttz(0u32), 32); assert_eq!(cttz(0i32), 32);
assert_eq!(cttz(0u64), 64); assert_eq!(cttz(0i64), 64);
assert_eq!(cttz(0u128), 128); assert_eq!(cttz(0i128), 128);

assert_eq!(cttz(1u8), 0); assert_eq!(cttz(1i8), 0);
assert_eq!(cttz(1u16), 0); assert_eq!(cttz(1i16), 0);
assert_eq!(cttz(1u32), 0); assert_eq!(cttz(1i32), 0);
assert_eq!(cttz(1u64), 0); assert_eq!(cttz(1i64), 0);
assert_eq!(cttz(1u128), 0); assert_eq!(cttz(1i128), 0);

assert_eq!(cttz(10u8), 1); assert_eq!(cttz(10i8), 1);
assert_eq!(cttz(10u16), 1); assert_eq!(cttz(10i16), 1);
assert_eq!(cttz(10u32), 1); assert_eq!(cttz(10i32), 1);
assert_eq!(cttz(10u64), 1); assert_eq!(cttz(10i64), 1);
assert_eq!(cttz(10u128), 1); assert_eq!(cttz(10i128), 1);

assert_eq!(cttz(100u8), 2); assert_eq!(cttz(100i8), 2);
assert_eq!(cttz(100u16), 2); assert_eq!(cttz(100i16), 2);
assert_eq!(cttz(100u32), 2); assert_eq!(cttz(100i32), 2);
assert_eq!(cttz(100u64), 2); assert_eq!(cttz(100i64), 2);
assert_eq!(cttz(100u128), 2); assert_eq!(cttz(100i128), 2);

assert_eq!(cttz_nonzero(-1i8 as u8), 0); assert_eq!(cttz_nonzero(-1i8), 0);
assert_eq!(cttz_nonzero(-1i16 as u16), 0); assert_eq!(cttz_nonzero(-1i16), 0);
assert_eq!(cttz_nonzero(-1i32 as u32), 0); assert_eq!(cttz_nonzero(-1i32), 0);
assert_eq!(cttz_nonzero(-1i64 as u64), 0); assert_eq!(cttz_nonzero(-1i64), 0);
assert_eq!(cttz_nonzero(-1i128 as u128), 0); assert_eq!(cttz_nonzero(-1i128), 0);

assert_eq!(cttz_nonzero(1u8), 0); assert_eq!(cttz_nonzero(1i8), 0);
assert_eq!(cttz_nonzero(1u16), 0); assert_eq!(cttz_nonzero(1i16), 0);
assert_eq!(cttz_nonzero(1u32), 0); assert_eq!(cttz_nonzero(1i32), 0);
assert_eq!(cttz_nonzero(1u64), 0); assert_eq!(cttz_nonzero(1i64), 0);
assert_eq!(cttz_nonzero(1u128), 0); assert_eq!(cttz_nonzero(1i128), 0);

assert_eq!(cttz_nonzero(10u8), 1); assert_eq!(cttz_nonzero(10i8), 1);
assert_eq!(cttz_nonzero(10u16), 1); assert_eq!(cttz_nonzero(10i16), 1);
assert_eq!(cttz_nonzero(10u32), 1); assert_eq!(cttz_nonzero(10i32), 1);
assert_eq!(cttz_nonzero(10u64), 1); assert_eq!(cttz_nonzero(10i64), 1);
assert_eq!(cttz_nonzero(10u128), 1); assert_eq!(cttz_nonzero(10i128), 1);

assert_eq!(cttz_nonzero(100u8), 2); assert_eq!(cttz_nonzero(100i8), 2);
assert_eq!(cttz_nonzero(100u16), 2); assert_eq!(cttz_nonzero(100i16), 2);
assert_eq!(cttz_nonzero(100u32), 2); assert_eq!(cttz_nonzero(100i32), 2);
assert_eq!(cttz_nonzero(100u64), 2); assert_eq!(cttz_nonzero(100i64), 2);
assert_eq!(cttz_nonzero(100u128), 2); assert_eq!(cttz_nonzero(100i128), 2);

assert_eq!(bswap(0x0Au8), 0x0A); // no-op
assert_eq!(bswap(0x0Ai8), 0x0A); // no-op
Expand All @@ -138,5 +160,18 @@ pub fn main() {
assert_eq!(bswap(0x0ABBCC0Di32), 0x0DCCBB0A);
assert_eq!(bswap(0x0122334455667708u64), 0x0877665544332201);
assert_eq!(bswap(0x0122334455667708i64), 0x0877665544332201);
assert_eq!(bswap(0x0122334455667708u128), 0x08776655443322010000000000000000);
assert_eq!(bswap(0x0122334455667708i128), 0x08776655443322010000000000000000);

assert_eq!(bitreverse(0x0Au8), 0x50);
assert_eq!(bitreverse(0x0Ai8), 0x50);
assert_eq!(bitreverse(0x0A0Cu16), 0x3050);
assert_eq!(bitreverse(0x0A0Ci16), 0x3050);
assert_eq!(bitreverse(0x0ABBCC0Eu32), 0x7033DD50);
assert_eq!(bitreverse(0x0ABBCC0Ei32), 0x7033DD50);
assert_eq!(bitreverse(0x0122334455667708u64), 0x10EE66AA22CC4480);
assert_eq!(bitreverse(0x0122334455667708i64), 0x10EE66AA22CC4480);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a test for u/i128? It would not surprise me at all if some LLVM backends have trouble with this intrinsic on 128 bit integers, so it would be good to have at least some test to demonstrate that it works at all. (And if it works at all, it's probably reasonably robust given how LLVM is structured.)

assert_eq!(bitreverse(0x0122334455667708u128), 0x10EE66AA22CC44800000000000000000);
assert_eq!(bitreverse(0x0122334455667708i128), 0x10EE66AA22CC44800000000000000000);
}
}