mech_core/program/compiler/
context.rs

1use crate::*;
2use super::*;
3
4#[derive(Debug)]
5pub struct CompileCtx {
6  // pointer identity -> register index
7  pub reg_map: HashMap<usize, Register>,
8  // symbol identity -> register index
9  pub symbols: HashMap<u64, Register>,
10  // symbol identity -> pointer identity
11  pub symbol_ptrs: HashMap<u64, usize>,
12  // symbol identity -> symbol name
13  pub dictionary: HashMap<u64, String>,
14  // mutable symbols
15  pub mutable_symbols: HashSet<u64>,
16  pub types: TypeSection,
17  pub features: HashSet<FeatureFlag>,
18  pub const_entries: Vec<ConstEntry>,
19  pub const_blob: Vec<u8>,
20  pub instrs: Vec<EncodedInstr>,
21  pub next_reg: Register,
22}
23
24#[cfg(feature = "compiler")]
25impl CompileCtx {
26  pub fn new() -> Self {
27    Self {
28      reg_map: HashMap::new(),
29      symbols: HashMap::new(),
30      mutable_symbols: HashSet::new(),
31      dictionary: HashMap::new(),
32      types: TypeSection::new(),
33      symbol_ptrs: HashMap::new(),
34      features: HashSet::new(),
35      const_entries: Vec::new(),
36      const_blob: Vec::new(),
37      instrs: Vec::new(),
38      next_reg: 0,
39    }
40  }
41
42  pub fn clear(&mut self) {
43    self.reg_map.clear();
44    self.symbols.clear();
45    self.dictionary.clear();
46    self.mutable_symbols.clear();
47    self.types = TypeSection::new();
48    self.features.clear();
49    self.const_entries.clear();
50    self.const_blob.clear();
51    self.instrs.clear();
52    self.next_reg = 0;
53  }
54
55  pub fn define_symbol(&mut self, id: usize, reg: Register, name: &str, mutable: bool) {
56    let symbol_id = hash_str(name);
57    self.symbols.insert(symbol_id, reg);
58    self.symbol_ptrs.insert(symbol_id, id);
59    self.dictionary.insert(symbol_id, name.to_string());
60    if mutable {
61      self.mutable_symbols.insert(symbol_id);
62    }
63  }
64
65  pub fn alloc_register_for_ptr(&mut self, ptr: usize) -> Register {
66    if let Some(&r) = self.reg_map.get(&ptr) { return r; }
67    let r = self.next_reg;
68    self.next_reg += 1;
69    self.reg_map.insert(ptr, r);
70    r
71  }
72
73  pub fn emit_const_load(&mut self, dst: Register, const_id: u32) {
74    self.instrs.push(EncodedInstr::ConstLoad { dst, const_id });
75  }
76  pub fn emit_nullop(&mut self, fxn_id: u64, dst: Register) {
77    self.instrs.push(EncodedInstr::NullOp { fxn_id, dst });
78  }
79  pub fn emit_unop(&mut self, fxn_id: u64, dst: Register, src: Register) {
80    self.instrs.push(EncodedInstr::UnOp { fxn_id, dst, src });
81  }
82  pub fn emit_binop(&mut self, fxn_id: u64, dst: Register, lhs: Register, rhs: Register) {
83    self.instrs.push(EncodedInstr::BinOp { fxn_id, dst, lhs, rhs });
84  }
85  pub fn emit_ternop(&mut self, fxn_id: u64, dst: Register, a: Register, b: Register, c: Register) {
86    self.instrs.push(EncodedInstr::TernOp { fxn_id, dst, a, b, c });
87  }
88  pub fn emit_quadop(&mut self, fxn_id: u64, dst: Register, a: Register, b: Register, c: Register, d: Register) {
89    self.instrs.push(EncodedInstr::QuadOp { fxn_id, dst, a, b, c, d });
90  }
91  pub fn emit_varop(&mut self, fxn_id: u64, dst: Register, args: Vec<Register>) {
92    self.instrs.push(EncodedInstr::VarArg { fxn_id, dst, args });
93  }
94  pub fn emit_ret(&mut self, src: Register) {
95    self.instrs.push(EncodedInstr::Ret { src })
96  }
97  
98  pub fn compile_const(&mut self, bytes: &[u8], value_kind: ValueKind) -> MResult<u32> {
99    let type_id = self.types.get_or_intern(&value_kind);
100    let align = value_kind.align();
101    let next_blob_len = self.const_blob.len() as u64;
102    let padded_off = align_up(next_blob_len, align as u64);
103    if padded_off > next_blob_len {
104      // add zero bytes padding to align the next write
105      self.const_blob.resize(padded_off as usize, 0);
106    }
107    self.features.insert(FeatureFlag::Builtin(value_kind.to_feature_kind()));
108    let offset = self.const_blob.len() as u64;
109    self.const_blob.extend_from_slice(bytes);
110    let length = (self.const_blob.len() as u64) - offset;    
111    let entry = ConstEntry {
112      type_id,
113      enc: ConstEncoding::Inline,
114      align: align as u8,
115      flags: 0,
116      reserved: 0,
117      offset,
118      length,
119    };
120    let const_id = self.const_entries.len() as u32;
121    self.const_entries.push(entry);
122    Ok(const_id)    
123  }
124
125  pub fn compile(&mut self) -> MResult<Vec<u8>> {
126
127    let header_size = ByteCodeHeader::HEADER_SIZE as u64;
128    let feat_bytes_len: u64 = 4 + (self.features.len() as u64) * 8;
129    let types_bytes_len: u64 = self.types.byte_len();
130    let const_tbl_len: u64 = (self.const_entries.len() as u64) * ConstEntry::byte_len();
131    let const_blob_len: u64 = self.const_blob.len() as u64;
132    let symbols_len: u64 = (self.symbols.len() as u64) * 13; // 8 bytes for id, 1 byte for mutable, 4 for reg
133    let instr_bytes_len: u64 = self.instrs.iter().map(|i| i.byte_len()).sum();
134    let dict_len: u64 = self.dictionary.values().map(|s| s.len() as u64 + 12).sum(); // 8 bytes for id, 4 for string length
135
136    let mut offset = header_size;                           // bytes in header
137    let feature_off = offset; offset += feat_bytes_len;     // offset to feature section
138    let types_off = offset; offset += types_bytes_len;      // offset to types section
139    let const_tbl_off = offset; offset += const_tbl_len;    // offset to constant table
140    let const_blob_off = offset; offset += const_blob_len;  // offset to constant blob
141    let symbols_off = offset; offset += symbols_len;        // offset to symbol section
142    let instr_off = offset; offset += instr_bytes_len;      // offset to instruction stream
143    let dict_off = offset; offset += dict_len;              // offset to dictionary section
144    
145    let file_len_before_trailer = offset;
146    let trailer_len = 4u64;
147    let full_file_len = file_len_before_trailer + trailer_len;
148
149    // The header!
150    let header = ByteCodeHeader {
151      magic: *b"MECH",
152      version: 1,             
153      mech_ver: parse_version_to_u16(env!("CARGO_PKG_VERSION")).unwrap(),
154      flags: 0,
155      reg_count: self.next_reg,
156      instr_count: self.instrs.len() as u32,
157      feature_count: self.features.len() as u32,
158      feature_off,
159      
160      types_count: self.types.entries.len() as u32,
161      types_off,
162
163      const_count: self.const_entries.len() as u32,
164      const_tbl_off,
165      const_tbl_len,
166      const_blob_off,
167      const_blob_len,
168
169      symbols_len,
170      symbols_off,
171
172      instr_off,
173      instr_len: instr_bytes_len,
174
175      dict_len,
176      dict_off,
177
178      reserved: 0,
179    };
180    
181    let mut buf = Cursor::new(Vec::<u8>::with_capacity(full_file_len as usize));
182
183    // 1. Write the header
184    header.write_to(&mut buf)?;
185
186    // 2. Write features
187    buf.write_u32::<LittleEndian>(self.features.len() as u32)?;
188    for f in &self.features {
189      buf.write_u64::<LittleEndian>(f.as_u64())?;
190    }
191
192    // 3. Write types
193    self.types.write_to(&mut buf)?;
194
195    // 4. write consts
196    for entry in &self.const_entries {
197      entry.write_to(&mut buf)?;
198    }
199
200    if !self.const_blob.is_empty() {
201      buf.write_all(&self.const_blob)?;
202    }
203
204    // 5. write symbols
205    for (id, reg) in &self.symbols {
206      let mutable = self.mutable_symbols.contains(id);
207      let entry = SymbolEntry::new(*id, mutable, *reg);
208      entry.write_to(&mut buf)?;
209    }
210
211    // 6. write instructions. This is where the action is!
212    for ins in &self.instrs {
213      ins.write_to(&mut buf)?;
214    }
215
216    // 7. write dictionary
217    for (id, name) in &self.dictionary {
218      let dict_entry = DictEntry::new(*id, name);
219      dict_entry.write_to(&mut buf)?;
220    }
221
222    // sanity check: the position should equal file_len_before_trailer
223    let pos = buf.position();
224    if pos != file_len_before_trailer {
225      return Err(MechError {file: file!().to_string(),tokens: vec![],msg: format!("Buffer position mismatch: expected {}, got {}", file_len_before_trailer, pos),id: line!(),kind: MechErrorKind::GenericError("Buffer position mismatch".to_string()),});
226    }
227
228    let bytes_so_far = buf.get_ref().as_slice();
229    let checksum = crc32fast::hash(bytes_so_far);
230    buf.write_u32::<LittleEndian>(checksum)?;
231
232    if buf.position() != full_file_len {
233      return Err(MechError {file: file!().to_string(),tokens: vec![],msg: format!("Final buffer length mismatch: expected {}, got {}", full_file_len, buf.position()),id: line!(),kind: MechErrorKind::GenericError("Final buffer length mismatch".to_string()),});
234    }
235
236    Ok(buf.into_inner())
237  }
238}
239
240#[inline]
241fn align_up(offset: u64, align: u64) -> u64 {
242  if align == 0 { return offset; }
243  ((offset + align - 1) / align) * align
244}