Skip to main content

ab_riscv_interpreter/rv32/b/zbc/
rv32_zbc_helpers.rs

1//! Opaque helpers for RV32 Zbc extension
2
3#[inline(always)]
4#[doc(hidden)]
5#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
6pub fn clmul(a: u32, b: u32) -> u32 {
7    // TODO: Miri is excluded because corresponding intrinsic is not implemented there
8    cfg_select! {
9        all(not(miri), target_arch = "riscv32", target_feature = "zbkc") => {
10            // SAFETY: Compile-time checked for supported feature
11            unsafe { core::arch::riscv32::clmul(a as usize, b as usize) as u32 }
12        }
13        _ => {{
14            let result = clmul_internal(a, b);
15            result as u32
16        }}
17    }
18}
19
20#[inline(always)]
21#[doc(hidden)]
22#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
23pub fn clmulh(a: u32, b: u32) -> u32 {
24    // TODO: Miri is excluded because corresponding intrinsic is not implemented there
25    cfg_select! {
26        all(not(miri), target_arch = "riscv32", target_feature = "zbkc") => {
27            // SAFETY: Compile-time checked for supported feature
28            unsafe { core::arch::riscv32::clmulh(a as usize, b as usize) as u32 }
29        }
30        _ => {{
31            let result = clmul_internal(a, b);
32            (result >> 32) as u32
33        }}
34    }
35}
36
37#[inline(always)]
38#[doc(hidden)]
39#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
40pub fn clmulr(a: u32, b: u32) -> u32 {
41    // TODO: Miri is excluded because corresponding intrinsic is not implemented there
42    cfg_select! {
43        all(not(miri), target_arch = "riscv32", target_feature = "zbc") => {
44            // SAFETY: Compile-time checked for supported feature
45            unsafe { core::arch::riscv32::clmulr(a as usize, b as usize) as u32 }
46        }
47        _ => {{
48            let result = clmul_internal(a, b);
49            (result >> 31) as u32
50        }}
51    }
52}
53
54/// Carryless multiplication helper
55#[cfg(any(miri, not(all(target_arch = "riscv32", target_feature = "zbc"))))]
56#[inline(always)]
57#[doc(hidden)]
58#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
59fn clmul_internal(a: u32, b: u32) -> u64 {
60    let a = u64::from(a);
61    let b = u64::from(b);
62
63    cfg_select! {
64        // TODO: `llvm.aarch64.neon.pmull64` is not supported in Miri yet:
65        //  https://github.com/rust-lang/miri/issues/3172#issuecomment-3730602707
66        all(
67            not(miri), target_arch = "aarch64", target_feature = "neon", target_feature = "aes"
68        ) => {{
69            use core::arch::aarch64::vmull_p64;
70
71            // SAFETY: Compile-time checked for supported feature
72            // Only lower 32 bits of a and b are meaningful; result fits in 64 bits
73            unsafe { vmull_p64(a, b) as u64 }
74        }}
75        all(target_arch = "x86_64", target_feature = "pclmulqdq") => {{
76            use core::arch::x86_64::{__m128i, _mm_clmulepi64_si128, _mm_cvtsi64_si128};
77            use core::mem::transmute;
78
79            // SAFETY: Compile-time checked for supported feature
80            unsafe {
81                let result = transmute::<__m128i, u128>(_mm_clmulepi64_si128(
82                    _mm_cvtsi64_si128(a.cast_signed()),
83                    _mm_cvtsi64_si128(b.cast_signed()),
84                    0,
85                ));
86                result as u64
87            }
88        }}
89        _ => {{
90            // Generic implementation: inputs are at most 32 bits wide, result fits in 64 bits
91            let mut result = 0u64;
92            let mut b = b;
93            for i in 0..u32::BITS {
94                let bit = b & 1;
95                result ^= a.wrapping_shl(i) & (0u64.wrapping_sub(bit));
96                b >>= 1;
97            }
98            result
99        }}
100    }
101}