Download crates/subleq-ir/src/subleq_codegen.rs from Snapkitty/rowm-polymorphic-notebook: direct link, hf CLI and curl.
- Browser
- Download file 7.42 kB
-
https://huggingface.co/Snapkitty/rowm-polymorphic-notebook/resolve/main/crates/subleq-ir/src/subleq_codegen.rs
- Command line
-
hf download hf://Snapkitty/rowm-polymorphic-notebook/crates/subleq-ir/src/subleq_codegen.rs
-
curl -L -o subleq_codegen.rs https://huggingface.co/Snapkitty/rowm-polymorphic-notebook/resolve/main/crates/subleq-ir/src/subleq_codegen.rs
7.42 kB
| //! SUBLEQ Codegen β Bytecode to SUBLEQ memory layout | |
| //! | |
| //! Maps bytecode instructions to SUBLEQ memory layout. | |
| //! Each SUBLEQ instruction: M[b] β M[b] - M[a]; if M[b] β€ 0 then IP β c | |
| use crate::bytecode::*; | |
| use anyhow::{Result, anyhow}; | |
| use std::collections::HashMap; | |
| pub struct SubleqCodegen { | |
| memory: Vec<i64>, | |
| next_addr: usize, | |
| register_map: HashMap<Register, usize>, // Reg β Memory address | |
| label_map: HashMap<String, usize>, // Label β Instruction address | |
| } | |
| impl SubleqCodegen { | |
| pub fn new(memory_size: usize) -> Self { | |
| let mut codegen = Self { | |
| memory: vec![0; memory_size], | |
| next_addr: 0, | |
| register_map: HashMap::new(), | |
| label_map: HashMap::new(), | |
| }; | |
| // Allocate fixed addresses for registers | |
| // R0-R15 at addresses 0-15 | |
| for i in 0..16 { | |
| codegen.register_map.insert(Register(i as u8), i); | |
| } | |
| codegen.next_addr = 16; // Code starts at 16 | |
| codegen | |
| } | |
| /// Compile bytecode to SUBLEQ memory layout | |
| pub fn compile(&mut self, bytecode: &Bytecode) -> Result<Vec<i64>> { | |
| // First pass: load constants into high memory | |
| for (idx, val) in bytecode.constants.iter().enumerate() { | |
| let addr = self.allocate_addr(); | |
| if let Some(n) = val.as_i64() { | |
| self.memory[addr] = n; | |
| } | |
| } | |
| // Second pass: generate SUBLEQ instructions | |
| for (i, instr) in bytecode.instructions.iter().enumerate() { | |
| self.compile_instr(instr, bytecode)?; | |
| } | |
| // Emit halt instruction at end (suicide: M[0] β M[0] - M[0]) | |
| self.emit_subleq(0, 0, 0); | |
| Ok(self.memory.clone()) | |
| } | |
| fn compile_instr(&mut self, instr: &Instr, bytecode: &Bytecode) -> Result<()> { | |
| match instr { | |
| Instr::LoadConst { reg, idx } => { | |
| // Copy constant to register | |
| let const_addr = 16 + idx; // Constants start at 16 | |
| let reg_addr = self.get_register_addr(*reg)?; | |
| self.emit_copy(const_addr, reg_addr)?; | |
| } | |
| Instr::Move { dst, src } => { | |
| let src_addr = self.get_register_addr(*src)?; | |
| let dst_addr = self.get_register_addr(*dst)?; | |
| self.emit_copy(src_addr, dst_addr)?; | |
| } | |
| Instr::BinOp { dst, op, src1, src2 } => { | |
| let src1_addr = self.get_register_addr(*src1)?; | |
| let src2_addr = self.get_register_addr(*src2)?; | |
| let dst_addr = self.get_register_addr(*dst)?; | |
| match op { | |
| BinOpCode::Add => self.emit_add(src1_addr, src2_addr, dst_addr)?, | |
| BinOpCode::Sub => self.emit_sub(src1_addr, src2_addr, dst_addr)?, | |
| BinOpCode::Mul => self.emit_mul(src1_addr, src2_addr, dst_addr)?, | |
| _ => return Err(anyhow!("Unsupported binary operation")), | |
| } | |
| } | |
| Instr::Label(label) => { | |
| self.label_map.insert(label.clone(), self.next_addr); | |
| } | |
| Instr::Jump(label) => { | |
| let target_addr = *self.label_map.get(label) | |
| .ok_or_else(|| anyhow!("Undefined label: {}", label))?; | |
| // Unconditional jump: make M[0] <= 0 and jump to target | |
| self.emit_subleq(0, 0, target_addr); | |
| } | |
| Instr::Return(_) => { | |
| // Halt: suicide instruction | |
| self.emit_subleq(0, 0, 0); | |
| } | |
| _ => {} | |
| } | |
| Ok(()) | |
| } | |
| /// Emit SUBLEQ instruction: M[b] β M[b] - M[a]; if M[b] β€ 0 then IP β c | |
| fn emit_subleq(&mut self, a: usize, b: usize, c: usize) { | |
| let ip = self.next_addr; | |
| self.memory[ip] = a as i64; | |
| self.memory[ip + 1] = b as i64; | |
| self.memory[ip + 2] = c as i64; | |
| self.next_addr += 3; | |
| } | |
| /// Copy M[src] to M[dst] | |
| fn emit_copy(&mut self, src: usize, dst: usize) -> Result<()> { | |
| // Clear destination: M[dst] β M[dst] - M[dst] | |
| let next_instr_addr = self.next_addr; | |
| self.emit_subleq(dst, dst, next_instr_addr + 3); | |
| // Decrement source to accumulator: M[acc] β M[acc] - M[src] | |
| let acc = 16; // Use a temporary accumulator | |
| self.emit_subleq(src, acc, next_instr_addr + 6); | |
| // Decrement accumulator to destination: M[dst] β M[dst] - M[acc] | |
| self.emit_subleq(acc, dst, next_instr_addr + 9); | |
| // Restore source: M[src] β M[src] - M[acc] (now negative) | |
| self.emit_subleq(acc, src, next_instr_addr + 12); | |
| Ok(()) | |
| } | |
| /// Add src1 + src2 β dst | |
| fn emit_add(&mut self, src1: usize, src2: usize, dst: usize) -> Result<()> { | |
| let temp = 17; | |
| // Clear destination | |
| self.emit_subleq(dst, dst, self.next_addr + 3); | |
| // Copy src1 to destination (modifies src1) | |
| self.emit_copy(src1, dst)?; | |
| // Subtract negative src2 (equivalent to adding) | |
| self.emit_subleq(temp, src2, self.next_addr + 3); | |
| self.emit_subleq(src2, dst, self.next_addr + 3); | |
| Ok(()) | |
| } | |
| /// Subtract src1 - src2 β dst | |
| fn emit_sub(&mut self, src1: usize, src2: usize, dst: usize) -> Result<()> { | |
| // Clear destination | |
| self.emit_subleq(dst, dst, self.next_addr + 3); | |
| // Copy src1 to dst | |
| self.emit_copy(src1, dst)?; | |
| // Subtract src2 from dst: M[dst] β M[dst] - M[src2] | |
| self.emit_subleq(src2, dst, self.next_addr + 3); | |
| Ok(()) | |
| } | |
| /// Multiply src1 * src2 β dst (naive implementation) | |
| fn emit_mul(&mut self, src1: usize, src2: usize, dst: usize) -> Result<()> { | |
| let counter = 18; | |
| let temp = 19; | |
| // Clear destination | |
| self.emit_subleq(dst, dst, self.next_addr + 3); | |
| // Copy src2 to counter | |
| self.emit_copy(src2, counter)?; | |
| // Loop: while counter > 0, add src1 to dst | |
| let loop_label = self.next_addr; | |
| self.emit_subleq(counter, counter, self.next_addr + 3); | |
| // If counter <= 0, jump to end | |
| let end_label = self.next_addr + 6; | |
| self.emit_subleq(counter, temp, end_label); | |
| // Add src1 to dst | |
| self.emit_subleq(src1, temp, loop_label); | |
| // Jump back | |
| self.emit_subleq(temp, temp, loop_label); | |
| Ok(()) | |
| } | |
| fn get_register_addr(&self, reg: Register) -> Result<usize> { | |
| self.register_map.get(®) | |
| .copied() | |
| .ok_or_else(|| anyhow!("Invalid register")) | |
| } | |
| fn allocate_addr(&mut self) -> usize { | |
| let addr = self.next_addr; | |
| self.next_addr += 1; | |
| addr | |
| } | |
| } | |
| mod tests { | |
| use super::*; | |
| fn test_codegen_create() { | |
| let codegen = SubleqCodegen::new(1024); | |
| assert_eq!(codegen.next_addr, 16); | |
| } | |
| fn test_emit_subleq() { | |
| let mut codegen = SubleqCodegen::new(1024); | |
| codegen.emit_subleq(0, 1, 3); | |
| assert_eq!(codegen.memory[0], 0); | |
| assert_eq!(codegen.memory[1], 1); | |
| assert_eq!(codegen.memory[2], 3); | |
| } | |
| } | |