use zisk_core::zisk_ops::ZiskOp;
pub const ADDS_X_ROW: usize = 3;
pub const NEG_HI: u64 = 0xFFFF_FFFF;
const LS_6_BITS: u64 = 0x3F;
const MASK_32: u64 = 0xFFFF_FFFF;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum AddShape {
Hi,
HiNeg,
Full,
}
#[inline(always)]
pub fn add_shape(a: u64, b: u64) -> AddShape {
let carry = ((a & MASK_32) + (b & MASK_32)) >> 32;
if (a >> 32) != 0 {
return AddShape::Full;
}
match b >> 32 {
0 if carry == 0 => AddShape::Hi,
NEG_HI if carry == 1 => AddShape::HiNeg,
_ => AddShape::Full,
}
}
pub fn opcode_is_shift(opcode: ZiskOp) -> bool {
match opcode {
ZiskOp::Sll
| ZiskOp::Srl
| ZiskOp::Sra
| ZiskOp::SllW
| ZiskOp::SrlW
| ZiskOp::SraW
| ZiskOp::Rol
| ZiskOp::RolW
| ZiskOp::Ror
| ZiskOp::RorW
| ZiskOp::Bclr
| ZiskOp::Bext
| ZiskOp::Binv
| ZiskOp::Bset => true,
ZiskOp::SignExtendB
| ZiskOp::SignExtendH
| ZiskOp::SignExtendW
| ZiskOp::Rev8
| ZiskOp::OrcB
| ZiskOp::Cpop
| ZiskOp::CpopW
| ZiskOp::Ctz
| ZiskOp::CtzW
| ZiskOp::Clz
| ZiskOp::ClzW
| ZiskOp::Pack
| ZiskOp::PackH
| ZiskOp::PackW => false,
_ => panic!("opcode_is_shift() got invalid opcode={opcode:?}"),
}
}
pub fn opcode_is_chain(opcode: ZiskOp) -> bool {
matches!(opcode, ZiskOp::Ctz | ZiskOp::CtzW)
}
pub fn opcode_is_chain_rev(opcode: ZiskOp) -> bool {
matches!(opcode, ZiskOp::Clz | ZiskOp::ClzW)
}
pub fn opcode_is_combine(opcode: ZiskOp) -> bool {
matches!(opcode, ZiskOp::Pack | ZiskOp::PackH | ZiskOp::PackW)
}
pub fn opcode_is_shift_word(opcode: ZiskOp) -> bool {
match opcode {
ZiskOp::SllW | ZiskOp::SrlW | ZiskOp::SraW | ZiskOp::RolW | ZiskOp::RorW => true,
ZiskOp::Sll
| ZiskOp::Srl
| ZiskOp::Sra
| ZiskOp::SignExtendB
| ZiskOp::SignExtendH
| ZiskOp::SignExtendW
| ZiskOp::Rev8
| ZiskOp::OrcB
| ZiskOp::Rol
| ZiskOp::Ror
| ZiskOp::Cpop
| ZiskOp::CpopW
| ZiskOp::Ctz
| ZiskOp::CtzW
| ZiskOp::Clz
| ZiskOp::ClzW
| ZiskOp::Pack
| ZiskOp::PackH
| ZiskOp::PackW
| ZiskOp::Bclr
| ZiskOp::Bext
| ZiskOp::Binv
| ZiskOp::Bset => false,
_ => panic!("opcode_is_shift_word() got invalid opcode={opcode:?}"),
}
}
#[inline(always)]
pub fn extension_requires_full(op: u8, a: u64, b: u64) -> bool {
let opcode = ZiskOp::try_from_code(op).expect("extension_requires_full(): invalid ZiskOp code");
if opcode_is_chain(opcode) || opcode_is_chain_rev(opcode) {
return true;
}
if opcode_is_shift(opcode) {
return b > LS_6_BITS;
}
if opcode_is_combine(opcode) {
return (a >> 32) != 0 || (b >> 32) != 0;
}
a != 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_shape_hi() {
assert_eq!(add_shape(0, 0), AddShape::Hi);
assert_eq!(add_shape(1, 2), AddShape::Hi);
assert_eq!(add_shape(0xFFFF_FFFE, 1), AddShape::Hi);
}
#[test]
fn add_shape_hi_carry_out_of_low_limb_is_not_hi() {
assert_eq!(add_shape(0xFFFF_FFFF, 1), AddShape::Full);
}
#[test]
fn add_shape_hi_neg() {
let minus_one = u64::MAX;
assert_eq!(add_shape(1, minus_one), AddShape::HiNeg);
assert_eq!(add_shape(0xFFFF_FFFF, minus_one), AddShape::HiNeg);
assert_eq!(add_shape(0, minus_one), AddShape::Full);
}
#[test]
fn add_shape_full_when_a_is_dirty() {
assert_eq!(add_shape(1 << 32, 0), AddShape::Full);
}
#[test]
fn add_shape_full_when_b_hi_is_neither_zero_nor_all_ones() {
assert_eq!(add_shape(0, 1 << 32), AddShape::Full);
}
#[test]
fn extension_chain_always_requires_full() {
for op in [ZiskOp::Ctz, ZiskOp::CtzW, ZiskOp::Clz, ZiskOp::ClzW] {
assert!(extension_requires_full(op.code(), 0, 0));
}
}
#[test]
fn extension_shift_requires_full_only_when_amount_is_dirty() {
let sll = ZiskOp::Sll.code();
assert!(!extension_requires_full(sll, 0x1234, 63));
assert!(extension_requires_full(sll, 0x1234, 64));
assert!(extension_requires_full(sll, 0x1234, 1 << 32));
}
#[test]
fn extension_combine_requires_full_only_when_a_high_limb_is_dirty() {
let pack = ZiskOp::Pack.code();
assert!(!extension_requires_full(pack, 0xFFFF_FFFF, 0xFFFF_FFFF));
assert!(extension_requires_full(pack, 1 << 32, 0));
assert!(extension_requires_full(pack, 0, 1 << 32));
}
#[test]
fn extension_single_source_requires_full_only_when_bus_a_is_set() {
let rev8 = ZiskOp::Rev8.code();
assert!(!extension_requires_full(rev8, 0, u64::MAX));
assert!(extension_requires_full(rev8, 1, u64::MAX));
}
}