use super::asm::{Asm, L, Res};
use crate::vm::dump::puc::modern::Kind;
use crate::vm::isa::Op;
pub(super) struct Frame {
pub ops: &'static [Kind],
pub v55: bool,
pub np: u32,
pub ret_c: u32,
pub needclose: bool,
pub vatab: bool,
}
pub(super) type Caps = Vec<Option<Vec<(bool, u8)>>>;
fn opn(ops: &[Kind], k: Kind) -> u32 {
ops.iter()
.position(|&x| x == k)
.expect("every emitted kind has an opcode") as u32
}
fn event(op: Op) -> Option<u32> {
Some(match op {
Op::Add => 6,
Op::Sub => 7,
Op::Mul => 8,
Op::Mod => 9,
Op::Pow => 10,
Op::Div => 11,
Op::IDiv => 12,
Op::BAnd => 13,
Op::BOr => 14,
Op::BXor => 15,
Op::Shl => 16,
Op::Shr => 17,
_ => return None,
})
}
pub(super) struct M<'a, 'p> {
pub asm: &'a mut Asm<'p>,
pub f: &'a Frame,
}
impl M<'_, '_> {
pub(super) fn op(&self, k: Kind) -> u32 {
opn(self.f.ops, k)
}
pub(super) fn abc(&self, k: Kind, a: u32, b: u32, c: u32, kf: bool) -> Res<u32> {
if a > 255 || b > 255 || c > 255 {
return Err(self
.asm
.err(format_args!("operands {a} {b} {c} do not fit")));
}
Ok(self.op(k) | (a << 7) | ((kf as u32) << 15) | (b << 16) | (c << 24))
}
pub(super) fn vabc(&self, k: Kind, a: u32, vb: u32, vc: u32, kf: bool) -> Res<u32> {
if a > 255 || vb > 63 || vc > 1023 {
return Err(self
.asm
.err(format_args!("operands {a} {vb} {vc} do not fit")));
}
Ok(self.op(k) | (a << 7) | ((kf as u32) << 15) | (vb << 16) | (vc << 22))
}
pub(super) fn abx(&self, k: Kind, a: u32, bx: u32) -> Res<u32> {
if a > 255 || bx >= 1 << 17 {
return Err(self.asm.err(format_args!("operands {a} {bx} do not fit")));
}
Ok(self.op(k) | (a << 7) | (bx << 15))
}
pub(super) fn asbx(&self, k: Kind, a: u32, sbx: i32) -> Res<u32> {
self.abx(k, a, (sbx + 65535) as u32)
}
pub(super) fn ax(&self, ax: u64) -> Res<u32> {
if ax >= 1 << 25 {
return Err(self.asm.err("extra argument does not fit"));
}
Ok(self.op(Kind::ExtraArg) | ((ax as u32) << 7))
}
pub(super) fn emit(&mut self, w: Res<u32>) -> Res<()> {
self.asm.emit(w?);
Ok(())
}
pub(super) fn load_k(&mut self, dst: u32, k: u32) -> Res<()> {
self.emit(self.abx(Kind::LoadK, dst, k))
}
pub(super) fn one(&mut self, l: L, caps: &mut Caps) -> Res<usize> {
match l.op {
Op::Move => {
let (a, b) = (self.asm.r(l.a)?, self.asm.r(l.b)?);
self.emit(self.abc(Kind::Move, a, b, 0, false))?;
}
Op::LoadI | Op::LoadF => {
let k = if l.op == Op::LoadI {
Kind::LoadI
} else {
Kind::LoadF
};
let a = self.asm.r(l.a)?;
self.emit(self.asbx(k, a, l.sbx))?;
}
Op::LoadK => {
let a = self.asm.r(l.a)?;
self.load_k(a, l.bx)?;
}
Op::LoadKx => {
let a = self.asm.r(l.a)?;
let Some(x) = self
.asm
.inst(self.asm.pc() + 1)
.filter(|x| x.op() == Op::ExtraArg)
else {
return Err(self.asm.err("LoadKx without its ExtraArg"));
};
self.emit(self.abc(Kind::LoadKx, a, 0, 0, false))?;
self.emit(self.ax(x.ax() as u64))?;
return Ok(2);
}
Op::LoadFalse | Op::LFalseSkip | Op::LoadTrue => {
let k = match l.op {
Op::LoadFalse => Kind::LoadFalse,
Op::LFalseSkip => Kind::LFalseSkip,
_ => Kind::LoadTrue,
};
let a = self.asm.r(l.a)?;
self.emit(self.abc(k, a, 0, 0, false))?;
}
Op::LoadNil => {
let a = self.asm.run(l.a, l.b + 1)?;
self.emit(self.abc(Kind::LoadNil, a, l.b, 0, false))?;
}
Op::GetUpval | Op::SetUpval => {
let k = if l.op == Op::GetUpval {
Kind::GetUpval
} else {
Kind::SetUpval
};
let a = self.asm.r(l.a)?;
self.emit(self.abc(k, a, l.b, 0, false))?;
}
Op::GetTabUp => {
let a = self.asm.r(l.a)?;
if self.asm.short_str(l.c) {
self.emit(self.abc(Kind::GetTabUp, a, l.b, l.c, false))?;
} else {
let (t, key) = (self.asm.temp()?, self.asm.temp()?);
self.emit(self.abc(Kind::GetUpval, t, l.b, 0, false))?;
self.load_k(key, l.c)?;
self.emit(self.abc(Kind::GetTable, a, t, key, false))?;
}
}
Op::GetTable => {
let (a, b, c) = (self.asm.r(l.a)?, self.asm.r(l.b)?, self.asm.r(l.c)?);
self.emit(self.abc(Kind::GetTable, a, b, c, false))?;
}
Op::GetI => {
let (a, b) = (self.asm.r(l.a)?, self.asm.r(l.b)?);
self.emit(self.abc(Kind::GetI, a, b, l.c, false))?;
}
Op::GetField => {
let (a, b) = (self.asm.r(l.a)?, self.asm.r(l.b)?);
if self.asm.short_str(l.c) {
self.emit(self.abc(Kind::GetField, a, b, l.c, false))?;
} else {
let key = self.asm.temp()?;
self.load_k(key, l.c)?;
self.emit(self.abc(Kind::GetTable, a, b, key, false))?;
}
}
Op::SetTabUp => {
let c = self.asm.r(l.c)?;
if self.asm.short_str(l.b) {
self.emit(self.abc(Kind::SetTabUp, l.a, l.b, c, false))?;
} else {
let (t, key) = (self.asm.temp()?, self.asm.temp()?);
self.emit(self.abc(Kind::GetUpval, t, l.a, 0, false))?;
self.load_k(key, l.b)?;
self.emit(self.abc(Kind::SetTable, t, key, c, false))?;
}
}
Op::SetTable => {
let (a, b, c) = (self.asm.r(l.a)?, self.asm.r(l.b)?, self.asm.r(l.c)?);
self.emit(self.abc(Kind::SetTable, a, b, c, false))?;
}
Op::SetI => {
let (a, c) = (self.asm.r(l.a)?, self.asm.r(l.c)?);
self.emit(self.abc(Kind::SetI, a, l.b, c, false))?;
}
Op::SetField => {
let (a, c) = (self.asm.r(l.a)?, self.asm.r(l.c)?);
if self.asm.short_str(l.b) {
self.emit(self.abc(Kind::SetField, a, l.b, c, false))?;
} else {
let key = self.asm.temp()?;
self.load_k(key, l.b)?;
self.emit(self.abc(Kind::SetTable, a, key, c, false))?;
}
}
Op::NewTable => self.new_table(l)?,
Op::SelfOp => self.self_op(l)?,
Op::Add if l.k => {
let (a, b) = (self.asm.r(l.a)?, self.asm.r(l.b)?);
self.emit(self.abc(Kind::ArithI, a, b, 127, false))?;
self.emit(self.abc(Kind::MmBinI, b, 127, 7, false))?;
}
Op::Add
| Op::Sub
| Op::Mul
| Op::Mod
| Op::Pow
| Op::Div
| Op::IDiv
| Op::BAnd
| Op::BOr
| Op::BXor
| Op::Shl
| Op::Shr => {
let (a, b, c) = (self.asm.r(l.a)?, self.asm.r(l.b)?, self.asm.r(l.c)?);
let tm = event(l.op).expect("arithmetic op");
self.emit(self.abc(Kind::Arith(l.op), a, b, c, false))?;
self.emit(self.abc(Kind::MmBin, b, c, tm, false))?;
}
Op::Unm | Op::BNot | Op::Not | Op::Len => {
let (a, b) = (self.asm.r(l.a)?, self.asm.r(l.b)?);
self.emit(self.abc(Kind::Unary(l.op), a, b, 0, false))?;
}
Op::Concat => {
let a = self.asm.run(l.a, l.b.max(1))?;
self.emit(self.abc(Kind::Concat, a, l.b, 0, false))?;
}
Op::Close | Op::Tbc => {
let k = if l.op == Op::Close {
Kind::Close
} else {
Kind::Tbc
};
let a = self.asm.r(l.a)?;
self.emit(self.abc(k, a, 0, 0, false))?;
}
_ => return self.flow(l, caps),
}
Ok(1)
}
}
pub(super) fn encode(asm: &mut Asm, f: &Frame, skip_first: bool, caps: &mut Caps) -> Res<()> {
let p = asm.p;
if p.is_vararg {
let a = if f.v55 { 0 } else { f.np };
let w = opn(f.ops, Kind::VarargPrep) | (a << 7);
asm.emit_prologue(w, p.line_defined.max(1));
}
let mut pc = usize::from(skip_first);
while pc < p.code.len() {
asm.begin(pc);
let l = L::of(p.code[pc]);
pc += M { asm, f }.one(l, caps)?;
}
Ok(())
}