Skip to content

Commit 453e170

Browse files
authored
Merge pull request rust-lang#29 from gnzlbg/arm_intrinsics
[arm] bitwise manipulation instructions
2 parents a5e7383 + 4fe06c1 commit 453e170

File tree

10 files changed

+167
-12
lines changed

10 files changed

+167
-12
lines changed

.appveyor.yml

+6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
environment:
2+
# We don't want to do identical comdat folding as it messes up the ability to
3+
# generate lossless backtraces in some cases. This is enabled by rustc by
4+
# default so pass a flag to disable it to ensure our tests work ok.
5+
RUSTFLAGS: -Clink-args=/OPT:NOICF
6+
27
matrix:
38
- TARGET: x86_64-pc-windows-msvc
49

@@ -15,4 +20,5 @@ build: false
1520

1621
test_script:
1722
- cargo test --target %TARGET%
23+
- set RUST_BACKTRACE=1
1824
- cargo test --target %TARGET% --release

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ debug = true
1515
opt-level = 3
1616

1717
[profile.bench]
18-
debug = 1
18+
debug = true
1919
opt-level = 3
2020

2121
[dev-dependencies]

assert-instr/assert-instr-macro/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ pub fn assert_instr(attr: TokenStream, item: TokenStream) -> TokenStream {
4444
#[allow(non_snake_case)]
4545
{ignore}
4646
fn assert_instr_{name}() {{
47-
::assert_instr::assert({name} as usize, \"{instr}\");
47+
::assert_instr::assert({name} as usize,
48+
\"{name}\",
49+
\"{instr}\");
4850
}}
4951
", name = name.as_str(), instr = instr.as_str(), ignore = ignore);
5052
let test: TokenStream = test.parse().unwrap();

assert-instr/src/lib.rs

+18-9
Original file line numberDiff line numberDiff line change
@@ -221,37 +221,47 @@ fn normalize(symbol: &str) -> String {
221221
///
222222
/// This asserts that the function at `fnptr` contains the instruction
223223
/// `expected` provided.
224-
pub fn assert(fnptr: usize, expected: &str) {
224+
pub fn assert(fnptr: usize, fnname: &str, expected: &str) {
225225
// Translate this function pointer to a symbolic name that we'd have found
226226
// in the disassembly.
227227
let mut sym = None;
228228
backtrace::resolve(fnptr as *mut _, |name| {
229229
sym = name.name().and_then(|s| s.as_str()).map(normalize);
230230
});
231-
let sym = match sym {
231+
232+
let functions = match sym.as_ref().and_then(|s| DISASSEMBLY.get(s)) {
232233
Some(s) => s,
233-
None => panic!("failed to get symbol of function pointer: {}", fnptr),
234+
None => {
235+
if let Some(sym) = sym {
236+
println!("assumed symbol name: `{}`", sym);
237+
}
238+
println!("maybe related functions");
239+
for f in DISASSEMBLY.keys().filter(|k| k.contains(fnname)) {
240+
println!("\t- {}", f);
241+
}
242+
panic!("failed to find disassembly of {:#x} ({})", fnptr, fnname);
243+
}
234244
};
235245

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

242249
// Look for `expected` as the first part of any instruction in this
243250
// function, returning if we do indeed find it.
244251
for instr in function.instrs.iter() {
252+
// Gets the first instruction, e.g. tzcntl in tzcntl %rax,%rax
245253
if let Some(part) = instr.parts.get(0) {
246-
if part == expected {
254+
// Truncates the instruction with the length of the expected
255+
// instruction: tzcntl => tzcnt and compares that.
256+
if part.starts_with(expected) {
247257
return
248258
}
249259
}
250260
}
251261

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

src/arm/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//! ARM intrinsics.
2+
pub use self::v6::*;
3+
pub use self::v7::*;
4+
#[cfg(target_arch = "aarch64")]
5+
pub use self::v8::*;
6+
7+
mod v6;
8+
mod v7;
9+
#[cfg(target_arch = "aarch64")]
10+
mod v8;

src/arm/v6.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//! ARMv6 intrinsics.
2+
//!
3+
//! The reference is [ARMv6-M Architecture Reference
4+
//! Manual](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0419c/index.html).
5+
6+
/// Reverse the order of the bytes.
7+
#[inline(always)]
8+
#[cfg_attr(test, assert_instr(rev))]
9+
pub fn _rev_u8(x: u8) -> u8 {
10+
x.swap_bytes() as u8
11+
}
12+
13+
/// Reverse the order of the bytes.
14+
#[inline(always)]
15+
#[cfg_attr(test, assert_instr(rev))]
16+
pub fn _rev_u16(x: u16) -> u16 {
17+
x.swap_bytes() as u16
18+
}
19+
20+
/// Reverse the order of the bytes.
21+
#[inline(always)]
22+
#[cfg_attr(test, assert_instr(rev))]
23+
pub fn _rev_u32(x: u32) -> u32 {
24+
x.swap_bytes() as u32
25+
}

src/arm/v7.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//! ARMv7 intrinsics.
2+
//!
3+
//! The reference is [ARMv7-M Architecture Reference Manual (Issue
4+
//! E.b)](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0403e.b/index.html).
5+
6+
pub use super::v6::*;
7+
8+
/// Count Leading Zeros.
9+
#[inline(always)]
10+
#[cfg_attr(test, assert_instr(clz))]
11+
pub fn _clz_u8(x: u8) -> u8 {
12+
x.leading_zeros() as u8
13+
}
14+
15+
/// Count Leading Zeros.
16+
#[inline(always)]
17+
#[cfg_attr(test, assert_instr(clz))]
18+
pub fn _clz_u16(x: u16) -> u16 {
19+
x.leading_zeros() as u16
20+
}
21+
22+
/// Count Leading Zeros.
23+
#[inline(always)]
24+
#[cfg_attr(test, assert_instr(clz))]
25+
pub fn _clz_u32(x: u32) -> u32 {
26+
x.leading_zeros() as u32
27+
}
28+
29+
#[allow(dead_code)]
30+
extern "C" {
31+
#[link_name="llvm.bitreverse.i32"]
32+
fn rbit_u32(i: i32) -> i32;
33+
}
34+
35+
/// Reverse the bit order.
36+
#[inline(always)]
37+
#[cfg_attr(test, assert_instr(rbit))]
38+
pub fn _rbit_u32(x: u32) -> u32 {
39+
unsafe { rbit_u32(x as i32) as u32 }
40+
}

src/arm/v8.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//! ARMv8 intrinsics.
2+
//!
3+
//! The reference is [ARMv8-A Reference Manual](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0487a.k_10775/index.html).
4+
5+
pub use super::v7::*;
6+
7+
/// Reverse the order of the bytes.
8+
#[inline(always)]
9+
#[cfg_attr(test, assert_instr(rev))]
10+
pub fn _rev_u64(x: u64) -> u64 {
11+
x.swap_bytes() as u64
12+
}
13+
14+
/// Count Leading Zeros.
15+
#[inline(always)]
16+
#[cfg_attr(test, assert_instr(clz))]
17+
pub fn _clz_u64(x: u64) -> u64 {
18+
x.leading_zeros() as u64
19+
}
20+
21+
#[allow(dead_code)]
22+
extern "C" {
23+
#[link_name="llvm.bitreverse.i64"]
24+
fn rbit_u64(i: i64) -> i64;
25+
}
26+
27+
/// Reverse the bit order.
28+
#[inline(always)]
29+
#[cfg_attr(test, assert_instr(rbit))]
30+
pub fn _rbit_u64(x: u64) -> u64 {
31+
unsafe { rbit_u64(x as i64) as u64 }
32+
}
33+
34+
/// Counts the leading most significant bits set.
35+
///
36+
/// When all bits of the operand are set it returns the size of the operand in
37+
/// bits.
38+
#[inline(always)]
39+
// LLVM Bug (should be cls): https://bugs.llvm.org/show_bug.cgi?id=31802
40+
#[cfg_attr(test, assert_instr(clz))]
41+
pub fn _cls_u32(x: u32) -> u32 {
42+
u32::leading_zeros(!x) as u32
43+
}
44+
45+
/// Counts the leading most significant bits set.
46+
///
47+
/// When all bits of the operand are set it returns the size of the operand in
48+
/// bits.
49+
#[inline(always)]
50+
// LLVM Bug (should be cls): https://bugs.llvm.org/show_bug.cgi?id=31802
51+
#[cfg_attr(test, assert_instr(clz))]
52+
pub fn _cls_u64(x: u64) -> u64 {
53+
u64::leading_zeros(!x) as u64
54+
}

src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ pub mod simd {
2020
pub mod vendor {
2121
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2222
pub use x86::*;
23+
24+
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
25+
pub use arm::*;
2326
}
2427

2528
#[macro_use]
@@ -31,3 +34,6 @@ mod v512;
3134
mod v64;
3235
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
3336
mod x86;
37+
38+
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
39+
mod arm;

src/x86/bmi2.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
//! The reference is [Intel 64 and IA-32 Architectures Software Developer's
44
//! Manual Volume 2: Instruction Set Reference,
5-
//! 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).
5+
//! 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).
66
//!
77
//! [Wikipedia](https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets#BMI2_.28Bit_Manipulation_Instruction_Set_2.29)
88
//! provides a quick overview of the available instructions.
@@ -15,6 +15,8 @@ use assert_instr::assert_instr;
1515
/// Unsigned multiplication of `a` with `b` returning a pair `(lo, hi)` with
1616
/// the low half and the high half of the result.
1717
#[inline(always)]
18+
// LLVM BUG (should be mulxl): https://bugs.llvm.org/show_bug.cgi?id=34232
19+
#[cfg_attr(test, assert_instr(imul))]
1820
#[target_feature = "+bmi2"]
1921
pub fn _mulx_u32(a: u32, b: u32) -> (u32, u32) {
2022
let result: u64 = (a as u64) * (b as u64);
@@ -27,6 +29,7 @@ pub fn _mulx_u32(a: u32, b: u32) -> (u32, u32) {
2729
/// Unsigned multiplication of `a` with `b` returning a pair `(lo, hi)` with
2830
/// the low half and the high half of the result.
2931
#[inline(always)]
32+
#[cfg_attr(test, assert_instr(mulx))]
3033
#[target_feature = "+bmi2"]
3134
pub fn _mulx_u64(a: u64, b: u64) -> (u64, u64) {
3235
let result: u128 = (a as u128) * (b as u128);

0 commit comments

Comments
 (0)