1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use crate::arch::asm;
#[cfg(test)]
use stdarch_test::assert_instr;

// x32 wants to use a 32-bit address size, but asm! defaults to using the full
// register name (e.g. rax). We have to explicitly override the placeholder to
// use the 32-bit register name in that case.
#[cfg(target_pointer_width = "32")]
macro_rules! bt {
    ($inst:expr) => {
        concat!($inst, " {b}, ({p:e})")
    };
}
#[cfg(target_pointer_width = "64")]
macro_rules! bt {
    ($inst:expr) => {
        concat!($inst, " {b}, ({p})")
    };
}

/// Returns the bit in position `b` of the memory addressed by `p`.
#[inline]
#[cfg_attr(test, assert_instr(bt))]
#[stable(feature = "simd_x86_bittest", since = "1.55.0")]
pub unsafe fn _bittest64(p: *const i64, b: i64) -> u8 {
    let r: u8;
    asm!(
        bt!("btq"),
        "setc {r}",
        p = in(reg) p,
        b = in(reg) b,
        r = out(reg_byte) r,
        options(readonly, nostack, pure, att_syntax)
    );
    r
}

/// Returns the bit in position `b` of the memory addressed by `p`, then sets the bit to `1`.
#[inline]
#[cfg_attr(test, assert_instr(bts))]
#[stable(feature = "simd_x86_bittest", since = "1.55.0")]
pub unsafe fn _bittestandset64(p: *mut i64, b: i64) -> u8 {
    let r: u8;
    asm!(
        bt!("btsq"),
        "setc {r}",
        p = in(reg) p,
        b = in(reg) b,
        r = out(reg_byte) r,
        options(nostack, att_syntax)
    );
    r
}

/// Returns the bit in position `b` of the memory addressed by `p`, then resets that bit to `0`.
#[inline]
#[cfg_attr(test, assert_instr(btr))]
#[stable(feature = "simd_x86_bittest", since = "1.55.0")]
pub unsafe fn _bittestandreset64(p: *mut i64, b: i64) -> u8 {
    let r: u8;
    asm!(
        bt!("btrq"),
        "setc {r}",
        p = in(reg) p,
        b = in(reg) b,
        r = out(reg_byte) r,
        options(nostack, att_syntax)
    );
    r
}

/// Returns the bit in position `b` of the memory addressed by `p`, then inverts that bit.
#[inline]
#[cfg_attr(test, assert_instr(btc))]
#[stable(feature = "simd_x86_bittest", since = "1.55.0")]
pub unsafe fn _bittestandcomplement64(p: *mut i64, b: i64) -> u8 {
    let r: u8;
    asm!(
        bt!("btcq"),
        "setc {r}",
        p = in(reg) p,
        b = in(reg) b,
        r = out(reg_byte) r,
        options(nostack, att_syntax)
    );
    r
}

#[cfg(test)]
mod tests {
    use crate::core_arch::x86_64::*;

    #[test]
    #[cfg_attr(miri, ignore)] // Uses inline assembly
    fn test_bittest64() {
        unsafe {
            let a = 0b0101_0000i64;
            assert_eq!(_bittest64(&a as _, 4), 1);
            assert_eq!(_bittest64(&a as _, 5), 0);
        }
    }

    #[test]
    #[cfg_attr(miri, ignore)] // Uses inline assembly
    fn test_bittestandset64() {
        unsafe {
            let mut a = 0b0101_0000i64;
            assert_eq!(_bittestandset64(&mut a as _, 4), 1);
            assert_eq!(_bittestandset64(&mut a as _, 4), 1);
            assert_eq!(_bittestandset64(&mut a as _, 5), 0);
            assert_eq!(_bittestandset64(&mut a as _, 5), 1);
        }
    }

    #[test]
    #[cfg_attr(miri, ignore)] // Uses inline assembly
    fn test_bittestandreset64() {
        unsafe {
            let mut a = 0b0101_0000i64;
            assert_eq!(_bittestandreset64(&mut a as _, 4), 1);
            assert_eq!(_bittestandreset64(&mut a as _, 4), 0);
            assert_eq!(_bittestandreset64(&mut a as _, 5), 0);
            assert_eq!(_bittestandreset64(&mut a as _, 5), 0);
        }
    }

    #[test]
    #[cfg_attr(miri, ignore)] // Uses inline assembly
    fn test_bittestandcomplement64() {
        unsafe {
            let mut a = 0b0101_0000i64;
            assert_eq!(_bittestandcomplement64(&mut a as _, 4), 1);
            assert_eq!(_bittestandcomplement64(&mut a as _, 4), 0);
            assert_eq!(_bittestandcomplement64(&mut a as _, 4), 1);
            assert_eq!(_bittestandcomplement64(&mut a as _, 5), 0);
            assert_eq!(_bittestandcomplement64(&mut a as _, 5), 1);
        }
    }
}