Skip to main content

ab_riscv_interpreter/rv32/zk/zbkb/
rv32_zbkb_helpers.rs

1//! Opaque helpers for RV32 Zbkb extension
2
3use const_fn_specialization::const_fn_specialization;
4
5#[const_fn_specialization]
6#[inline(always)]
7#[doc(hidden)]
8#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
9pub fn zip(src: u32) -> u32 {
10    // TODO: Miri is excluded because corresponding intrinsic is not implemented there
11    cfg_select! {
12        all(not(miri), target_arch = "riscv32", target_feature = "zbkb") => {
13            // SAFETY: Compile-time checked for supported feature
14            unsafe { core::arch::riscv32::zip(src) }
15        }
16        _ => zip_generic(src),
17    }
18}
19
20#[const_fn_specialization]
21#[inline(always)]
22#[doc(hidden)]
23#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
24pub const fn zip(src: u32) -> u32 {
25    zip_generic(src)
26}
27
28/// Spread each 16-bit half into alternating bits.
29#[inline(always)]
30#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
31const fn zip_generic(src: u32) -> u32 {
32    // Classic SWAR interleave for 16-bit -> 32-bit Morton.
33    #[inline(always)]
34    const fn spread(mut x: u32) -> u32 {
35        x = (x | (x << 8u8)) & 0x00FF_00FF;
36        x = (x | (x << 4u8)) & 0x0F0F_0F0F;
37        x = (x | (x << 2u8)) & 0x3333_3333;
38        (x | (x << 1u8)) & 0x5555_5555
39    }
40
41    let lo = src & 0x0000_FFFF;
42    let hi = src >> 16u8;
43
44    spread(lo) | (spread(hi) << 1u8)
45}
46
47#[const_fn_specialization]
48#[inline(always)]
49#[doc(hidden)]
50#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
51pub fn unzip(src: u32) -> u32 {
52    // TODO: Miri is excluded because corresponding intrinsic is not implemented there
53    cfg_select! {
54        all(not(miri), target_arch = "riscv32", target_feature = "zbkb") => {
55            // SAFETY: Compile-time checked for supported feature
56            unsafe { core::arch::riscv32::unzip(src) }
57        }
58        _ => unzip_generic(src),
59    }
60}
61
62#[const_fn_specialization]
63#[inline(always)]
64#[doc(hidden)]
65#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
66pub const fn unzip(src: u32) -> u32 {
67    unzip_generic(src)
68}
69
70/// Gather alternating bits of both halves back together, inverse of [`zip_generic()`].
71#[inline(always)]
72#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
73const fn unzip_generic(src: u32) -> u32 {
74    #[inline(always)]
75    const fn compact(mut x: u32) -> u32 {
76        x &= 0x5555_5555;
77        x = (x | (x >> 1u8)) & 0x3333_3333;
78        x = (x | (x >> 2u8)) & 0x0F0F_0F0F;
79        x = (x | (x >> 4u8)) & 0x00FF_00FF;
80        (x | (x >> 8u8)) & 0x0000_FFFF
81    }
82
83    let lo = compact(src);
84    let hi = compact(src >> 1u8);
85    lo | (hi << 16u8)
86}