Skip to content

Commit 8b33b63

Browse files
authored
cmov: extract portable testeq* and testne* functions (#1340)
More small functions that are good candidates for optimized assembly implementations
1 parent 66db811 commit 8b33b63

File tree

1 file changed

+78
-14
lines changed

1 file changed

+78
-14
lines changed

cmov/src/portable.rs

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ impl Cmov for u32 {
5353
impl CmovEq for u32 {
5454
#[inline]
5555
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
56-
let ne = testnz32(self ^ rhs) as u8;
56+
let ne = testne32(*self, *rhs);
5757
output.cmovnz(&input, ne);
5858
}
5959

6060
#[inline]
6161
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
62-
let ne = testnz32(self ^ rhs) as u8;
63-
output.cmovnz(&input, ne ^ 1);
62+
let eq = testeq32(*self, *rhs);
63+
output.cmovnz(&input, eq);
6464
}
6565
}
6666

@@ -81,41 +81,105 @@ impl Cmov for u64 {
8181
impl CmovEq for u64 {
8282
#[inline]
8383
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
84-
let ne = testnz64(self ^ rhs) as u8;
84+
let ne = testne64(*self, *rhs);
8585
output.cmovnz(&input, ne);
8686
}
8787

8888
#[inline]
8989
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
90-
let ne = testnz64(self ^ rhs) as u8;
91-
output.cmovnz(&input, ne ^ 1);
90+
let eq = testeq64(*self, *rhs);
91+
output.cmovnz(&input, eq);
9292
}
9393
}
9494

95+
/// Returns `1` if `x` is equal to `y`, otherwise returns `0` (32-bit version)
96+
fn testeq32(x: u32, y: u32) -> Condition {
97+
testne32(x, y) ^ 1
98+
}
99+
100+
/// Returns `1` if `x` is equal to `y`, otherwise returns `0` (64-bit version)
101+
fn testeq64(x: u64, y: u64) -> Condition {
102+
testne64(x, y) ^ 1
103+
}
104+
105+
/// Returns `0` if `x` is equal to `y`, otherwise returns `1` (32-bit version)
106+
fn testne32(x: u32, y: u32) -> Condition {
107+
testnz32(x ^ y) as Condition
108+
}
109+
110+
/// Returns `0` if `x` is equal to `y`, otherwise returns `1` (64-bit version)
111+
fn testne64(x: u64, y: u64) -> Condition {
112+
testnz64(x ^ y) as Condition
113+
}
114+
95115
/// Returns `0` if `x` is `0`, otherwise returns `1` (32-bit version)
96-
pub fn testnz32(mut x: u32) -> u32 {
97-
x |= x.wrapping_neg();
98-
core::hint::black_box(x >> (u32::BITS - 1))
116+
fn testnz32(mut x: u32) -> u32 {
117+
x |= x.wrapping_neg(); // MSB now set if non-zero
118+
core::hint::black_box(x >> (u32::BITS - 1)) // Extract MSB
99119
}
100120

101121
/// Returns `0` if `x` is `0`, otherwise returns `1` (64-bit version)
102-
pub fn testnz64(mut x: u64) -> u64 {
103-
x |= x.wrapping_neg();
104-
core::hint::black_box(x >> (u64::BITS - 1))
122+
fn testnz64(mut x: u64) -> u64 {
123+
x |= x.wrapping_neg(); // MSB now set if non-zero
124+
core::hint::black_box(x >> (u64::BITS - 1)) // Extract MSB
105125
}
106126

107127
/// Return a [`u32::MAX`] mask if `condition` is non-zero, otherwise return zero for a zero input.
108-
pub fn masknz32(condition: Condition) -> u32 {
128+
fn masknz32(condition: Condition) -> u32 {
109129
testnz32(condition as u32).wrapping_neg()
110130
}
111131

112132
/// Return a [`u64::MAX`] mask if `condition` is non-zero, otherwise return zero for a zero input.
113-
pub fn masknz64(condition: Condition) -> u64 {
133+
fn masknz64(condition: Condition) -> u64 {
114134
testnz64(condition as u64).wrapping_neg()
115135
}
116136

117137
#[cfg(test)]
118138
mod tests {
139+
#[test]
140+
fn testeq32() {
141+
assert_eq!(super::testeq32(0, 0), 1);
142+
assert_eq!(super::testeq32(1, 0), 0);
143+
assert_eq!(super::testeq32(0, 1), 0);
144+
assert_eq!(super::testeq32(1, 1), 1);
145+
assert_eq!(super::testeq32(u32::MAX, 1), 0);
146+
assert_eq!(super::testeq32(1, u32::MAX), 0);
147+
assert_eq!(super::testeq32(u32::MAX, u32::MAX), 1);
148+
}
149+
150+
#[test]
151+
fn testeq64() {
152+
assert_eq!(super::testeq64(0, 0), 1);
153+
assert_eq!(super::testeq64(1, 0), 0);
154+
assert_eq!(super::testeq64(0, 1), 0);
155+
assert_eq!(super::testeq64(1, 1), 1);
156+
assert_eq!(super::testeq64(u64::MAX, 1), 0);
157+
assert_eq!(super::testeq64(1, u64::MAX), 0);
158+
assert_eq!(super::testeq64(u64::MAX, u64::MAX), 1);
159+
}
160+
161+
#[test]
162+
fn testne32() {
163+
assert_eq!(super::testne32(0, 0), 0);
164+
assert_eq!(super::testne32(1, 0), 1);
165+
assert_eq!(super::testne32(0, 1), 1);
166+
assert_eq!(super::testne32(1, 1), 0);
167+
assert_eq!(super::testne32(u32::MAX, 1), 1);
168+
assert_eq!(super::testne32(1, u32::MAX), 1);
169+
assert_eq!(super::testne32(u32::MAX, u32::MAX), 0);
170+
}
171+
172+
#[test]
173+
fn testne64() {
174+
assert_eq!(super::testne64(0, 0), 0);
175+
assert_eq!(super::testne64(1, 0), 1);
176+
assert_eq!(super::testne64(0, 1), 1);
177+
assert_eq!(super::testne64(1, 1), 0);
178+
assert_eq!(super::testne64(u64::MAX, 1), 1);
179+
assert_eq!(super::testne64(1, u64::MAX), 1);
180+
assert_eq!(super::testne64(u64::MAX, u64::MAX), 0);
181+
}
182+
119183
#[test]
120184
fn testnz32() {
121185
assert_eq!(super::testnz32(0), 0);

0 commit comments

Comments
 (0)