use super::{
BytecodeBlockId, BytecodeEdgeKind, BytecodeImmediate, BytecodeInstruction,
BytecodeInstructionId, BytecodeOperand,
};
use crate::builder::BytecodeBuilder;
use crate::function::{BytecodeFunction, BytecodeFunctionConstant};
use crate::model::{BytecodeImportId, Register};
use crate::opcodes::Opcode;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BytecodeWriteError {
ChildFunctionLimitExceeded {
id: u32,
},
MissingResultRegister {
operand: BytecodeOperand,
},
MissingOperand {
opcode: Opcode,
index: usize,
},
UnexpectedOperand {
opcode: Opcode,
index: usize,
expected: &'static str,
actual: BytecodeOperand,
},
UnexpectedImmediate {
opcode: Opcode,
index: usize,
expected: &'static str,
actual: BytecodeImmediate,
},
VmConstantOutOfRange {
value: i32,
},
}
pub(crate) fn encode_function_bytecode<'strings>(
builder: &mut BytecodeBuilder<'strings>,
function: &mut BytecodeFunction<'strings>,
) -> Result<Vec<u8>, BytecodeWriteError> {
let function_id = builder.begin_function(function.num_params, function.is_vararg);
if !function.debug_name.is_empty() {
builder.set_debug_function_name(function.debug_name);
}
builder.set_debug_function_line_defined(function.line_defined as i32);
builder.set_function_type_info(function.type_info.clone());
for ty in function.upvalue_types.iter().copied() {
builder.push_upvalue_type_info(ty);
}
for upvalue in &function.upvalue_names {
builder.push_debug_upvalue(*upvalue);
}
for constant in &function.constants {
match constant {
BytecodeFunctionConstant::Nil => {
builder.add_constant_nil();
}
BytecodeFunctionConstant::Boolean(value) => {
builder.add_constant_boolean(*value);
}
BytecodeFunctionConstant::Number(value) => {
builder.add_constant_number(*value);
}
BytecodeFunctionConstant::Vector(value) => {
builder.add_constant_vector(value.x(), value.y(), value.z(), value.w());
}
BytecodeFunctionConstant::VectorDouble(value) => {
builder.add_constant_vector_double(value.x(), value.y(), value.z(), value.w());
}
BytecodeFunctionConstant::String(value) => {
builder.add_constant_string(*value);
}
BytecodeFunctionConstant::Import(value) => {
builder.add_import(BytecodeImportId::from_raw(*value));
}
BytecodeFunctionConstant::TableIndex(index) => {
builder.add_constant_table(&function.table_shapes[*index as usize]);
}
BytecodeFunctionConstant::Closure(id) => {
builder.add_constant_closure(*id);
}
BytecodeFunctionConstant::Integer(value) => {
builder.add_constant_integer(*value);
}
BytecodeFunctionConstant::ClassIndex(index) => {
builder.add_class_shape(function.class_shapes[*index as usize].clone());
}
}
}
for proto in &function.protos {
builder
.add_child_function(*proto)
.ok_or(BytecodeWriteError::ChildFunctionLimitExceeded { id: *proto })?;
}
let instruction_pcs = FunctionCodeEmitter::new(builder, function).emit()?;
for local in &function.local_types {
let start_pc = remap_pc(&instruction_pcs, builder.get_debug_pc(), local.start_pc);
let end_pc = remap_pc(&instruction_pcs, builder.get_debug_pc(), local.end_pc);
builder.push_local_type_info(local.ty, local.register, start_pc, end_pc);
}
for local in &function.locals {
let start_pc = remap_pc(&instruction_pcs, builder.get_debug_pc(), local.start_pc);
let end_pc = remap_pc(&instruction_pcs, builder.get_debug_pc(), local.end_pc);
builder.push_debug_local(local.name, local.register, start_pc, end_pc);
}
builder.fold_jumps();
builder.expand_jumps();
builder.end_function(
function.max_stack_size,
function.upvalue_count,
function.flags,
0,
);
Ok(builder.get_function_data(function_id))
}
fn remap_pc(instruction_pcs: &[u32], debug_pc: u32, pc: u32) -> u32 {
instruction_pcs
.get(pc as usize)
.copied()
.unwrap_or(debug_pc)
}
struct FunctionCodeEmitter<'function, 'strings> {
function: &'function mut BytecodeFunction<'strings>,
builder: &'function mut BytecodeBuilder<'strings>,
instruction_pcs: Vec<u32>,
jumps: Vec<JumpInfo>,
}
#[derive(Clone, Copy)]
struct JumpInfo {
opcode: Opcode,
pc: usize,
target: BytecodeBlockId,
}
impl<'function, 'strings> FunctionCodeEmitter<'function, 'strings> {
fn new(
builder: &'function mut BytecodeBuilder<'strings>,
function: &'function mut BytecodeFunction<'strings>,
) -> Self {
let instruction_pcs = vec![u32::MAX; function.instructions.len()];
Self {
function,
builder,
instruction_pcs,
jumps: Vec::new(),
}
}
fn emit(mut self) -> Result<Vec<u32>, BytecodeWriteError> {
let schedule = self.reschedule();
for (index, block_id) in schedule.iter().copied().enumerate() {
if let Some(fallthrough) = self.fallthrough(block_id)
&& fallthrough != self.function.exit_block
&& !self.function.block(fallthrough).is_dead()
&& schedule.get(index + 1).copied() != Some(fallthrough)
{
self.append_fallthrough_jump(block_id, fallthrough);
}
self.function.blocks[block_id.index()].set_start_pc(self.builder.get_debug_pc());
let mut instruction_index = 0usize;
while instruction_index < self.function.block(block_id).graph_instructions().len() {
let instruction_id =
self.function.block(block_id).graph_instructions()[instruction_index];
if instruction_id.index() >= self.instruction_pcs.len() {
self.instruction_pcs
.resize(instruction_id.index() + 1, self.builder.get_debug_pc());
}
self.instruction_pcs[instruction_id.index()] = self.builder.get_debug_pc();
self.emit_instruction(instruction_id)?;
instruction_index += 1;
}
}
for jump_index in 0..self.jumps.len() {
let jump = self.jumps[jump_index];
self.patch_jump(jump);
}
Ok(self.instruction_pcs)
}
fn reschedule(&self) -> Vec<BytecodeBlockId> {
let mut schedule = self
.function
.blocks()
.iter()
.enumerate()
.filter(|(_, block)| !block.is_dead())
.map(|(index, _)| BytecodeBlockId::new(index))
.collect::<Vec<_>>();
schedule.sort_by_key(|block| self.function.block(*block).sort_key());
debug_assert_eq!(schedule.pop(), Some(self.function.exit_block));
schedule
}
fn fallthrough(&self, block: BytecodeBlockId) -> Option<BytecodeBlockId> {
self.function
.block(block)
.successors()
.iter()
.find(|edge| edge.kind == BytecodeEdgeKind::Fallthrough)
.map(|edge| edge.target)
}
fn append_fallthrough_jump(&mut self, block: BytecodeBlockId, fallthrough: BytecodeBlockId) {
let id = BytecodeInstructionId::new(self.function.instructions.len());
self.function
.instructions
.push(BytecodeInstruction::synthetic_jump(block, fallthrough));
self.function.blocks[block.index()].append_graph_instruction(id);
}
fn emit_instruction(&mut self, id: BytecodeInstructionId) -> Result<(), BytecodeWriteError> {
let instruction = self.function.graph_instruction(id).clone();
match instruction.opcode() {
Opcode::Nop | Opcode::Break | Opcode::NativeCall => {
self.emit_abc(instruction.opcode(), 0, 0, 0, instruction.line());
}
Opcode::LoadNil => {
self.emit_abc(
Opcode::LoadNil,
self.register(BytecodeOperand::Instruction(id))?,
0,
0,
instruction.line(),
);
}
Opcode::LoadB => {
if instruction.operands().len() > 1 {
self.record_jump(instruction.opcode(), self.block_input(&instruction, 1)?);
}
self.emit_abc(
Opcode::LoadB,
self.register(BytecodeOperand::Instruction(id))?,
u8::from(self.bool_imm(&instruction, 0)?),
0,
instruction.line(),
);
}
Opcode::LoadN => {
self.emit_ad(
Opcode::LoadN,
self.register(BytecodeOperand::Instruction(id))?,
self.int_imm(&instruction, 0)? as i16,
instruction.line(),
);
}
Opcode::LoadK => {
self.emit_ad(
Opcode::LoadK,
self.register(BytecodeOperand::Instruction(id))?,
self.vm_const(&instruction, 0)? as i16,
instruction.line(),
);
}
Opcode::Move => {
self.emit_abc(
Opcode::Move,
self.register(BytecodeOperand::Instruction(id))?,
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
}
Opcode::GetGlobal => {
self.emit_abc(
Opcode::GetGlobal,
self.register(BytecodeOperand::Instruction(id))?,
0,
self.int_imm(&instruction, 0)? as u8,
instruction.line(),
);
self.emit_aux(self.vm_const_word(&instruction, 1)?, instruction.line());
}
Opcode::SetGlobal => {
self.emit_abc(
Opcode::SetGlobal,
self.reg_input(&instruction, 0)?,
0,
self.int_imm(&instruction, 1)? as u8,
instruction.line(),
);
self.emit_aux(self.vm_const_word(&instruction, 2)?, instruction.line());
}
Opcode::GetUpval => {
self.emit_abc(
Opcode::GetUpval,
self.register(BytecodeOperand::Instruction(id))?,
self.upvalue(&instruction, 0)?,
0,
instruction.line(),
);
}
Opcode::SetUpval => {
self.emit_abc(
Opcode::SetUpval,
self.reg_input(&instruction, 0)?,
self.upvalue(&instruction, 1)?,
0,
instruction.line(),
);
}
Opcode::CloseUpvals => {
self.emit_abc(
Opcode::CloseUpvals,
self.vm_reg(&instruction, 0)?,
0,
0,
instruction.line(),
);
}
Opcode::GetImport => {
self.emit_ad(
Opcode::GetImport,
self.register(BytecodeOperand::Instruction(id))?,
self.vm_const(&instruction, 0)? as i16,
instruction.line(),
);
self.emit_aux(self.import_imm(&instruction, 1)?, instruction.line());
}
Opcode::GetTable => {
self.emit_abc(
Opcode::GetTable,
self.register(BytecodeOperand::Instruction(id))?,
self.reg_input(&instruction, 0)?,
self.reg_input(&instruction, 1)?,
instruction.line(),
);
}
Opcode::SetTable => {
self.emit_abc(
Opcode::SetTable,
self.reg_input(&instruction, 0)?,
self.reg_input(&instruction, 1)?,
self.reg_input(&instruction, 2)?,
instruction.line(),
);
}
Opcode::GetUDataKs | Opcode::GetTableKs => {
self.emit_abc(
instruction.opcode(),
self.register(BytecodeOperand::Instruction(id))?,
self.reg_input(&instruction, 0)?,
self.int_imm(&instruction, 1)? as u8,
instruction.line(),
);
self.emit_aux(self.vm_const_word(&instruction, 2)?, instruction.line());
}
Opcode::SetUDataKs | Opcode::SetTableKs => {
self.emit_abc(
instruction.opcode(),
self.reg_input(&instruction, 0)?,
self.reg_input(&instruction, 1)?,
self.int_imm(&instruction, 2)? as u8,
instruction.line(),
);
self.emit_aux(self.vm_const_word(&instruction, 3)?, instruction.line());
}
Opcode::GetTableN => {
self.emit_abc(
Opcode::GetTableN,
self.register(BytecodeOperand::Instruction(id))?,
self.reg_input(&instruction, 0)?,
(self.int_imm(&instruction, 1)? - 1) as u8,
instruction.line(),
);
}
Opcode::SetTableN => {
self.emit_abc(
Opcode::SetTableN,
self.reg_input(&instruction, 0)?,
self.reg_input(&instruction, 1)?,
(self.int_imm(&instruction, 2)? - 1) as u8,
instruction.line(),
);
}
Opcode::NewClosure => {
self.emit_ad(
Opcode::NewClosure,
self.register(BytecodeOperand::Instruction(id))?,
self.proto(&instruction, 0)? as i16,
instruction.line(),
);
}
Opcode::NameCall | Opcode::NameCallUData => {
self.emit_abc(
instruction.opcode(),
self.register(BytecodeOperand::Instruction(id))?,
self.reg_input(&instruction, 0)?,
self.int_imm(&instruction, 1)? as u8,
instruction.line(),
);
self.emit_aux(self.vm_const_word(&instruction, 2)?, instruction.line());
}
Opcode::Call => {
self.emit_abc(
Opcode::Call,
self.reg_input(&instruction, 2)?,
(self.int_imm(&instruction, 0)? + 1) as u8,
(self.int_imm(&instruction, 1)? + 1) as u8,
instruction.line(),
);
}
Opcode::CallFb => {
self.emit_abc(
Opcode::CallFb,
self.reg_input(&instruction, 3)?,
(self.int_imm(&instruction, 0)? + 1) as u8,
(self.int_imm(&instruction, 1)? + 1) as u8,
instruction.line(),
);
self.emit_aux(self.int_imm(&instruction, 2)? as u32, instruction.line());
}
Opcode::Return => {
self.emit_abc(
Opcode::Return,
self.reg_input(&instruction, 1)?,
(self.int_imm(&instruction, 0)? + 1) as u8,
0,
instruction.line(),
);
}
Opcode::Jump | Opcode::JumpBack => {
self.record_jump(instruction.opcode(), self.block_input(&instruction, 0)?);
self.emit_ad(instruction.opcode(), 0, 0, instruction.line());
}
Opcode::JumpIf | Opcode::JumpIfNot => {
self.record_jump(instruction.opcode(), self.block_input(&instruction, 1)?);
self.emit_ad(
instruction.opcode(),
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
}
Opcode::JumpIfEq
| Opcode::JumpIfLe
| Opcode::JumpIfLt
| Opcode::JumpIfNotEq
| Opcode::JumpIfNotLe
| Opcode::JumpIfNotLt => {
self.record_jump(instruction.opcode(), self.block_input(&instruction, 2)?);
self.emit_ad(
instruction.opcode(),
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
self.emit_aux(
u32::from(self.reg_input(&instruction, 1)?),
instruction.line(),
);
}
Opcode::Add
| Opcode::Sub
| Opcode::Mul
| Opcode::Div
| Opcode::Mod
| Opcode::Pow
| Opcode::And
| Opcode::Or
| Opcode::IDiv => {
self.emit_abc(
instruction.opcode(),
self.register(BytecodeOperand::Instruction(id))?,
self.reg_input(&instruction, 0)?,
self.reg_input(&instruction, 1)?,
instruction.line(),
);
}
Opcode::AddK
| Opcode::SubK
| Opcode::MulK
| Opcode::DivK
| Opcode::ModK
| Opcode::PowK
| Opcode::AndK
| Opcode::OrK
| Opcode::IDivK => {
self.emit_abc(
instruction.opcode(),
self.register(BytecodeOperand::Instruction(id))?,
self.reg_input(&instruction, 0)?,
self.vm_const(&instruction, 1)? as u8,
instruction.line(),
);
}
Opcode::Concat => {
self.emit_abc(
Opcode::Concat,
self.register(BytecodeOperand::Instruction(id))?,
self.reg_input(&instruction, 0)?,
self.reg_input(&instruction, instruction.operands().len() - 1)?,
instruction.line(),
);
}
Opcode::Not | Opcode::Minus | Opcode::Length => {
self.emit_abc(
instruction.opcode(),
self.register(BytecodeOperand::Instruction(id))?,
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
}
Opcode::NewTable => {
self.emit_abc(
Opcode::NewTable,
self.register(BytecodeOperand::Instruction(id))?,
self.int_imm(&instruction, 0)? as u8,
0,
instruction.line(),
);
self.emit_aux(self.int_imm(&instruction, 1)? as u32, instruction.line());
}
Opcode::DupTable => {
self.emit_ad(
Opcode::DupTable,
self.register(BytecodeOperand::Instruction(id))?,
self.vm_const(&instruction, 0)? as i16,
instruction.line(),
);
}
Opcode::SetList => {
self.emit_abc(
Opcode::SetList,
self.reg_input(&instruction, 2)?,
self.reg_input(&instruction, 3)?,
(self.int_imm(&instruction, 1)? + 1) as u8,
instruction.line(),
);
self.emit_aux(self.int_imm(&instruction, 0)? as u32, instruction.line());
}
Opcode::ForNPrep | Opcode::ForNLoop => {
self.record_jump(instruction.opcode(), self.block_input(&instruction, 3)?);
self.emit_ad(
instruction.opcode(),
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
}
Opcode::ForGPrep | Opcode::ForGPrepNext | Opcode::ForGPrepInext => {
self.record_jump(instruction.opcode(), self.block_input(&instruction, 3)?);
self.emit_ad(
instruction.opcode(),
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
}
Opcode::ForGLoop => {
self.record_jump(Opcode::ForGLoop, self.block_input(&instruction, 5)?);
self.emit_ad(
Opcode::ForGLoop,
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
self.emit_aux(
(u32::from(self.bool_imm(&instruction, 3)?) << 31)
| self.int_imm(&instruction, 4)? as u32,
instruction.line(),
);
}
Opcode::FastCall => {
self.emit_abc(
Opcode::FastCall,
self.int_imm(&instruction, 0)? as u8,
0,
self.int_imm(&instruction, 1)? as u8,
instruction.line(),
);
}
Opcode::FastCall1 => {
self.emit_abc(
Opcode::FastCall1,
self.int_imm(&instruction, 0)? as u8,
self.reg_input(&instruction, 1)?,
self.int_imm(&instruction, 2)? as u8,
instruction.line(),
);
}
Opcode::FastCall2 => {
self.emit_abc(
Opcode::FastCall2,
self.int_imm(&instruction, 0)? as u8,
self.reg_input(&instruction, 1)?,
self.int_imm(&instruction, 3)? as u8,
instruction.line(),
);
self.emit_aux(
u32::from(self.reg_input(&instruction, 2)?),
instruction.line(),
);
}
Opcode::FastCall2K => {
self.emit_abc(
Opcode::FastCall2K,
self.int_imm(&instruction, 0)? as u8,
self.reg_input(&instruction, 1)?,
self.int_imm(&instruction, 3)? as u8,
instruction.line(),
);
self.emit_aux(self.vm_const_word(&instruction, 2)?, instruction.line());
}
Opcode::FastCall3 => {
self.emit_abc(
Opcode::FastCall3,
self.int_imm(&instruction, 0)? as u8,
self.reg_input(&instruction, 1)?,
self.int_imm(&instruction, 4)? as u8,
instruction.line(),
);
self.emit_aux(
u32::from(self.reg_input(&instruction, 2)?)
| (u32::from(self.reg_input(&instruction, 3)?) << 8),
instruction.line(),
);
}
Opcode::GetVarargs => {
self.emit_abc(
Opcode::GetVarargs,
self.vm_reg(&instruction, 0)?,
(self.int_imm(&instruction, 1)? + 1) as u8,
0,
instruction.line(),
);
}
Opcode::DupClosure => {
self.emit_ad(
Opcode::DupClosure,
self.register(BytecodeOperand::Instruction(id))?,
self.vm_const(&instruction, 0)? as i16,
instruction.line(),
);
}
Opcode::PrepVarargs => {
self.emit_ad(
Opcode::PrepVarargs,
self.int_imm(&instruction, 0)? as u8,
0,
instruction.line(),
);
}
Opcode::LoadKx => {
self.emit_ad(
Opcode::LoadKx,
self.register(BytecodeOperand::Instruction(id))?,
0,
instruction.line(),
);
self.emit_aux(self.vm_const_word(&instruction, 0)?, instruction.line());
}
Opcode::JumpX => {
self.record_jump(Opcode::JumpX, self.block_input(&instruction, 0)?);
self.emit_e(Opcode::JumpX, 0, instruction.line());
}
Opcode::Coverage => {
self.emit_e(
Opcode::Coverage,
self.int_imm(&instruction, 0)?,
instruction.line(),
);
}
Opcode::Capture => {
let capture_type = self.int_imm(&instruction, 0)? as u8;
let captured = if capture_type <= 1 {
self.reg_input(&instruction, 1)?
} else {
self.upvalue(&instruction, 1)?
};
self.emit_abc(
Opcode::Capture,
capture_type,
captured,
self.int_imm(&instruction, 2)? as u8,
instruction.line(),
);
}
Opcode::SubRK | Opcode::DivRK => {
self.emit_abc(
instruction.opcode(),
self.register(BytecodeOperand::Instruction(id))?,
self.vm_const(&instruction, 0)? as u8,
self.reg_input(&instruction, 1)?,
instruction.line(),
);
}
Opcode::JumpXEqKNil => {
self.record_jump(Opcode::JumpXEqKNil, self.block_input(&instruction, 2)?);
self.emit_ad(
Opcode::JumpXEqKNil,
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
self.emit_aux(
u32::from(self.bool_imm(&instruction, 1)?) << 31,
instruction.line(),
);
}
Opcode::JumpXEqKB => {
self.record_jump(Opcode::JumpXEqKB, self.block_input(&instruction, 2)?);
self.emit_ad(
Opcode::JumpXEqKB,
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
self.emit_aux(
(u32::from(self.bool_imm(&instruction, 1)?) << 31)
| u32::from(self.bool_imm(&instruction, 3)?),
instruction.line(),
);
}
Opcode::JumpXEqKN | Opcode::JumpXEqKS => {
self.record_jump(instruction.opcode(), self.block_input(&instruction, 2)?);
self.emit_ad(
instruction.opcode(),
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
self.emit_aux(
(u32::from(self.bool_imm(&instruction, 1)?) << 31)
| self.vm_const_word(&instruction, 3)?,
instruction.line(),
);
}
Opcode::CmpProto => {
self.record_jump(Opcode::CmpProto, self.block_input(&instruction, 2)?);
self.emit_ad(
Opcode::CmpProto,
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
self.emit_aux(self.int_imm(&instruction, 1)? as u32, instruction.line());
}
Opcode::NewClassMember => {
self.emit_abc(
Opcode::NewClassMember,
self.reg_input(&instruction, 0)?,
0,
self.reg_input(&instruction, 1)?,
instruction.line(),
);
self.emit_aux(self.vm_const_word(&instruction, 2)?, instruction.line());
}
Opcode::NewClass => {
self.emit_abc(
Opcode::NewClass,
self.register(BytecodeOperand::Instruction(id))?,
self.reg_input(&instruction, 0)?,
0,
instruction.line(),
);
self.emit_aux(self.vm_const_word(&instruction, 1)?, instruction.line());
}
}
Ok(())
}
fn register(&self, operand: BytecodeOperand) -> Result<Register, BytecodeWriteError> {
match operand {
BytecodeOperand::Phi(id) => {
if let Some(register) = self.function.registers.get(&BytecodeOperand::Phi(id)) {
return Ok(*register);
}
let operands = self.function.phi(id).operands();
debug_assert!(!operands.is_empty());
let register = self.register(operands[0])?;
Ok(register)
}
BytecodeOperand::Projection(id) => {
let projection = self.function.projection(id);
Ok(self.register(projection.source)? + projection.index as u8)
}
BytecodeOperand::VmRegister(register) => Ok(register),
_ => self
.function
.registers
.get(&operand)
.copied()
.ok_or(BytecodeWriteError::MissingResultRegister { operand }),
}
}
fn operand(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<BytecodeOperand, BytecodeWriteError> {
instruction
.operands()
.get(index)
.copied()
.ok_or(BytecodeWriteError::MissingOperand {
opcode: instruction.opcode(),
index,
})
}
fn int_imm(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<i32, BytecodeWriteError> {
let operand = self.operand(instruction, index)?;
let BytecodeOperand::Immediate(id) = operand else {
return Err(BytecodeWriteError::UnexpectedOperand {
opcode: instruction.opcode(),
index,
expected: "immediate",
actual: operand,
});
};
let BytecodeImmediate::Int(value) = *self.function.immediate(id) else {
return Err(BytecodeWriteError::UnexpectedImmediate {
opcode: instruction.opcode(),
index,
expected: "integer",
actual: *self.function.immediate(id),
});
};
Ok(value)
}
fn bool_imm(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<bool, BytecodeWriteError> {
let operand = self.operand(instruction, index)?;
let BytecodeOperand::Immediate(id) = operand else {
return Err(BytecodeWriteError::UnexpectedOperand {
opcode: instruction.opcode(),
index,
expected: "immediate",
actual: operand,
});
};
let BytecodeImmediate::Boolean(value) = *self.function.immediate(id) else {
return Err(BytecodeWriteError::UnexpectedImmediate {
opcode: instruction.opcode(),
index,
expected: "boolean",
actual: *self.function.immediate(id),
});
};
Ok(value)
}
fn import_imm(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<u32, BytecodeWriteError> {
let operand = self.operand(instruction, index)?;
let BytecodeOperand::Immediate(id) = operand else {
return Err(BytecodeWriteError::UnexpectedOperand {
opcode: instruction.opcode(),
index,
expected: "immediate",
actual: operand,
});
};
let BytecodeImmediate::Import(value) = *self.function.immediate(id) else {
return Err(BytecodeWriteError::UnexpectedImmediate {
opcode: instruction.opcode(),
index,
expected: "import",
actual: *self.function.immediate(id),
});
};
Ok(value)
}
fn reg_input(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<Register, BytecodeWriteError> {
self.register(self.operand(instruction, index)?)
}
fn vm_reg(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<Register, BytecodeWriteError> {
let operand = self.operand(instruction, index)?;
let BytecodeOperand::VmRegister(register) = operand else {
return Err(BytecodeWriteError::UnexpectedOperand {
opcode: instruction.opcode(),
index,
expected: "VM register",
actual: operand,
});
};
Ok(register)
}
fn vm_const(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<i32, BytecodeWriteError> {
let operand = self.operand(instruction, index)?;
let BytecodeOperand::VmConstant(value) = operand else {
return Err(BytecodeWriteError::UnexpectedOperand {
opcode: instruction.opcode(),
index,
expected: "VM constant",
actual: operand,
});
};
Ok(value)
}
fn vm_const_word(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<u32, BytecodeWriteError> {
let value = self.vm_const(instruction, index)?;
u32::try_from(value).map_err(|_| BytecodeWriteError::VmConstantOutOfRange { value })
}
fn upvalue(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<u8, BytecodeWriteError> {
let operand = self.operand(instruction, index)?;
let BytecodeOperand::VmUpvalue(value) = operand else {
return Err(BytecodeWriteError::UnexpectedOperand {
opcode: instruction.opcode(),
index,
expected: "VM upvalue",
actual: operand,
});
};
Ok(value as u8)
}
fn proto(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<u16, BytecodeWriteError> {
let operand = self.operand(instruction, index)?;
let BytecodeOperand::VmProto(value) = operand else {
return Err(BytecodeWriteError::UnexpectedOperand {
opcode: instruction.opcode(),
index,
expected: "VM proto",
actual: operand,
});
};
Ok(value as u16)
}
fn block_input(
&self,
instruction: &BytecodeInstruction,
index: usize,
) -> Result<BytecodeBlockId, BytecodeWriteError> {
let operand = self.operand(instruction, index)?;
let BytecodeOperand::Block(block) = operand else {
return Err(BytecodeWriteError::UnexpectedOperand {
opcode: instruction.opcode(),
index,
expected: "block",
actual: operand,
});
};
Ok(block)
}
fn record_jump(&mut self, opcode: Opcode, target: BytecodeBlockId) {
self.jumps.push(JumpInfo {
opcode,
pc: self.builder.get_instruction_count(),
target,
});
}
fn patch_jump(&mut self, jump: JumpInfo) {
let target_pc = self.function.block(jump.target).start_pc() as usize;
if jump.opcode.is_jump_d() {
debug_assert!(self.builder.patch_jump_d(jump.pc, target_pc));
} else if jump.opcode.is_skip_c() {
debug_assert!(self.builder.patch_skip_c(jump.pc, target_pc));
} else if jump.opcode == Opcode::JumpX {
debug_assert!(self.builder.patch_jump_e(jump.pc, target_pc));
}
}
fn emit_abc(&mut self, opcode: Opcode, a: u8, b: u8, c: u8, line: u32) {
self.builder.set_debug_line(line as usize);
self.builder.emit_abc(opcode, a, b, c);
}
fn emit_ad(&mut self, opcode: Opcode, a: u8, d: i16, line: u32) {
self.builder.set_debug_line(line as usize);
self.builder.emit_ad(opcode, a, d);
}
fn emit_e(&mut self, opcode: Opcode, e: i32, line: u32) {
self.builder.set_debug_line(line as usize);
self.builder.emit_e(opcode, e);
}
fn emit_aux(&mut self, word: u32, line: u32) {
self.builder.set_debug_line(line as usize);
self.builder.emit_aux(word);
}
}