Skip to content

Commit 4086539

Browse files
authored
cmov: add cmovz/cmovnz-alike support for aarch64 (#744)
1 parent 23c8c46 commit 4086539

File tree

3 files changed

+49
-8
lines changed

3 files changed

+49
-8
lines changed

cmov/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "cmov"
33
description = """
44
Conditional move CPU intrinsics which are guaranteed to execute in
55
constant-time and not be rewritten as branches by the compiler.
6-
Provides wrappers for the CMOV family of instructions on x86/x86_64 CPUs.
6+
Provides wrappers for the CMOV family of instructions on x86/x86_64/aarch64 CPUs.
77
"""
88
version = "0.1.0"
99
authors = ["RustCrypto Developers"]

cmov/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
Conditional move CPU intrinsics which are guaranteed to execute in
1010
constant-time and not be rewritten as branches by the compiler.
1111

12-
Provides wrappers for the [CMOV family] of instructions on x86/x86_64 CPUs.
12+
Provides wrappers for the [CMOV family] of instructions on x86/x86_64 and AArch64 CPUs.
1313

1414
[Documentation][docs-link]
1515

@@ -36,8 +36,9 @@ lowerings, such as the [x86-cmov-conversion] pass.
3636
This crate provides guaranteed constant-time operation using inline assembly
3737
on the following CPU architectures:
3838

39-
- [x] `x86`
40-
- [x] `x86_64`
39+
- [x] `x86` (`CMOVZ`, `CMOVNZ`)
40+
- [x] `x86_64` (`CMOVZ`, CMOVNZ`)
41+
- [x] `aarch64` (`CSEL`)
4142

4243
On other target architectures, a "best effort" portable fallback implementation
4344
based on bitwise arithmetic is used instead. However, we cannot guarantee that

cmov/src/lib.rs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
)]
77
#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
88

9-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
9+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
1010
use core::arch::asm;
1111

1212
/// Move if zero.
@@ -28,6 +28,46 @@ pub fn cmovz(condition: usize, src: usize, dst: &mut usize) {
2828
}
2929
}
3030

31+
/// Move if zero.
32+
///
33+
/// Uses a `cmp` instruction to check if the given `condition` value is
34+
/// equal to zero, then calls `csel` to conditionally move
35+
/// `src` to `dst` when `condition` is equal to zero.
36+
#[cfg(any(target_arch = "aarch64"))]
37+
#[inline(always)]
38+
pub fn cmovz(condition: usize, src: usize, dst: &mut usize) {
39+
unsafe {
40+
asm! {
41+
"cmp {0}, 0",
42+
"csel {1}, {2}, {3}, EQ",
43+
in(reg) condition,
44+
inlateout(reg) *dst,
45+
in(reg) src,
46+
in(reg) *dst,
47+
};
48+
}
49+
}
50+
51+
/// Move if not zero.
52+
///
53+
/// Uses a `cmp` instruction to check if the given `condition` value is not
54+
/// equal to zero, then calls `csel` to conditionally move
55+
/// `src` to `dst` when `condition` is nonzero.
56+
#[cfg(any(target_arch = "aarch64"))]
57+
#[inline(always)]
58+
pub fn cmovnz(condition: usize, src: usize, dst: &mut usize) {
59+
unsafe {
60+
asm! {
61+
"cmp {0}, 0",
62+
"csel {1}, {2}, {3}, NE",
63+
in(reg) condition,
64+
inlateout(reg) *dst,
65+
in(reg) src,
66+
in(reg) *dst,
67+
};
68+
}
69+
}
70+
3171
/// Move if not zero.
3272
///
3373
/// Uses a `test` instruction to check if the given `condition` value is not
@@ -52,7 +92,7 @@ pub fn cmovnz(condition: usize, src: usize, dst: &mut usize) {
5292
/// This implementation is based on portable bitwise arithmetic but cannot
5393
/// guarantee that the resulting generated assembly is free of branch
5494
/// instructions.
55-
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
95+
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
5696
#[inline(never)]
5797
pub fn cmovz(condition: usize, src: usize, dst: &mut usize) {
5898
let mask = (1 ^ is_non_zero(condition)).wrapping_sub(1);
@@ -64,7 +104,7 @@ pub fn cmovz(condition: usize, src: usize, dst: &mut usize) {
64104
/// This implementation is based on portable bitwise arithmetic but cannot
65105
/// guarantee that the resulting generated assembly is free of branch
66106
/// instructions.
67-
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
107+
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
68108
#[inline(never)]
69109
pub fn cmovnz(condition: usize, src: usize, dst: &mut usize) {
70110
let mask = is_non_zero(condition).wrapping_sub(1);
@@ -76,7 +116,7 @@ pub fn cmovnz(condition: usize, src: usize, dst: &mut usize) {
76116
/// # Returns
77117
/// - `condition` is zero: `0`
78118
/// - `condition` is non-zero: `1`
79-
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
119+
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
80120
#[inline(always)]
81121
fn is_non_zero(condition: usize) -> usize {
82122
const SHIFT_BITS: usize = core::mem::size_of::<usize>() - 1;

0 commit comments

Comments
 (0)