Skip to content

Commit fc58772

Browse files
committed
[WIP] cmov: fix aarch64 ASM bug; add regression test
The implementation was using the wrong register type when invoking `csel` for `u64` (`:w` which is 32-bit, should use `:x` for 64-bit) This captures the bug in a regression test and fixes it.
1 parent a164fcb commit fc58772

File tree

2 files changed

+79
-17
lines changed

2 files changed

+79
-17
lines changed

cmov/src/aarch64.rs

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use crate::{Cmov, CmovEq, Condition};
22
use core::arch::asm;
33

4+
/// Conditional select
45
macro_rules! csel {
5-
($csel:expr, $dst:expr, $src:expr, $condition:expr) => {
6+
($cmp:expr, $csel:expr, $dst:expr, $src:expr, $condition:expr) => {
67
unsafe {
78
asm! {
89
"cmp {0:w}, 0",
@@ -17,13 +18,14 @@ macro_rules! csel {
1718
};
1819
}
1920

20-
macro_rules! csel_eq {
21-
($instruction:expr, $lhs:expr, $rhs:expr, $condition:expr, $dst:expr) => {
21+
/// Conditional select-based equality test
22+
macro_rules! cseleq {
23+
($eor:expr, $cmp:expr, $instruction:expr, $lhs:expr, $rhs:expr, $condition:expr, $dst:expr) => {
2224
let mut tmp = *$dst as u16;
2325
unsafe {
2426
asm! {
25-
"eor {0:w}, {1:w}, {2:w}",
26-
"cmp {0:w}, 0",
27+
$eor,
28+
$cmp,
2729
$instruction,
2830
out(reg) _,
2931
in(reg) *$lhs,
@@ -39,74 +41,118 @@ macro_rules! csel_eq {
3941
};
4042
}
4143

44+
/// Conditional select using 32-bit `:w` registers
45+
macro_rules! csel32 {
46+
($csel:expr, $dst:expr, $src:expr, $condition:expr) => {
47+
csel!("cmp {0:w}, 0", $csel, $dst, $src, $condition)
48+
};
49+
}
50+
51+
/// Conditional select using 64-bit `:x` registers
52+
macro_rules! csel64 {
53+
($csel:expr, $dst:expr, $src:expr, $condition:expr) => {
54+
csel!("cmp {0:x}, 0", $csel, $dst, $src, $condition)
55+
};
56+
}
57+
58+
/// Conditional select equality test using 32-bit `:w` registers
59+
macro_rules! cseleq32 {
60+
($instruction:expr, $lhs:expr, $rhs:expr, $condition:expr, $dst:expr) => {
61+
cseleq!(
62+
"eor {0:w}, {1:w}, {2:w}",
63+
"cmp {0:w}, 0",
64+
$instruction,
65+
$lhs,
66+
$rhs,
67+
$condition,
68+
$dst
69+
)
70+
};
71+
}
72+
73+
/// Conditional select equality test using 64-bit `:w` registers
74+
macro_rules! cseleq64 {
75+
($instruction:expr, $lhs:expr, $rhs:expr, $condition:expr, $dst:expr) => {
76+
cseleq!(
77+
"eor {0:x}, {1:x}, {2:x}",
78+
"cmp {0:x}, 0",
79+
$instruction,
80+
$lhs,
81+
$rhs,
82+
$condition,
83+
$dst
84+
)
85+
};
86+
}
87+
4288
impl Cmov for u16 {
4389
#[inline]
4490
fn cmovnz(&mut self, value: &Self, condition: Condition) {
45-
csel!("csel {1:w}, {2:w}, {3:w}, NE", self, value, condition);
91+
csel32!("csel {1:w}, {2:w}, {3:w}, NE", self, value, condition);
4692
}
4793

4894
#[inline]
4995
fn cmovz(&mut self, value: &Self, condition: Condition) {
50-
csel!("csel {1:w}, {2:w}, {3:w}, EQ", self, value, condition);
96+
csel32!("csel {1:w}, {2:w}, {3:w}, EQ", self, value, condition);
5197
}
5298
}
5399

54100
impl CmovEq for u16 {
55101
#[inline]
56102
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
57-
csel_eq!("csel {3:w}, {4:w}, {5:w}, NE", self, rhs, input, output);
103+
cseleq32!("csel {3:w}, {4:w}, {5:w}, NE", self, rhs, input, output);
58104
}
59105

60106
#[inline]
61107
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
62-
csel_eq!("csel {3:w}, {4:w}, {5:w}, EQ", self, rhs, input, output);
108+
cseleq32!("csel {3:w}, {4:w}, {5:w}, EQ", self, rhs, input, output);
63109
}
64110
}
65111

66112
impl Cmov for u32 {
67113
#[inline]
68114
fn cmovnz(&mut self, value: &Self, condition: Condition) {
69-
csel!("csel {1:w}, {2:w}, {3:w}, NE", self, value, condition);
115+
csel32!("csel {1:w}, {2:w}, {3:w}, NE", self, value, condition);
70116
}
71117

72118
#[inline]
73119
fn cmovz(&mut self, value: &Self, condition: Condition) {
74-
csel!("csel {1:w}, {2:w}, {3:w}, EQ", self, value, condition);
120+
csel32!("csel {1:w}, {2:w}, {3:w}, EQ", self, value, condition);
75121
}
76122
}
77123

78124
impl CmovEq for u32 {
79125
#[inline]
80126
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
81-
csel_eq!("csel {3:w}, {4:w}, {5:w}, NE", self, rhs, input, output);
127+
cseleq32!("csel {3:w}, {4:w}, {5:w}, NE", self, rhs, input, output);
82128
}
83129

84130
#[inline]
85131
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
86-
csel_eq!("csel {3:w}, {4:w}, {5:w}, EQ", self, rhs, input, output);
132+
cseleq32!("csel {3:w}, {4:w}, {5:w}, EQ", self, rhs, input, output);
87133
}
88134
}
89135

90136
impl Cmov for u64 {
91137
#[inline]
92138
fn cmovnz(&mut self, value: &Self, condition: Condition) {
93-
csel!("csel {1:x}, {2:x}, {3:x}, NE", self, value, condition);
139+
csel64!("csel {1:x}, {2:x}, {3:x}, NE", self, value, condition);
94140
}
95141

96142
#[inline]
97143
fn cmovz(&mut self, value: &Self, condition: Condition) {
98-
csel!("csel {1:x}, {2:x}, {3:x}, EQ", self, value, condition);
144+
csel64!("csel {1:x}, {2:x}, {3:x}, EQ", self, value, condition);
99145
}
100146
}
101147

102148
impl CmovEq for u64 {
103149
#[inline]
104150
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
105-
csel_eq!("csel {3:w}, {4:w}, {5:w}, NE", self, rhs, input, output);
151+
cseleq64!("csel {3:x}, {4:x}, {5:x}, NE", self, rhs, input, output);
106152
}
107153

108154
#[inline]
109155
fn cmoveq(&self, rhs: &Self, input: Condition, output: &mut Condition) {
110-
csel_eq!("csel {3:w}, {4:w}, {5:w}, EQ", self, rhs, input, output);
156+
cseleq64!("csel {3:x}, {4:x}, {5:x}, EQ", self, rhs, input, output);
111157
}
112158
}

cmov/tests/regression.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//! Tests for previous bugs in the implementation.
2+
3+
// TODO(tarcieri): known to be broken on PPC32. See RustCrypto/utils#1298
4+
#![cfg(not(target_arch = "powerpc"))]
5+
6+
use cmov::CmovEq;
7+
8+
#[test]
9+
fn u64_cmoveq() {
10+
let n = 0x8200_0000_0000_0000u64;
11+
let mut cond = 0u8;
12+
n.cmoveq(&0, 1u8, &mut cond);
13+
14+
// 0x8200_0000_0000_0000 is not equal to 0
15+
assert_eq!(cond, 0);
16+
}

0 commit comments

Comments
 (0)