use crate::*;
use super::*;
#[derive(Debug)]
pub struct CompileCtx {
pub reg_map: HashMap<usize, Register>,
pub symbols: HashMap<u64, Register>,
pub symbol_ptrs: HashMap<u64, usize>,
pub dictionary: HashMap<u64, String>,
pub mutable_symbols: HashSet<u64>,
pub types: TypeSection,
pub features: HashSet<FeatureFlag>,
pub const_entries: Vec<ConstEntry>,
pub const_blob: Vec<u8>,
pub instrs: Vec<EncodedInstr>,
pub next_reg: Register,
}
#[cfg(feature = "compiler")]
impl CompileCtx {
pub fn new() -> Self {
Self {
reg_map: HashMap::new(),
symbols: HashMap::new(),
mutable_symbols: HashSet::new(),
dictionary: HashMap::new(),
types: TypeSection::new(),
symbol_ptrs: HashMap::new(),
features: HashSet::new(),
const_entries: Vec::new(),
const_blob: Vec::new(),
instrs: Vec::new(),
next_reg: 0,
}
}
pub fn clear(&mut self) {
self.reg_map.clear();
self.symbols.clear();
self.dictionary.clear();
self.mutable_symbols.clear();
self.types = TypeSection::new();
self.features.clear();
self.const_entries.clear();
self.const_blob.clear();
self.instrs.clear();
self.next_reg = 0;
}
pub fn define_symbol(&mut self, id: usize, reg: Register, name: &str, mutable: bool) {
let symbol_id = hash_str(name);
self.symbols.insert(symbol_id, reg);
self.symbol_ptrs.insert(symbol_id, id);
self.dictionary.insert(symbol_id, name.to_string());
if mutable {
self.mutable_symbols.insert(symbol_id);
}
}
pub fn alloc_register_for_ptr(&mut self, ptr: usize) -> Register {
if let Some(&r) = self.reg_map.get(&ptr) { return r; }
let r = self.next_reg;
self.next_reg += 1;
self.reg_map.insert(ptr, r);
r
}
pub fn emit_const_load(&mut self, dst: Register, const_id: u32) {
self.instrs.push(EncodedInstr::ConstLoad { dst, const_id });
}
pub fn emit_nullop(&mut self, fxn_id: u64, dst: Register) {
self.instrs.push(EncodedInstr::NullOp { fxn_id, dst });
}
pub fn emit_unop(&mut self, fxn_id: u64, dst: Register, src: Register) {
self.instrs.push(EncodedInstr::UnOp { fxn_id, dst, src });
}
pub fn emit_binop(&mut self, fxn_id: u64, dst: Register, lhs: Register, rhs: Register) {
self.instrs.push(EncodedInstr::BinOp { fxn_id, dst, lhs, rhs });
}
pub fn emit_ternop(&mut self, fxn_id: u64, dst: Register, a: Register, b: Register, c: Register) {
self.instrs.push(EncodedInstr::TernOp { fxn_id, dst, a, b, c });
}
pub fn emit_quadop(&mut self, fxn_id: u64, dst: Register, a: Register, b: Register, c: Register, d: Register) {
self.instrs.push(EncodedInstr::QuadOp { fxn_id, dst, a, b, c, d });
}
pub fn emit_varop(&mut self, fxn_id: u64, dst: Register, args: Vec<Register>) {
self.instrs.push(EncodedInstr::VarArg { fxn_id, dst, args });
}
pub fn emit_ret(&mut self, src: Register) {
self.instrs.push(EncodedInstr::Ret { src })
}
pub fn compile_const(&mut self, bytes: &[u8], value_kind: ValueKind) -> MResult<u32> {
let type_id = self.types.get_or_intern(&value_kind);
let align = value_kind.align();
let next_blob_len = self.const_blob.len() as u64;
let padded_off = align_up(next_blob_len, align as u64);
if padded_off > next_blob_len {
self.const_blob.resize(padded_off as usize, 0);
}
self.features.insert(FeatureFlag::Builtin(value_kind.to_feature_kind()));
let offset = self.const_blob.len() as u64;
self.const_blob.extend_from_slice(bytes);
let length = (self.const_blob.len() as u64) - offset;
let entry = ConstEntry {
type_id,
enc: ConstEncoding::Inline,
align: align as u8,
flags: 0,
reserved: 0,
offset,
length,
};
let const_id = self.const_entries.len() as u32;
self.const_entries.push(entry);
Ok(const_id)
}
pub fn compile(&mut self) -> MResult<Vec<u8>> {
let header_size = ByteCodeHeader::HEADER_SIZE as u64;
let feat_bytes_len: u64 = 4 + (self.features.len() as u64) * 8;
let types_bytes_len: u64 = self.types.byte_len();
let const_tbl_len: u64 = (self.const_entries.len() as u64) * ConstEntry::byte_len();
let const_blob_len: u64 = self.const_blob.len() as u64;
let symbols_len: u64 = (self.symbols.len() as u64) * 13; let instr_bytes_len: u64 = self.instrs.iter().map(|i| i.byte_len()).sum();
let dict_len: u64 = self.dictionary.values().map(|s| s.len() as u64 + 12).sum();
let mut offset = header_size; let feature_off = offset; offset += feat_bytes_len; let types_off = offset; offset += types_bytes_len; let const_tbl_off = offset; offset += const_tbl_len; let const_blob_off = offset; offset += const_blob_len; let symbols_off = offset; offset += symbols_len; let instr_off = offset; offset += instr_bytes_len; let dict_off = offset; offset += dict_len;
let file_len_before_trailer = offset;
let trailer_len = 4u64;
let full_file_len = file_len_before_trailer + trailer_len;
let header = ByteCodeHeader {
magic: *b"MECH",
version: 1,
mech_ver: parse_version_to_u16(env!("CARGO_PKG_VERSION")).unwrap(),
flags: 0,
reg_count: self.next_reg,
instr_count: self.instrs.len() as u32,
feature_count: self.features.len() as u32,
feature_off,
types_count: self.types.entries.len() as u32,
types_off,
const_count: self.const_entries.len() as u32,
const_tbl_off,
const_tbl_len,
const_blob_off,
const_blob_len,
symbols_len,
symbols_off,
instr_off,
instr_len: instr_bytes_len,
dict_len,
dict_off,
reserved: 0,
};
let mut buf = Cursor::new(Vec::<u8>::with_capacity(full_file_len as usize));
header.write_to(&mut buf)?;
buf.write_u32::<LittleEndian>(self.features.len() as u32)?;
for f in &self.features {
buf.write_u64::<LittleEndian>(f.as_u64())?;
}
self.types.write_to(&mut buf)?;
for entry in &self.const_entries {
entry.write_to(&mut buf)?;
}
if !self.const_blob.is_empty() {
buf.write_all(&self.const_blob)?;
}
for (id, reg) in &self.symbols {
let mutable = self.mutable_symbols.contains(id);
let entry = SymbolEntry::new(*id, mutable, *reg);
entry.write_to(&mut buf)?;
}
for ins in &self.instrs {
ins.write_to(&mut buf)?;
}
for (id, name) in &self.dictionary {
let dict_entry = DictEntry::new(*id, name);
dict_entry.write_to(&mut buf)?;
}
let pos = buf.position();
if pos != file_len_before_trailer {
return Err(
MechError::new(
BufferPositionMismatchError {
expected: file_len_before_trailer,
got: pos,
},
None
).with_compiler_loc()
);
}
let bytes_so_far = buf.get_ref().as_slice();
let checksum = crc32fast::hash(bytes_so_far);
buf.write_u32::<LittleEndian>(checksum)?;
if buf.position() != full_file_len {
return Err(
MechError::new(
FinalBufferLengthMismatchError {
expected: full_file_len,
got: buf.position(),
},
None,
).with_compiler_loc()
);
}
Ok(buf.into_inner())
}
}
#[inline]
fn align_up(offset: u64, align: u64) -> u64 {
if align == 0 { return offset; }
((offset + align - 1) / align) * align
}
#[derive(Debug, Clone)]
pub struct BufferPositionMismatchError {
pub expected: u64,
pub got: u64,
}
impl MechErrorKind for BufferPositionMismatchError {
fn name(&self) -> &str { "BufferPositionMismatch" }
fn message(&self) -> String {
format!("Buffer position mismatch: expected {}, got {}", self.expected, self.got)
}
}
#[derive(Debug, Clone)]
pub struct FinalBufferLengthMismatchError {
pub expected: u64,
pub got: u64,
}
impl MechErrorKind for FinalBufferLengthMismatchError {
fn name(&self) -> &str { "FinalBufferLengthMismatch" }
fn message(&self) -> String {
format!("Final buffer length mismatch: expected {}, got {}", self.expected, self.got)
}
}