use super::*;
use crate::merge_u4;
use crate::split_as_u4;
use crate::Condition;
use crate::OpcodeVariant;
use crate::CONDITIONAL_BITS_SHIFT;
use crate::CONDITIONAL_BITS_WIDTH;
use crate::DST_REGS_SHIFT;
use crate::OPCODES_TABLE;
use crate::OPCODES_TABLE_WIDTH;
use crate::SRC_REGS_SHIFT;
use crate::{NOP_OPCODE_VARIANT, PANIC_OPCODE_VARIANT};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct EncodingModeProduction;
impl EncodingModeProduction {
const IMM0_SHIFT: u32 = 32;
const IMM1_SHIFT: u32 = 48;
const VARIANT_AND_CONDITION_BITS: usize = 16;
const VARIANT_MASK: u64 = (1u64 << OPCODES_TABLE_WIDTH) - 1;
const CONDITION_MASK: u64 = ((1u64 << CONDITIONAL_BITS_WIDTH) - 1) << CONDITIONAL_BITS_SHIFT;
const UNUSED_GAP_RAW_MASK: u64 = (1u64
<< (Self::VARIANT_AND_CONDITION_BITS - OPCODES_TABLE_WIDTH - CONDITIONAL_BITS_WIDTH))
- 1;
const UNUSED_BITS_MASK: u64 = Self::UNUSED_GAP_RAW_MASK << OPCODES_TABLE_WIDTH;
pub fn variant_and_condition_from_u64_word(
raw_value: u64,
) -> (OpcodeVariant, Condition, usize) {
let variant_bits = raw_value & Self::VARIANT_MASK;
let opcode_variant = OPCODES_TABLE[variant_bits as usize];
let condition_bits = (raw_value & Self::CONDITION_MASK) >> CONDITIONAL_BITS_SHIFT;
let condition = Condition::materialize_variant(condition_bits as usize);
(opcode_variant, condition, variant_bits as usize)
}
}
impl VmEncodingMode<8> for EncodingModeProduction {
type PcOrImm = u16;
type IntegerRepresentation = u64;
fn nop_encoding() -> Self::IntegerRepresentation {
let variant = *NOP_OPCODE_VARIANT;
let full_nop_opcode = DecodedOpcode::<8, Self> {
variant,
condition: Condition::Always,
src0_reg_idx: 0,
src1_reg_idx: 0,
dst0_reg_idx: 0,
dst1_reg_idx: 0,
imm_0: Self::PcOrImm::from_u64_clipped(0),
imm_1: Self::PcOrImm::from_u64_clipped(0),
};
full_nop_opcode.serialize_as_integer()
}
fn exception_revert_encoding() -> Self::IntegerRepresentation {
let variant = *PANIC_OPCODE_VARIANT;
let full_nop_opcode = DecodedOpcode::<8, Self> {
variant,
condition: Condition::Always,
src0_reg_idx: 0,
src1_reg_idx: 0,
dst0_reg_idx: 0,
dst1_reg_idx: 0,
imm_0: Self::PcOrImm::from_u64_clipped(0),
imm_1: Self::PcOrImm::from_u64_clipped(0),
};
full_nop_opcode.serialize_as_integer()
}
fn parse_preliminary_variant_and_absolute_number(
integer_representaiton: Self::IntegerRepresentation,
) -> (DecodedOpcode<8, Self>, VariantMonotonicNumber) {
let raw_value = integer_representaiton;
let (variant, condition, variant_bits) =
Self::variant_and_condition_from_u64_word(raw_value);
let src_byte = (raw_value >> SRC_REGS_SHIFT) as u8;
let dst_byte = (raw_value >> DST_REGS_SHIFT) as u8;
let imm_0 = (raw_value >> Self::IMM0_SHIFT) as u16;
let imm_1 = (raw_value >> Self::IMM1_SHIFT) as u16;
let (src0_reg_idx, src1_reg_idx) = split_as_u4(src_byte);
let (dst0_reg_idx, dst1_reg_idx) = split_as_u4(dst_byte);
let new = DecodedOpcode {
variant,
condition,
src0_reg_idx,
src1_reg_idx,
dst0_reg_idx,
dst1_reg_idx,
imm_0,
imm_1,
};
(new, VariantMonotonicNumber::from_usize(variant_bits))
}
fn encode_as_integer(opcode: &DecodedOpcode<8, Self>) -> Self::IntegerRepresentation {
let mut encoding = 0u64;
let variant_bits = if let Some(variant_bits) = crate::OPCODE_TO_CANONICAL_INDEX_LOOKUP_MAP
.get(&opcode.variant)
.copied()
{
variant_bits
} else {
panic!("Unknown variant {}", opcode.variant);
};
assert!(variant_bits.next_power_of_two().trailing_zeros() as usize <= OPCODES_TABLE_WIDTH);
encoding |= variant_bits as u64;
let condition = opcode.condition.variant_index();
assert!(condition.next_power_of_two().trailing_zeros() as usize <= CONDITIONAL_BITS_WIDTH);
encoding |= (condition as u64) << CONDITIONAL_BITS_SHIFT;
let src_registers_byte = merge_u4(opcode.src0_reg_idx, opcode.src1_reg_idx);
encoding |= (src_registers_byte as u64) << SRC_REGS_SHIFT;
let dst_registers_byte = merge_u4(opcode.dst0_reg_idx, opcode.dst1_reg_idx);
encoding |= (dst_registers_byte as u64) << DST_REGS_SHIFT;
encoding |= (opcode.imm_0 as u64) << Self::IMM0_SHIFT;
encoding |= (opcode.imm_1 as u64) << Self::IMM1_SHIFT;
encoding
}
fn encode_as_bytes(opcode: &DecodedOpcode<8, Self>) -> [u8; 8] {
Self::encode_as_integer(opcode).encode_as_bytes()
}
fn split_pc(pc: Self::PcOrImm) -> (Self::PcOrImm, Self::PcOrImm) {
(pc >> 2, pc & 0b11)
}
fn integer_representaiton_from_u256(
value: U256,
index: Self::PcOrImm,
) -> Self::IntegerRepresentation {
match index {
0 => value.0[3],
1 => value.0[2],
2 => value.0[1],
3 => value.0[0],
_ => unreachable!(),
}
}
fn is_canonical_encoding(value: Self::IntegerRepresentation) -> bool {
let unused_bits = value & Self::UNUSED_BITS_MASK;
unused_bits == 0
}
}