forgot to commit these
This commit is contained in:
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub struct FunctionContext {
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
stack_offset: i32,
|
||||||
|
|
||||||
|
registers: [(Register, bool); 16],
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user