#![warn(missing_docs)]
use fidget_core::{compiler::RegOp, vm::VmData};
use zerocopy::IntoBytes;
pub use fidget_core::compiler::RegOpDiscriminants as BytecodeOp;
pub struct Bytecode {
reg_count: u8,
mem_count: u32,
data: Vec<u32>,
}
impl Bytecode {
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.data.len()
}
pub fn data(&self) -> &[u32] {
&self.data
}
pub fn reg_count(&self) -> u8 {
self.reg_count
}
pub fn mem_count(&self) -> u32 {
self.mem_count
}
pub fn as_bytes(&self) -> &[u8] {
self.data.as_bytes()
}
pub fn new<const N: usize>(t: &VmData<N>) -> Self {
let mut data = vec![u32::MAX, 0u32];
let mut reg_count = 0u8;
let mut mem_count = 0u32;
for op in t.iter_asm() {
let r = BytecodeOp::from(op);
let mut word = [r as u8, 0xFF, 0xFF, 0xFF];
let mut imm = None;
let mut store_reg = |i, r| {
reg_count = reg_count.max(r); word[i] = r;
};
match op {
RegOp::Input(reg, slot) | RegOp::Output(reg, slot) => {
store_reg(1, reg);
imm = Some(slot);
}
RegOp::Load(reg, slot) | RegOp::Store(reg, slot) => {
store_reg(1, reg);
mem_count = mem_count.max(slot);
imm = Some(slot);
}
RegOp::CopyImm(out, imm_f32) => {
store_reg(1, out);
imm = Some(imm_f32.to_bits());
}
RegOp::NegReg(out, reg)
| RegOp::AbsReg(out, reg)
| RegOp::RecipReg(out, reg)
| RegOp::SqrtReg(out, reg)
| RegOp::SquareReg(out, reg)
| RegOp::FloorReg(out, reg)
| RegOp::CeilReg(out, reg)
| RegOp::RoundReg(out, reg)
| RegOp::CopyReg(out, reg)
| RegOp::SinReg(out, reg)
| RegOp::CosReg(out, reg)
| RegOp::TanReg(out, reg)
| RegOp::AsinReg(out, reg)
| RegOp::AcosReg(out, reg)
| RegOp::AtanReg(out, reg)
| RegOp::ExpReg(out, reg)
| RegOp::LnReg(out, reg)
| RegOp::NotReg(out, reg) => {
store_reg(1, out);
store_reg(2, reg);
}
RegOp::AddRegImm(out, reg, imm_f32)
| RegOp::MulRegImm(out, reg, imm_f32)
| RegOp::DivRegImm(out, reg, imm_f32)
| RegOp::DivImmReg(out, reg, imm_f32)
| RegOp::SubImmReg(out, reg, imm_f32)
| RegOp::SubRegImm(out, reg, imm_f32)
| RegOp::AtanRegImm(out, reg, imm_f32)
| RegOp::AtanImmReg(out, reg, imm_f32)
| RegOp::MinRegImm(out, reg, imm_f32)
| RegOp::MaxRegImm(out, reg, imm_f32)
| RegOp::CompareRegImm(out, reg, imm_f32)
| RegOp::CompareImmReg(out, reg, imm_f32)
| RegOp::ModRegImm(out, reg, imm_f32)
| RegOp::ModImmReg(out, reg, imm_f32)
| RegOp::AndRegImm(out, reg, imm_f32)
| RegOp::OrRegImm(out, reg, imm_f32) => {
store_reg(1, out);
store_reg(2, reg);
imm = Some(imm_f32.to_bits());
}
RegOp::AddRegReg(out, lhs, rhs)
| RegOp::MulRegReg(out, lhs, rhs)
| RegOp::DivRegReg(out, lhs, rhs)
| RegOp::SubRegReg(out, lhs, rhs)
| RegOp::AtanRegReg(out, lhs, rhs)
| RegOp::MinRegReg(out, lhs, rhs)
| RegOp::MaxRegReg(out, lhs, rhs)
| RegOp::CompareRegReg(out, lhs, rhs)
| RegOp::ModRegReg(out, lhs, rhs)
| RegOp::AndRegReg(out, lhs, rhs)
| RegOp::OrRegReg(out, lhs, rhs) => {
store_reg(1, out);
store_reg(2, lhs);
store_reg(3, rhs);
}
}
data.push(u32::from_le_bytes(word));
data.push(imm.unwrap_or(0xFF000000));
}
data.extend([u32::MAX, u32::MAX]);
Bytecode {
data,
mem_count,
reg_count,
}
}
}
pub fn iter_ops<'a>() -> impl Iterator<Item = (&'a str, u8)> {
use strum::IntoEnumIterator;
BytecodeOp::iter().enumerate().map(|(i, op)| {
let s: &'static str = op.into();
(s, i as u8)
})
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn simple_bytecode() {
let mut ctx = fidget_core::Context::new();
let x = ctx.x();
let c = ctx.constant(1.0);
let out = ctx.add(x, c).unwrap();
let data = VmData::<255>::new(&ctx, &[out]).unwrap();
let bc = Bytecode::new(&data);
let mut iter = bc.data.iter();
let mut next = || *iter.next().unwrap();
assert_eq!(next(), 0xFFFFFFFF); assert_eq!(next(), 0);
assert_eq!(
next().to_le_bytes(),
[BytecodeOp::Input as u8, 0, 0xFF, 0xFF]
);
assert_eq!(next(), 0); assert_eq!(
next().to_le_bytes(),
[BytecodeOp::AddRegImm as u8, 0, 0, 0xFF]
);
assert_eq!(f32::from_bits(next()), 1.0);
assert_eq!(
next().to_le_bytes(),
[BytecodeOp::Output as u8, 0, 0xFF, 0xFF]
);
assert_eq!(next(), 0); assert_eq!(next(), 0xFFFFFFFF); assert_eq!(next(), 0xFFFFFFFF);
assert!(iter.next().is_none());
}
}