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