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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
//! Constant-latency branchless algorithms for SIMD and conformance checking.
//!
//! This module provides branchless (zero-conditional-branch) implementations of common
//! bit manipulation and selection operations. All functions have constant latency
//! and avoid branch misprediction penalties. Functions are guaranteed to compile
//! to assembly with zero conditional jumps (verified via objdump analysis).
//!
//! # Feature Gating
//!
//! When `bcinr` feature is enabled, functions delegate to `bcinr` implementations
//! optimized for the target architecture. Otherwise, portable fallback implementations
//! are used, which are still constant-latency.
/// Branchless select: if condition is non-zero, return `true_val`, else `false_val`.
///
/// # Constant Latency
///
/// This function executes in constant time without conditional jumps.
/// The `condition` parameter is converted to a mask via bitwise operations.
///
/// # Assembly Properties
///
/// - x86-64: Uses `cmov` (conditional move) instead of `jz`/`jnz` jumps
/// - ARM64: Uses `csel` (conditional select) instead of `b.eq` branches
/// - WASM: Compiles to `select` opcode (no branches)
///
/// # Example
///
/// ```
/// # use wasm4pm::branchless::select_u64;
/// let x = 10u64;
/// let y = 5u64;
/// let cond = x < y; // false (0): 10 < 5 is false
/// let result = select_u64(cond as u64, y, x);
/// assert_eq!(result, x); // condition is 0, so returns false_val
/// ```
#[inline]
pub fn select_u64(condition: u64, true_val: u64, false_val: u64) -> u64 {
#[cfg(feature = "bcinr")]
{
bcinr::mask::select_u64(condition, true_val, false_val)
}
#[cfg(not(feature = "bcinr"))]
{
// Portable branchless select via bit manipulation.
// Convert condition to mask: non-zero -> all bits set, zero -> 0.
// Technique: ((x != 0) as u64).wrapping_neg() produces -1 (0xFFFF...FFFF) or 0.
let mask = ((condition != 0) as i64).wrapping_neg() as u64;
(true_val & mask) | (false_val & !mask)
}
}
/// Branchless select for u32 values.
///
/// See [`select_u64`] for detailed documentation.
///
/// # Example
///
/// ```
/// # use wasm4pm::branchless::select_u32;
/// let x = 5u32;
/// let y = 10u32;
/// let cond = x > y; // false (0)
/// let result = select_u32(cond as u32, y, x);
/// assert_eq!(result, x);
/// ```
#[inline]
pub fn select_u32(condition: u32, true_val: u32, false_val: u32) -> u32 {
#[cfg(feature = "bcinr")]
{
bcinr::mask::select_u32(condition, true_val, false_val)
}
#[cfg(not(feature = "bcinr"))]
{
// Portable branchless select via bit manipulation.
// Convert condition to mask: non-zero -> all bits set, zero -> 0.
let mask = ((condition != 0) as i32).wrapping_neg() as u32;
(true_val & mask) | (false_val & !mask)
}
}
/// Branchless blend of two u64 values using a bit mask.
///
/// Returns `(x & mask) | (y & !mask)`, selecting bits from `x` where
/// mask is set and from `y` where mask is unset.
///
/// # Constant Latency
///
/// Executes in a fixed number of logical operations regardless of input values.
/// Compile-time optimization may reduce this to inline bitwise operations.
///
/// # Example
///
/// ```
/// # use wasm4pm::branchless::blend;
/// let x = 0xFF00u64;
/// let y = 0x00FFu64;
/// let mask = 0xAAAAu64; // alternating bits
/// let result = blend(x, y, mask);
/// // result has bits from x where mask=1, from y where mask=0
/// ```
#[inline]
pub fn blend(x: u64, y: u64, mask: u64) -> u64 {
(x & mask) | (y & !mask)
}
/// Branchless population count (number of set bits) for u64.
///
/// # Constant Latency
///
/// On modern x86-64 (Nehalem+), compiles to a single `popcnt` instruction.
/// On older targets without `popcnt`, uses a loop-unrolled bit-counting algorithm.
///
/// # Example
///
/// ```
/// # use wasm4pm::branchless::popcount;
/// let x = 0b1010_1010u64;
/// assert_eq!(popcount(x), 4);
/// ```
#[inline]
pub fn popcount(x: u64) -> u32 {
// Portable popcount via Knuth's algorithm (constant latency for fixed bit width).
// Split into 32-bit halves and use Rust's built-in (which compiles to popcnt on modern targets).
let hi = (x >> 32) as u32;
let lo = x as u32;
hi.count_ones() + lo.count_ones()
}
/// Branchless count of leading zeros for u64.
///
/// # Constant Latency
///
/// On modern x86-64 (Lzcnt), compiles to a single instruction.
/// On older targets, uses a loop-unrolled bit-searching algorithm.
///
/// # Example
///
/// ```
/// # use wasm4pm::branchless::leading_zeros;
/// let x = 0x0000_0001u64;
/// assert_eq!(leading_zeros(x), 63);
/// ```
#[inline]
pub fn leading_zeros(x: u64) -> u32 {
// Portable leading zeros (Rust built-in; compiles to lzcnt on modern x86-64).
x.leading_zeros()
}
/// Branchless population count for u32.
///
/// See [`popcount`] for detailed documentation.
#[inline]
pub fn popcount_u32(x: u32) -> u32 {
#[cfg(feature = "bcinr")]
{
bcinr::int::popcount_u32(x)
}
#[cfg(not(feature = "bcinr"))]
{
x.count_ones()
}
}
/// Branchless count of leading zeros for u32.
///
/// See [`leading_zeros`] for detailed documentation.
#[inline]
pub fn leading_zeros_u32(x: u32) -> u32 {
x.leading_zeros()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_select_u64_true() {
let result = select_u64(1, 42u64, 0u64);
assert_eq!(result, 42);
}
#[test]
fn test_select_u64_false() {
let result = select_u64(0, 42u64, 99u64);
assert_eq!(result, 99);
}
#[test]
fn test_select_u32_true() {
let result = select_u32(1, 42u32, 0u32);
assert_eq!(result, 42);
}
#[test]
fn test_select_u32_false() {
let result = select_u32(0, 42u32, 99u32);
assert_eq!(result, 99);
}
#[test]
fn test_blend_basic() {
let x = 0xFFFFu64;
let y = 0x0000u64;
let mask = 0xFF00u64;
let result = blend(x, y, mask);
assert_eq!(result, 0xFF00u64);
}
#[test]
fn test_popcount_basic() {
assert_eq!(popcount(0b1010_1010u64), 4);
assert_eq!(popcount(0u64), 0);
assert_eq!(popcount(u64::MAX), 64);
}
#[test]
fn test_leading_zeros_basic() {
assert_eq!(leading_zeros(0u64), 64);
assert_eq!(leading_zeros(u64::MAX), 0);
assert_eq!(leading_zeros(0x0000_0001u64), 63);
}
#[test]
fn test_popcount_u32_basic() {
assert_eq!(popcount_u32(0b1010_1010u32), 4);
assert_eq!(popcount_u32(0u32), 0);
assert_eq!(popcount_u32(u32::MAX), 32);
}
#[test]
fn test_leading_zeros_u32_basic() {
assert_eq!(leading_zeros_u32(0u32), 32);
assert_eq!(leading_zeros_u32(u32::MAX), 0);
assert_eq!(leading_zeros_u32(0x0000_0001u32), 31);
}
}