Skip to main content

ab_riscv_interpreter/rv64/b/
zbc.rs

1//! RV64 Zbc extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::rv64::Rv64InterpreterState;
7use crate::{ExecutableInstruction, ExecutionError};
8use ab_riscv_macros::instruction_execution;
9use ab_riscv_primitives::instruction::rv64::b::zbc::Rv64ZbcInstruction;
10use ab_riscv_primitives::registers::Register;
11use core::ops::ControlFlow;
12
13#[instruction_execution]
14impl<Reg, Memory, PC, InstructionHandler, CustomError>
15    ExecutableInstruction<
16        Rv64InterpreterState<Reg, Memory, PC, InstructionHandler, CustomError>,
17        CustomError,
18    > for Rv64ZbcInstruction<Reg>
19where
20    Reg: Register<Type = u64>,
21    [(); Reg::N]:,
22{
23    #[inline(always)]
24    fn execute(
25        self,
26        state: &mut Rv64InterpreterState<Reg, Memory, PC, InstructionHandler, CustomError>,
27    ) -> Result<ControlFlow<()>, ExecutionError<Reg::Type, Self, CustomError>> {
28        match self {
29            Self::Clmul { rd, rs1, rs2 } => {
30                let a = state.regs.read(rs1);
31                let b = state.regs.read(rs2);
32
33                #[cfg(all(not(miri), target_arch = "riscv64", target_feature = "zbkc"))]
34                let value = core::arch::riscv64::clmul(a as usize, b as usize) as u64;
35
36                #[cfg(not(all(not(miri), target_arch = "riscv64", target_feature = "zbkc")))]
37                let value = {
38                    let result = clmul_internal(a, b);
39                    result as u64
40                };
41
42                state.regs.write(rd, value);
43            }
44            Self::Clmulh { rd, rs1, rs2 } => {
45                let a = state.regs.read(rs1);
46                let b = state.regs.read(rs2);
47
48                #[cfg(all(not(miri), target_arch = "riscv64", target_feature = "zbkc"))]
49                let value = core::arch::riscv64::clmulh(a as usize, b as usize) as u64;
50
51                #[cfg(not(all(not(miri), target_arch = "riscv64", target_feature = "zbkc")))]
52                let value = {
53                    let result = clmul_internal(a, b);
54                    (result >> 64) as u64
55                };
56
57                state.regs.write(rd, value);
58            }
59            Self::Clmulr { rd, rs1, rs2 } => {
60                let a = state.regs.read(rs1);
61                let b = state.regs.read(rs2);
62
63                #[cfg(all(not(miri), target_arch = "riscv64", target_feature = "zbc"))]
64                let value = core::arch::riscv64::clmulr(a as usize, b as usize) as u64;
65
66                #[cfg(not(all(not(miri), target_arch = "riscv64", target_feature = "zbc")))]
67                let value = {
68                    let result = clmul_internal(a, b);
69                    (result >> 1) as u64
70                };
71
72                state.regs.write(rd, value);
73            }
74        }
75
76        Ok(ControlFlow::Continue(()))
77    }
78}
79
80/// Carryless multiplication helper, only useful for importing when inheriting instructions.
81///
82/// NOTE: This function is conditionally-compiled, make sure to copy the same conditions downstream.
83#[cfg(any(miri, not(all(target_arch = "riscv64", target_feature = "zbc"))))]
84#[inline(always)]
85pub fn clmul_internal(a: u64, b: u64) -> u128 {
86    // TODO: `llvm.aarch64.neon.pmull64` is not supported in Miri yet:
87    //  https://github.com/rust-lang/miri/issues/3172#issuecomment-3730602707
88    #[cfg(all(
89        not(miri),
90        target_arch = "aarch64",
91        target_feature = "neon",
92        target_feature = "aes"
93    ))]
94    {
95        use core::arch::aarch64::vmull_p64;
96
97        // SAFETY: Necessary target features enabled
98        unsafe { vmull_p64(a, b) }
99    }
100
101    #[cfg(all(target_arch = "x86_64", target_feature = "pclmulqdq"))]
102    {
103        use core::arch::x86_64::{__m128i, _mm_clmulepi64_si128, _mm_cvtsi64_si128};
104        use core::mem::transmute;
105
106        // SAFETY: Necessary target features enabled, `__m128i` and `u128` have the same memory
107        // layout
108        unsafe {
109            transmute::<__m128i, u128>(_mm_clmulepi64_si128(
110                _mm_cvtsi64_si128(a.cast_signed()),
111                _mm_cvtsi64_si128(b.cast_signed()),
112                0,
113            ))
114        }
115    }
116
117    #[cfg(not(any(
118        all(
119            not(miri),
120            target_arch = "aarch64",
121            target_feature = "neon",
122            target_feature = "aes"
123        ),
124        all(target_arch = "x86_64", target_feature = "pclmulqdq")
125    )))]
126    {
127        // Generic implementation
128        let mut result = 0u128;
129        let a = a as u128;
130        let mut b = b;
131        for i in 0..u64::BITS {
132            let bit = (b & 1) as u128;
133            result ^= a.wrapping_shl(i) & (0u128.wrapping_sub(bit));
134            b >>= 1;
135        }
136        result
137    }
138}