Skip to content

Commit

Permalink
Merge pull request rust-lang#29 from gnzlbg/arm_intrinsics
Browse files Browse the repository at this point in the history
[arm] bitwise manipulation instructions
  • Loading branch information
alexcrichton committed Sep 21, 2017
2 parents a5e7383 + 4fe06c1 commit 453e170
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 12 deletions.
6 changes: 6 additions & 0 deletions .appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
environment:
# We don't want to do identical comdat folding as it messes up the ability to
# generate lossless backtraces in some cases. This is enabled by rustc by
# default so pass a flag to disable it to ensure our tests work ok.
RUSTFLAGS: -Clink-args=/OPT:NOICF

matrix:
- TARGET: x86_64-pc-windows-msvc

Expand All @@ -15,4 +20,5 @@ build: false

test_script:
- cargo test --target %TARGET%
- set RUST_BACKTRACE=1
- cargo test --target %TARGET% --release
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ debug = true
opt-level = 3

[profile.bench]
debug = 1
debug = true
opt-level = 3

[dev-dependencies]
Expand Down
4 changes: 3 additions & 1 deletion assert-instr/assert-instr-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ pub fn assert_instr(attr: TokenStream, item: TokenStream) -> TokenStream {
#[allow(non_snake_case)]
{ignore}
fn assert_instr_{name}() {{
::assert_instr::assert({name} as usize, \"{instr}\");
::assert_instr::assert({name} as usize,
\"{name}\",
\"{instr}\");
}}
", name = name.as_str(), instr = instr.as_str(), ignore = ignore);
let test: TokenStream = test.parse().unwrap();
Expand Down
27 changes: 18 additions & 9 deletions assert-instr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,37 +221,47 @@ fn normalize(symbol: &str) -> String {
///
/// This asserts that the function at `fnptr` contains the instruction
/// `expected` provided.
pub fn assert(fnptr: usize, expected: &str) {
pub fn assert(fnptr: usize, fnname: &str, expected: &str) {
// Translate this function pointer to a symbolic name that we'd have found
// in the disassembly.
let mut sym = None;
backtrace::resolve(fnptr as *mut _, |name| {
sym = name.name().and_then(|s| s.as_str()).map(normalize);
});
let sym = match sym {

let functions = match sym.as_ref().and_then(|s| DISASSEMBLY.get(s)) {
Some(s) => s,
None => panic!("failed to get symbol of function pointer: {}", fnptr),
None => {
if let Some(sym) = sym {
println!("assumed symbol name: `{}`", sym);
}
println!("maybe related functions");
for f in DISASSEMBLY.keys().filter(|k| k.contains(fnname)) {
println!("\t- {}", f);
}
panic!("failed to find disassembly of {:#x} ({})", fnptr, fnname);
}
};

// Find our function in the list of all disassembled functions
let functions = &DISASSEMBLY.get(&sym)
.expect(&format!("failed to find disassembly of {}", sym));
assert_eq!(functions.len(), 1);
let function = &functions[0];

// Look for `expected` as the first part of any instruction in this
// function, returning if we do indeed find it.
for instr in function.instrs.iter() {
// Gets the first instruction, e.g. tzcntl in tzcntl %rax,%rax
if let Some(part) = instr.parts.get(0) {
if part == expected {
// Truncates the instruction with the length of the expected
// instruction: tzcntl => tzcnt and compares that.
if part.starts_with(expected) {
return
}
}
}

// Help debug by printing out the found disassembly, and then panic as we
// didn't find the instruction.
println!("disassembly for {}: ", sym);
println!("disassembly for {}: ", sym.as_ref().unwrap());
for (i, instr) in function.instrs.iter().enumerate() {
print!("\t{:2}: ", i);
for part in instr.parts.iter() {
Expand All @@ -261,4 +271,3 @@ pub fn assert(fnptr: usize, expected: &str) {
}
panic!("failed to find instruction `{}` in the disassembly", expected);
}

10 changes: 10 additions & 0 deletions src/arm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! ARM intrinsics.
pub use self::v6::*;
pub use self::v7::*;
#[cfg(target_arch = "aarch64")]
pub use self::v8::*;

mod v6;
mod v7;
#[cfg(target_arch = "aarch64")]
mod v8;
25 changes: 25 additions & 0 deletions src/arm/v6.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! ARMv6 intrinsics.
//!
//! The reference is [ARMv6-M Architecture Reference
//! Manual](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0419c/index.html).

/// Reverse the order of the bytes.
#[inline(always)]
#[cfg_attr(test, assert_instr(rev))]
pub fn _rev_u8(x: u8) -> u8 {
x.swap_bytes() as u8
}

/// Reverse the order of the bytes.
#[inline(always)]
#[cfg_attr(test, assert_instr(rev))]
pub fn _rev_u16(x: u16) -> u16 {
x.swap_bytes() as u16
}

/// Reverse the order of the bytes.
#[inline(always)]
#[cfg_attr(test, assert_instr(rev))]
pub fn _rev_u32(x: u32) -> u32 {
x.swap_bytes() as u32
}
40 changes: 40 additions & 0 deletions src/arm/v7.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//! ARMv7 intrinsics.
//!
//! The reference is [ARMv7-M Architecture Reference Manual (Issue
//! E.b)](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0403e.b/index.html).

pub use super::v6::*;

/// Count Leading Zeros.
#[inline(always)]
#[cfg_attr(test, assert_instr(clz))]
pub fn _clz_u8(x: u8) -> u8 {
x.leading_zeros() as u8
}

/// Count Leading Zeros.
#[inline(always)]
#[cfg_attr(test, assert_instr(clz))]
pub fn _clz_u16(x: u16) -> u16 {
x.leading_zeros() as u16
}

/// Count Leading Zeros.
#[inline(always)]
#[cfg_attr(test, assert_instr(clz))]
pub fn _clz_u32(x: u32) -> u32 {
x.leading_zeros() as u32
}

#[allow(dead_code)]
extern "C" {
#[link_name="llvm.bitreverse.i32"]
fn rbit_u32(i: i32) -> i32;
}

/// Reverse the bit order.
#[inline(always)]
#[cfg_attr(test, assert_instr(rbit))]
pub fn _rbit_u32(x: u32) -> u32 {
unsafe { rbit_u32(x as i32) as u32 }
}
54 changes: 54 additions & 0 deletions src/arm/v8.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! ARMv8 intrinsics.
//!
//! The reference is [ARMv8-A Reference Manual](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0487a.k_10775/index.html).

pub use super::v7::*;

/// Reverse the order of the bytes.
#[inline(always)]
#[cfg_attr(test, assert_instr(rev))]
pub fn _rev_u64(x: u64) -> u64 {
x.swap_bytes() as u64
}

/// Count Leading Zeros.
#[inline(always)]
#[cfg_attr(test, assert_instr(clz))]
pub fn _clz_u64(x: u64) -> u64 {
x.leading_zeros() as u64
}

#[allow(dead_code)]
extern "C" {
#[link_name="llvm.bitreverse.i64"]
fn rbit_u64(i: i64) -> i64;
}

/// Reverse the bit order.
#[inline(always)]
#[cfg_attr(test, assert_instr(rbit))]
pub fn _rbit_u64(x: u64) -> u64 {
unsafe { rbit_u64(x as i64) as u64 }
}

/// Counts the leading most significant bits set.
///
/// When all bits of the operand are set it returns the size of the operand in
/// bits.
#[inline(always)]
// LLVM Bug (should be cls): https://bugs.llvm.org/show_bug.cgi?id=31802
#[cfg_attr(test, assert_instr(clz))]
pub fn _cls_u32(x: u32) -> u32 {
u32::leading_zeros(!x) as u32
}

/// Counts the leading most significant bits set.
///
/// When all bits of the operand are set it returns the size of the operand in
/// bits.
#[inline(always)]
// LLVM Bug (should be cls): https://bugs.llvm.org/show_bug.cgi?id=31802
#[cfg_attr(test, assert_instr(clz))]
pub fn _cls_u64(x: u64) -> u64 {
u64::leading_zeros(!x) as u64
}
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub mod simd {
pub mod vendor {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use x86::*;

#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
pub use arm::*;
}

#[macro_use]
Expand All @@ -31,3 +34,6 @@ mod v512;
mod v64;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod x86;

#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
mod arm;
5 changes: 4 additions & 1 deletion src/x86/bmi2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! The reference is [Intel 64 and IA-32 Architectures Software Developer's
//! Manual Volume 2: Instruction Set Reference,
//! A-Z](http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf).
//! A-Z](http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectu res-software-developer-instruction-set-reference-manual-325383.pdf).
//!
//! [Wikipedia](https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets#BMI2_.28Bit_Manipulation_Instruction_Set_2.29)
//! provides a quick overview of the available instructions.
Expand All @@ -15,6 +15,8 @@ use assert_instr::assert_instr;
/// Unsigned multiplication of `a` with `b` returning a pair `(lo, hi)` with
/// the low half and the high half of the result.
#[inline(always)]
// LLVM BUG (should be mulxl): https://bugs.llvm.org/show_bug.cgi?id=34232
#[cfg_attr(test, assert_instr(imul))]
#[target_feature = "+bmi2"]
pub fn _mulx_u32(a: u32, b: u32) -> (u32, u32) {
let result: u64 = (a as u64) * (b as u64);
Expand All @@ -27,6 +29,7 @@ pub fn _mulx_u32(a: u32, b: u32) -> (u32, u32) {
/// Unsigned multiplication of `a` with `b` returning a pair `(lo, hi)` with
/// the low half and the high half of the result.
#[inline(always)]
#[cfg_attr(test, assert_instr(mulx))]
#[target_feature = "+bmi2"]
pub fn _mulx_u64(a: u64, b: u64) -> (u64, u64) {
let result: u128 = (a as u128) * (b as u128);
Expand Down

0 comments on commit 453e170

Please sign in to comment.