forgot to commit these

This commit is contained in:
2026-02-10 16:37:33 +00:00
parent 8361833b1c
commit 75ad04cf95
3 changed files with 803 additions and 0 deletions
+610
View File
@@ -0,0 +1,610 @@
use std::fmt;
use crate::{
backend::dsa::registers::Register,
model::{CompilerError, Expression},
};
pub struct CodeGen {
// For building the final program
program: InsBlock,
// For generating temporary blocks
label_counter: usize,
stack_offset: i32,
}
impl CodeGen {
pub fn new() -> Self {
Self {
program: InsBlock::new(),
label_counter: 0,
stack_offset: 0,
}
}
/// Emit directly to program (for top-level constructs)
pub fn emit(&mut self, instr: Instruction) {
self.program.push(instr);
}
/// Emit a block to program
pub fn emit_block(&mut self, block: InsBlock) {
self.program.append(block);
}
/// Build expression (returns block for composition)
pub fn build_expr(&mut self, expr: &Expression) -> Result<InsBlock, CompilerError> {
// ... returns InstrBlock
todo!()
}
/// Get final output
pub fn finish(mut self) -> String {
// Optimize before final output
// self.program.remove_dead_code();
// self.program.optimize_peephole();
self.program
.instructions
.iter()
.map(|i| i.to_string())
.collect::<Vec<_>>()
.join("\n")
}
}
pub struct InsBlock {
instructions: Vec<Instruction>,
}
impl InsBlock {
pub fn new() -> Self {
Self {
instructions: vec![],
}
}
pub fn push(&mut self, instr: Instruction) {
self.instructions.push(instr);
}
pub fn append(&mut self, mut other: Self) {
self.instructions.append(&mut other.instructions);
}
pub fn extend(&mut self, instrs: impl IntoIterator<Item = Instruction>) {
self.instructions.extend(instrs);
}
pub fn is_empty(&self) -> bool {
self.instructions.is_empty()
}
pub fn len(&self) -> usize {
self.instructions.len()
}
pub fn iter(&self) -> impl Iterator<Item = &Instruction> {
self.instructions.iter()
}
}
pub enum Instruction {
// Labels and comments
Label(Label),
Comment(String),
// Data movement
Mov {
src: Register,
dest: Register,
},
Movs {
src: Register,
dest: Register,
},
// Memory operations
Ldb {
addr: MemOperand,
dest: Register,
},
Ldh {
addr: MemOperand,
dest: Register,
},
Ldw {
addr: MemOperand,
dest: Register,
},
Stb {
src: Register,
addr: MemOperand,
},
Sth {
src: Register,
addr: MemOperand,
},
Stw {
src: Register,
addr: MemOperand,
},
// Immediate loads
Lli {
imm: Imm,
dest: Register,
},
Lui {
imm: Imm,
dest: Register,
},
// Arithmetic
Add {
src1: Register,
src2: Register,
dest: Register,
},
Sub {
src1: Register,
src2: Register,
dest: Register,
},
IAdd {
src: Register,
imm: Imm,
dest: Option<Register>,
},
ISub {
src: Register,
imm: Imm,
dest: Option<Register>,
},
Inc {
reg: Register,
},
Dec {
reg: Register,
},
// Bitwise
And {
src1: Register,
src2: Register,
dest: Register,
},
Or {
src1: Register,
src2: Register,
dest: Register,
},
Xor {
src1: Register,
src2: Register,
dest: Register,
},
Not {
src: Register,
dest: Register,
},
Nand {
src1: Register,
src2: Register,
dest: Register,
},
Nor {
src1: Register,
src2: Register,
dest: Register,
},
Xnor {
src1: Register,
src2: Register,
dest: Register,
},
// Shifts
Shl {
src1: Register,
r_shamt: Register,
i_shamt: u16,
dest: Register,
},
Shr {
src1: Register,
rsh: Register,
ish: u16,
dest: Register,
},
// Comparison
Cmp {
reg1: Register,
reg2: Register,
},
// Jumps
Jmp {
target: Label,
},
Jeq {
target: Label,
},
Jne {
target: Label,
},
Jgt {
target: Label,
},
Jge {
target: Label,
},
Jlt {
target: Label,
},
Jle {
target: Label,
},
// Stack
Push {
reg: Register,
},
Pop {
reg: Register,
},
// Function calls
Call {
target: String,
}, // namespace::function
Return,
// System
Hlt,
Nop,
Int {
code: u8,
},
}
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Label(l) => write!(f, "{}:", l),
Self::Comment(c) => write!(f, "; {}", c),
Self::Mov { src, dest } => write!(f, " mov {}, {}", src, dest),
Self::Movs { src, dest } => write!(f, " movs {}, {}", src, dest),
Self::Ldb { addr, dest } => {
write!(f, " ldb {}, {}", format_mem_operand(addr), dest)
}
Self::Ldh { addr, dest } => {
write!(f, " ldh {}, {}", format_mem_operand(addr), dest)
}
Self::Ldw { addr, dest } => {
write!(f, " ldw {}, {}", format_mem_operand(addr), dest)
}
// Self::Ldbs { addr, dest } => {
// write!(f, " ldbs {}, {}", format_mem_operand(addr), dest)
// }
// Self::Ldhs { addr, dest } => {
// write!(f, " ldhs {}, {}", format_mem_operand(addr), dest)
// }
// Self::Ldws { addr, dest } => {
// write!(f, " ldws {}, {}", format_mem_operand(addr), dest)
// }
Self::Stb { src, addr } => {
write!(f, " stb {}, {}", src, format_mem_operand(addr))
}
Self::Sth { src, addr } => {
write!(f, " sth {}, {}", src, format_mem_operand(addr))
}
Self::Stw { src, addr } => {
write!(f, " stw {}, {}", src, format_mem_operand(addr))
}
Self::Lli { imm, dest } => write!(f, " lli {}, {}", imm, dest),
Self::Lui { imm, dest } => write!(f, " lui {}, {}", imm, dest),
// arithmetic
Self::Add { src1, src2, dest } => {
write!(f, " add {}, {}, {}", src1, src2, dest)
}
Self::Sub { src1, src2, dest } => {
write!(f, " sub {}, {}, {}", src1, src2, dest)
}
Self::And { src1, src2, dest } => {
write!(f, " and {}, {}, {}", src1, src2, dest)
}
Self::Or { src1, src2, dest } => {
write!(f, " or {}, {}, {}", src1, src2, dest)
}
Self::Nand { src1, src2, dest } => {
write!(f, " nand {}, {}, {}", src1, src2, dest)
}
Self::Xor { src1, src2, dest } => {
write!(f, " xor {}, {}, {}", src1, src2, dest)
}
Self::Nor { src1, src2, dest } => {
write!(f, " nor {}, {}, {}", src1, src2, dest)
}
Self::Not { src, dest } => {
write!(f, " not {} {}", src, dest)
}
Self::Xnor { src1, src2, dest } => {
write!(f, " xnor {}, {}, {}", src1, src2, dest)
}
Self::IAdd { src, imm, dest } => {
if let Some(d) = dest {
write!(f, " iadd {}, {}, {}", src, imm, d)
} else {
write!(f, " iadd {}, {}", src, imm)
}
}
Self::ISub { src, imm, dest } => {
if let Some(d) = dest {
write!(f, " isub {}, {}, {}", src, imm, d)
} else {
write!(f, " isub {}, {}", src, imm)
}
}
// shift instructions
Self::Shl {
src1,
r_shamt,
i_shamt,
dest,
} => {
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
}
Self::Shr {
src1,
rsh: r_shamt,
ish: i_shamt,
dest,
} => {
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
}
// increment instructions
Self::Inc { reg } => write!(f, " inc {}", reg),
Self::Dec { reg } => write!(f, " dec {}", reg),
Self::Cmp { reg1, reg2 } => write!(f, " cmp {}, {}", reg1, reg2),
// jump instructions
Self::Jmp { target } => write!(f, " jmp {}", target),
Self::Jeq { target } => write!(f, " jeq {}", target),
Self::Jne { target } => write!(f, " jne {}", target),
Self::Jgt { target } => write!(f, " jgt {}", target),
Self::Jge { target } => write!(f, " jge {}", target),
Self::Jlt { target } => write!(f, " jlt {}", target),
Self::Jle { target } => write!(f, " jle {}", target),
// stack pseudoinstructions
Self::Push { reg } => write!(f, " push {}", reg),
Self::Pop { reg } => write!(f, " pop {}", reg),
// call & return pseudoinstructions
Self::Call { target } => write!(f, " call {}", target),
Self::Return => write!(f, " return"),
// misc instructions
Self::Int { code } => write!(f, " int {}", code),
Self::Hlt => write!(f, " hlt"),
Self::Nop => write!(f, " nop"),
}
}
}
impl Instruction {
// Movement
pub fn mov(src: Register, dest: Register) -> Self {
Self::Mov { src, dest }
}
// Memory loads
pub fn ldw_reg(base: Register, dest: Register) -> Self {
Self::Ldw {
addr: MemOperand::RegIndirect(base),
dest,
}
}
pub fn ldw_reg_offset(base: Register, offset: i32, dest: Register) -> Self {
Self::Ldw {
addr: MemOperand::RegOffset(base, offset),
dest,
}
}
pub fn ldw_label(label: impl Into<Label>, dest: Register) -> Self {
Self::Ldw {
addr: MemOperand::Label(label.into()),
dest,
}
}
// Memory stores
pub fn stw_reg(src: Register, base: Register) -> Self {
Self::Stw {
src,
addr: MemOperand::RegIndirect(base),
}
}
pub fn stw_reg_offset(src: Register, base: Register, offset: i32) -> Self {
Self::Stw {
src,
addr: MemOperand::RegOffset(base, offset),
}
}
// Arithmetic
pub fn add(src1: Register, src2: Register, dest: Register) -> Self {
Self::Add { src1, src2, dest }
}
pub fn sub(src1: Register, src2: Register, dest: Register) -> Self {
Self::Sub { src1, src2, dest }
}
pub fn iadd(src: Register, imm: i32) -> Self {
Self::IAdd {
src,
imm: Imm(imm),
dest: None,
}
}
pub fn iadd_dest(src: Register, imm: i32, dest: Register) -> Self {
Self::IAdd {
src,
imm: Imm(imm),
dest: Some(dest),
}
}
pub fn inc(reg: Register) -> Self {
Self::Inc { reg }
}
pub fn dec(reg: Register) -> Self {
Self::Dec { reg }
}
// Immediate loads
pub fn load_imm32(value: u32, dest: Register) -> Vec<Self> {
let lower = (value & 0xFFFF) as i32;
let upper = ((value >> 16) & 0xFFFF) as i32;
vec![
Self::Lli {
imm: Imm(lower),
dest,
},
Self::Lui {
imm: Imm(upper),
dest,
},
]
}
// Control flow
pub fn label(name: impl Into<String>) -> Self {
Self::Label(Label(name.into()))
}
pub fn jmp(target: impl Into<Label>) -> Self {
Self::Jmp {
target: target.into(),
}
}
pub fn jeq(target: impl Into<Label>) -> Self {
Self::Jeq {
target: target.into(),
}
}
pub fn cmp(reg1: Register, reg2: Register) -> Self {
Self::Cmp { reg1, reg2 }
}
// Stack
pub fn push(reg: Register) -> Self {
Self::Push { reg }
}
pub fn pop(reg: Register) -> Self {
Self::Pop { reg }
}
// Functions
pub fn call(target: impl Into<String>) -> Self {
Self::Call {
target: target.into(),
}
}
pub fn int(code: u8) -> Self {
Self::Int { code }
}
pub fn ret() -> Self {
Self::Return
}
// Utilities
pub fn comment(text: impl Into<String>) -> Self {
Self::Comment(text.into())
}
}
// Convenience trait for Label conversion
impl From<String> for Label {
fn from(s: String) -> Self {
Label(s)
}
}
impl From<&str> for Label {
fn from(s: &str) -> Self {
Label(s.to_string())
}
}
fn format_mem_operand(op: &MemOperand) -> String {
match op {
MemOperand::RegIndirect(reg) => format!("{}", reg),
MemOperand::RegOffset(reg, offset) => {
if *offset >= 0 {
format!("{}, {}", reg, offset)
} else {
format!("{}, {}", reg, offset)
}
}
MemOperand::Label(label) => format!("{}", label),
MemOperand::LabelOffset(label, offset) => {
format!("{}, {}", label, offset)
}
}
}
/// Memory operand for loads/stores
#[derive(Debug, Clone)]
pub enum MemOperand {
/// Register indirect: [reg]
RegIndirect(Register),
/// Register with offset: [reg + offset]
RegOffset(Register, i32),
/// Label: [label]
Label(Label),
/// Label with offset: [label + offset]
LabelOffset(Label, i32),
}
/// Immediate value (16-bit or 32-bit)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Imm(pub i32);
impl fmt::Display for Imm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
/// Label reference
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Label(pub String);
impl fmt::Display for Label {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
+7
View File
@@ -0,0 +1,7 @@
pub struct FunctionContext {
name: String,
stack_offset: i32,
registers: [(Register, bool); 16],
}
+186
View File
@@ -0,0 +1,186 @@
use std::{collections::HashMap, hash::Hash};
use crate::{
backend::dsa::{
instruction::{InsBlock, Reg},
registers::Register,
},
model::{CompilerError, TypeId},
};
pub struct Variable {
name: String,
/// the type of the variable.
r#type: TypeId,
/// size taken up in bytes.
/// if size > 4, value must be stored on the stack.
size: usize,
// location
/// this must be None if it cannot be stored in a register.
allocated_register: Option<Register>,
/// represents the offset from the base pointer (Bpr) of the stack frame.
/// needs to be signed as offset is positive for function args and negative for local
/// variables. as we can't access values at negative offsets, we use the following
/// formula: addr = Spr + offset - (Spr - Bpr) where we know (Spr-Bpr) at compile
/// time.
bpr_offset: Option<isize>,
}
impl Variable {
pub fn new_uninit(name: String, r#type: TypeId) -> Self {
Self {
name,
size: r#type.size(),
r#type,
allocated_register: None,
bpr_offset: None,
}
}
fn alloc_register(
&mut self,
scope: &'_ mut Scope,
) -> Result<Register, CompilerError> {
if self.size > 4 {
return Err(CompilerError::Generic(format!(
"Type {} cannot be allocated a register as it has a size of {} bytes",
self.r#type, self.size
)));
}
todo!("integrate with register alloc logic")
// self.allocated_register = Some(...)
}
fn alloc_stack(&mut self, scope: &'_ mut Scope) -> Result<usize, CompilerError> {
todo!("integrate with stack alloc logic")
// self.bpr_offset = Some(...)
}
pub fn load(&mut self, scope: &'_ mut Scope) -> Result<Register, CompilerError> {
todo!("load var from stack to reg (if possible)")
}
pub fn new_local(
name: String,
r#type: TypeId,
scope: &'_ mut Scope,
) -> Result<Self, CompilerError> {
let mut var = Self::new_uninit(name, r#type);
var.alloc_register(scope)?;
Ok(var)
}
pub fn new_stack(
name: String,
r#type: TypeId,
scope: &'_ mut Scope,
) -> Result<Self, CompilerError> {
let mut init = Self::new_uninit(name, r#type);
init.alloc_stack(scope)?;
Ok(init)
}
pub fn drop(&mut self, scope: &'_ mut Scope) -> Result<(), CompilerError> {
if let Some(reg) = self.allocated_register {
todo!("dealloc reg in current function")
}
if let Some(offset) = self.bpr_offset {
todo!("free stack slot in current function")
}
Ok(())
}
pub fn spill(&mut self, scope: &'_ mut Scope) -> Result<(), CompilerError> {
todo!()
}
}
/// scope object
pub struct Scope<'a> {
/// outer scope, for a function this will be the global scope.
parent: Option<&'a mut Scope<'a>>,
/// is the scope a function body or just a loop?
/// depending on the type, ending a scope will have different behaviour
r#type: ScopeType,
/// variables
variables: HashMap<String, Variable>,
/// tells us if a given register is being used or not.
/// this can be an array as registers have u8 representation.
in_use: [(Register, bool); 16],
stack_offset: i32,
}
impl<'a> Scope<'a> {
pub fn new(parent: &'a mut Scope<'a>, r#type: ScopeType) -> Scope<'a> {
Self {
stack_offset: parent.stack_offset,
parent: Some(parent),
r#type,
variables: HashMap::new(),
in_use: Register::get_gp().map(|reg| (reg, false)),
}
}
pub fn stack_offset(&self) -> i32 {
self.stack_offset
}
pub fn stack_offset_mut(&mut self) -> &mut i32 {
&mut self.stack_offset
}
pub fn close(&mut self) -> Result<(), CompilerError> {
// closing a scope means we need to drop all variables in scope and free registers.
for (name, var) in self.variables {
if let Some(reg) = var.allocated_register {
}
if let Some(off)
}
for reg in self.in_use
Ok(())
}
pub fn alloc_temp_reg(&mut self) -> Result<(Register, InsBlock), CompilerError> {
todo!()
}
pub fn alloc_var_reg(&mut self) -> Result<(Register, InsBlock), CompilerError> {
todo!()
}
pub fn alloc_var_stack(&mut self) -> Result<(Register, InsBlock), CompilerError> {
todo!()
}
pub fn free_var_stack(&mut self) -> Result<(Register, InsBlock), CompilerError> {
todo!()
}
pub fn free_temp_reg(&mut self) -> Result<(Register, InsBlock), CompilerError> {
todo!()
}
}
#[derive(PartialEq, Copy, Clone, Debug)]
enum ScopeType {
Function,
IfBlock,
LoopBlock,
}