Skip to main content

ab_riscv_interpreter/rv32/b/zbc/
rv32_zbc_helpers.rs

1//! Opaque helpers for RV32 Zbc extension
2
3// TODO: Remove once https://github.com/rust-lang/rust-clippy/issues/17498 is resolved
4#![cfg_attr(
5    all(
6        any(miri, not(all(target_arch = "riscv32", target_feature = "zbc"))),
7        any(
8            all(
9                target_arch = "aarch64",
10                target_feature = "neon",
11                target_feature = "aes"
12            ),
13            all(target_arch = "x86_64", target_feature = "pclmulqdq")
14        ),
15        not(feature = "no-panic")
16    ),
17    expect(
18        clippy::items_after_statements,
19        reason = "https://github.com/rust-lang/rust-clippy/issues/17498"
20    )
21)]
22
23use crate::const_utils::ConstRange;
24use const_fn_specialization::const_fn_specialization;
25
26#[const_fn_specialization]
27#[inline(always)]
28#[doc(hidden)]
29#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
30pub fn clmul(a: u32, b: u32) -> u32 {
31    // TODO: Miri is excluded because corresponding intrinsic is not implemented there
32    cfg_select! {
33        all(not(miri), target_arch = "riscv32", target_feature = "zbkc") => {
34            // SAFETY: Compile-time checked for supported feature
35            unsafe { core::arch::riscv32::clmul(a as usize, b as usize) as u32 }
36        }
37        _ => clmul_internal(a, b) as u32,
38    }
39}
40
41#[const_fn_specialization]
42#[inline(always)]
43#[doc(hidden)]
44#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
45pub fn clmulh(a: u32, b: u32) -> u32 {
46    // TODO: Miri is excluded because corresponding intrinsic is not implemented there
47    cfg_select! {
48        all(not(miri), target_arch = "riscv32", target_feature = "zbkc") => {
49            // SAFETY: Compile-time checked for supported feature
50            unsafe { core::arch::riscv32::clmulh(a as usize, b as usize) as u32 }
51        }
52        _ => (clmul_internal(a, b) >> 32) as u32,
53    }
54}
55
56#[const_fn_specialization]
57#[inline(always)]
58#[doc(hidden)]
59#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
60pub fn clmulr(a: u32, b: u32) -> u32 {
61    // TODO: Miri is excluded because corresponding intrinsic is not implemented there
62    cfg_select! {
63        all(not(miri), target_arch = "riscv32", target_feature = "zbc") => {
64            // SAFETY: Compile-time checked for supported feature
65            unsafe { core::arch::riscv32::clmulr(a as usize, b as usize) as u32 }
66        }
67        _ => (clmul_internal(a, b) >> 31) as u32,
68    }
69}
70
71#[const_fn_specialization]
72#[inline(always)]
73#[doc(hidden)]
74#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
75pub const fn clmul(a: u32, b: u32) -> u32 {
76    clmul_internal_generic(u64::from(a), u64::from(b)) as u32
77}
78
79#[const_fn_specialization]
80#[inline(always)]
81#[doc(hidden)]
82#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
83pub const fn clmulh(a: u32, b: u32) -> u32 {
84    (clmul_internal_generic(u64::from(a), u64::from(b)) >> 32) as u32
85}
86
87#[const_fn_specialization]
88#[inline(always)]
89#[doc(hidden)]
90#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
91pub const fn clmulr(a: u32, b: u32) -> u32 {
92    (clmul_internal_generic(u64::from(a), u64::from(b)) >> 31) as u32
93}
94
95/// Carryless multiplication helper
96#[cfg(any(miri, not(all(target_arch = "riscv32", target_feature = "zbc"))))]
97#[inline(always)]
98#[doc(hidden)]
99#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
100fn clmul_internal(a: u32, b: u32) -> u64 {
101    let a = u64::from(a);
102    let b = u64::from(b);
103
104    cfg_select! {
105        all(
106            target_arch = "aarch64",
107            target_feature = "neon",
108            target_feature = "aes"
109        ) => {
110            use core::arch::aarch64::vmull_p64;
111
112            // SAFETY: Compile-time checked for supported feature
113            // Only lower 32 bits of a and b are meaningful; result fits in 64 bits
114            unsafe { vmull_p64(a, b) as u64 }
115        }
116        all(target_arch = "x86_64", target_feature = "pclmulqdq") => {
117            use core::arch::x86_64::{__m128i, _mm_clmulepi64_si128, _mm_cvtsi64_si128};
118            use core::mem::transmute;
119
120            // SAFETY: Compile-time checked for supported feature
121            unsafe {
122                let result = transmute::<__m128i, u128>(_mm_clmulepi64_si128(
123                    _mm_cvtsi64_si128(a.cast_signed()),
124                    _mm_cvtsi64_si128(b.cast_signed()),
125                    0,
126                ));
127                result as u64
128            }
129        }
130        _ => clmul_internal_generic(a, b),
131    }
132}
133
134/// Generic carryless multiplication of two 32-bit values widened to `u64`, result fits in 64 bits
135#[inline(always)]
136#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
137const fn clmul_internal_generic(a: u64, b: u64) -> u64 {
138    let mut result = 0u64;
139    let mut b = b;
140    for i in ConstRange::new(0, u32::BITS) {
141        let bit = b & 1;
142        result ^= a.wrapping_shl(i) & (0u64.wrapping_sub(bit));
143        b >>= 1;
144    }
145    result
146}