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::instructions::rv64::b::zbc::Rv64ZbcInstruction;
10use ab_riscv_primitives::registers::general_purpose::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                // TODO: Miri is excluded because corresponding intrinsic is not implemented there
34                let value = cfg_select! {
35                    all(not(miri), target_arch = "riscv64", target_feature = "zbkc") => {
36                        core::arch::riscv64::clmul(a as usize, b as usize) as u64
37                    }
38                    _ => {{
39                        let result = clmul_internal(a, b);
40                        result as u64
41                    }}
42                };
43
44                state.regs.write(rd, value);
45            }
46            Self::Clmulh { rd, rs1, rs2 } => {
47                let a = state.regs.read(rs1);
48                let b = state.regs.read(rs2);
49
50                // TODO: Miri is excluded because corresponding intrinsic is not implemented there
51                let value = cfg_select! {
52                    all(not(miri), target_arch = "riscv64", target_feature = "zbkc") => {
53                        core::arch::riscv64::clmulh(a as usize, b as usize) as u64
54                    }
55                    _ => {{
56                        let result = clmul_internal(a, b);
57                        (result >> 64) as u64
58                    }}
59                };
60
61                state.regs.write(rd, value);
62            }
63            Self::Clmulr { rd, rs1, rs2 } => {
64                let a = state.regs.read(rs1);
65                let b = state.regs.read(rs2);
66
67                // TODO: Miri is excluded because corresponding intrinsic is not implemented there
68                let value = cfg_select! {
69                    all(not(miri), target_arch = "riscv64", target_feature = "zbc") => {
70                        core::arch::riscv64::clmulr(a as usize, b as usize) as u64
71                    }
72                    _ => {{
73                        let result = clmul_internal(a, b);
74                        (result >> 1) as u64
75                    }}
76                };
77
78                state.regs.write(rd, value);
79            }
80        }
81
82        Ok(ControlFlow::Continue(()))
83    }
84}
85
86/// Carryless multiplication helper, only useful for importing when inheriting instructions.
87///
88/// NOTE: This function is conditionally-compiled, make sure to copy the same conditions downstream.
89#[cfg(any(miri, not(all(target_arch = "riscv64", target_feature = "zbc"))))]
90#[inline(always)]
91pub fn clmul_internal(a: u64, b: u64) -> u128 {
92    cfg_select! {
93        // TODO: `llvm.aarch64.neon.pmull64` is not supported in Miri yet:
94        //  https://github.com/rust-lang/miri/issues/3172#issuecomment-3730602707
95        all(
96            not(miri), target_arch = "aarch64", target_feature = "neon", target_feature = "aes"
97        ) => {{
98            use core::arch::aarch64::vmull_p64;
99
100            // SAFETY: Necessary target features enabled
101            unsafe { vmull_p64(a, b) }
102        }}
103        all(target_arch = "x86_64", target_feature = "pclmulqdq") => {{
104            use core::arch::x86_64::{__m128i, _mm_clmulepi64_si128, _mm_cvtsi64_si128};
105            use core::mem::transmute;
106
107            // SAFETY: Necessary target features enabled, `__m128i` and `u128` have the same memory
108            // layout
109            unsafe {
110                transmute::<__m128i, u128>(_mm_clmulepi64_si128(
111                    _mm_cvtsi64_si128(a.cast_signed()),
112                    _mm_cvtsi64_si128(b.cast_signed()),
113                    0,
114                ))
115            }
116        }}
117        _ => {{
118            // Generic implementation
119            let mut result = 0u128;
120            let a = a as u128;
121            let mut b = b;
122            for i in 0..u64::BITS {
123                let bit = (b & 1) as u128;
124                result ^= a.wrapping_shl(i) & (0u128.wrapping_sub(bit));
125                b >>= 1;
126            }
127            result
128        }}
129    }
130}