Skip to content

Commit 99f2b93

Browse files
committed
Replace asm with intrinsics
The `asm!` directive didn't compile on nightly anymore as it was. The new code still has issues, most obviously that Bit Test and Set instruction is not available. Another issue is the fact that most of these are available through stable atomics interfaces, so those should be used instead. However, they are not available for u128, even though they were for a short time: rust-lang/rust#39590 Lastly, memory-ordering is not specified. However, this is not much different from what it used to be like.
1 parent d517cfd commit 99f2b93

File tree

3 files changed

+33
-88
lines changed

3 files changed

+33
-88
lines changed

src/atomics/x86.rs

Lines changed: 28 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,34 @@
1+
#![allow(mutable_transmutes)]
12

2-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
3-
pub fn compare_and_swap(destination: &u64, expected: u64, new_value: u64) -> bool {
4-
let value_at_dest : u64;
5-
unsafe {
6-
asm!("LOCK CMPXCHG qword ptr [RCX], RBX"
7-
: "={rax}"(value_at_dest) // output
8-
9-
: "{rbx}"(new_value),
10-
"{rcx}"(destination), // input
11-
"{rax}"(expected)
12-
13-
: "rax", "memory" // clobbers
14-
15-
: "intel" // options
16-
);
17-
}
3+
use std::mem;
4+
use std::intrinsics::{atomic_cxchg, atomic_xadd};
185

19-
// this information is also available through the zero flag, but it's
20-
// impossible (?) to use that information without doing some sort of
21-
// secondary compare outside of the asm! block
22-
value_at_dest == expected
23-
}
6+
pub fn compare_and_swap(destination: &u64, expected: u64, new_value: u64) -> bool {
7+
let destination : &mut u64 = unsafe { mem::transmute(destination) };
8+
let (value_at_dest, success) = unsafe { atomic_cxchg(destination, expected, new_value) };
9+
assert!((value_at_dest == expected && success) || (value_at_dest != expected && !success));
2410

25-
#[repr(align(16))]
26-
#[derive(Debug)]
27-
pub struct DoubleU64 {
28-
high: u64,
29-
low: u64,
11+
success
3012
}
3113

32-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
33-
pub fn compare_and_swap_2(destination: &DoubleU64, expected: &DoubleU64, new_value: &DoubleU64) -> bool { // TODO: return Result to pass back values?
34-
let value_at_dest_high : u64;
35-
let value_at_dest_low : u64;
36-
37-
unsafe {
38-
asm!("LOCK CMPXCHG16B [R8]"
39-
: "={rax}"(value_at_dest_high), // output
40-
"={rdx}"(value_at_dest_low)
41-
42-
: "{rbx}"(new_value.high), // input
43-
"{rcx}"(new_value.low),
44-
"{r8}"(destination),
45-
"{rax}"(expected.high)
46-
"{rdx}"(expected.low)
14+
pub fn compare_and_swap_u128(destination: &u128, expected: u128, new_value: u128) -> bool { // TODO: return Result to pass back values?
15+
let destination : &mut u128 = unsafe { mem::transmute(destination) };
16+
let (value_at_dest, success) = unsafe { atomic_cxchg(destination, expected, new_value) };
17+
assert!((value_at_dest == expected && success) || (value_at_dest != expected && !success));
4718

48-
: "rax", "rdx", "memory" // clobbers
49-
50-
: "intel" // options
51-
);
52-
}
53-
54-
// this information is also available through the zero flag, but it's
55-
// impossible (?) to use that information without doing some sort of
56-
// secondary compare outside of the asm! block
57-
value_at_dest_high == expected.high && value_at_dest_low == expected.low
19+
success
5820
}
5921

60-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
6122
pub fn fetch_and_add(destination: &u64, addend: u64) -> u64 {
62-
let value_at_dest : u64;
63-
unsafe {
64-
asm!("LOCK XADD qword ptr [RCX], RBX"
65-
: "={rbx}"(value_at_dest) // output
66-
67-
: "{rbx}"(addend), // input
68-
"{rcx}"(destination)
69-
70-
: "rbx", "memory" // clobbers
71-
72-
: "intel" // options
73-
);
74-
}
23+
let destination : &mut u64 = unsafe { mem::transmute(destination) };
24+
let value_at_dest = unsafe { atomic_xadd(destination, addend) };
7525

7626
value_at_dest
7727
}
7828

79-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
8029
pub fn test_and_set(destination: &u64) {
81-
unsafe {
82-
asm!("LOCK BTS qword ptr [RCX], 63"
83-
: // output
84-
: "{rcx}"(destination) // input
85-
: "rbx", "memory" // clobbers
86-
: "intel" // options
87-
);
88-
}
30+
//let destination : &mut u64 = unsafe { mem::transmute(destination) };
31+
//*destination = *destination | (1 << 63);
8932
}
9033

9134
#[cfg(test)]
@@ -112,15 +55,17 @@ mod test {
11255
}
11356

11457
#[test]
115-
fn test_compare_and_swap_2_single_thread() {
116-
let mut x = DoubleU64 { high: 1, low: 2 };
117-
assert!(compare_and_swap_2(&mut x, &DoubleU64 { high: 1, low: 2 }, &DoubleU64 { high: 2, low: 3 }));
118-
assert_eq!(x.high, 2);
119-
assert_eq!(x.low , 3);
120-
121-
assert!(!compare_and_swap_2(&mut x, &DoubleU64 { high: 1, low: 2 }, &DoubleU64 { high: 3, low: 2 }));
122-
assert_eq!(x.high, 2);
123-
assert_eq!(x.low , 3);
58+
fn test_compare_and_swap_u128_single_thread() {
59+
let mut x = 0x00000000000000010000000000000002u128;
60+
let expect = 0x00000000000000010000000000000002u128;
61+
let new = 0x00000000000000020000000000000003u128;
62+
assert!(compare_and_swap_u128(&mut x, expect, new));
63+
assert_eq!(x, new);
64+
65+
// won't swap new for newer
66+
let newer = 0x00000000000000030000000000000002u128;
67+
assert!(!compare_and_swap_u128(&mut x, expect, newer));
68+
assert_eq!(x, new);
12469
}
12570

12671
#[test]

src/crq.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ use node::{ Node, NODE_VALUE_EMPTY };
99
use atomics::x86::*;
1010

1111
fn compare_and_swap_nodes(node: &Node, expected: &Node, new_value: &Node) -> bool {
12-
let mem_current : &DoubleU64 = unsafe { mem::transmute(node) };
13-
let mem_expected : &DoubleU64 = unsafe { mem::transmute(expected) };
14-
let mem_new_value : &DoubleU64 = unsafe { mem::transmute(new_value) };
12+
let mem_current : &u128 = unsafe { mem::transmute(node) };
13+
let mem_expected : &u128 = unsafe { mem::transmute(expected) };
14+
let mem_new_value : &u128 = unsafe { mem::transmute(new_value) };
1515

16-
compare_and_swap_2(mem_current, mem_expected, mem_new_value)
16+
compare_and_swap_u128(mem_current, *mem_expected, *mem_new_value)
1717
}
1818

1919
pub const RING_SIZE: usize = 256;

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![feature(asm)]
1+
#![feature(core_intrinsics)]
22

33
pub mod crq;
44
pub mod lcrq;

0 commit comments

Comments
 (0)