use anyhow::*;
use std::collections::{HashMap, HashSet};
+use std::fmt::Display;
pub struct CodeGen<'a> {
module: &'a IRModule,
data_stack_size: usize,
+ lines: Vec<String>,
}
macro_rules! asm_macro {
($name:ident, $src:expr) => {
- fn $name(lines: &mut Vec<String>) {
- lines.push($src.to_string());
+ fn $name(&mut self) {
+ self.ine($src);
}
};
($name:ident, $src:expr, $arg0:ty) => {
- fn $name(lines: &mut Vec<String>, val0: $arg0) {
- lines.push(format!($src, val0));
+ fn $name(&mut self, val0: $arg0) {
+ self.line(format!($src, val0));
}
};
($name:ident, $src:expr, $arg0:ty, $arg1:ty) => {
- fn $name(lines: &mut Vec<String>, val0: $arg0, val1: $arg1) {
- lines.push(format!($src, val0, val1));
+ fn $name(&mut self, val0: $arg0, val1: $arg1) {
+ self.line(format!($src, val0, val1));
}
};
}
-asm_macro!(copy_top_stack_value_to, " lw {}, 0(s2)", &str);
-asm_macro!(copy_offset_stack_value_to, " lw {}, {}*8(s2)", &str, isize);
-asm_macro!(deref_pointer_to_from, " lw {}, 0({})", &str, &str);
-asm_macro!(copy_to_top_of_stack, " sw {}, 0(s2)", &str);
-asm_macro!(move_stack_ptr_by_cells, " addi s2, s2, {}*8", isize);
-
-fn pop_to(lines: &mut Vec<String>, reg: &str) {
- copy_top_stack_value_to(lines, reg);
- move_stack_ptr_by_cells(lines, 1);
-}
-
-fn push_from(lines: &mut Vec<String>, reg: &str) {
- move_stack_ptr_by_cells(lines, -1);
- copy_to_top_of_stack(lines, reg);
-}
-
-
impl<'a> CodeGen<'a> {
pub fn new(ir_mod: &'a IRModule, data_stack_size: usize) -> Self {
Self {
module: ir_mod,
- data_stack_size
+ data_stack_size,
+ lines: vec![],
}
}
+ fn line<S: Display>(&mut self, line: S) {
+ self.lines.push(format!(" {}", line));
+ }
+
+ fn label<S: Display>(&mut self, line: S) {
+ self.lines.push(line.to_string());
+ }
+
+ asm_macro!(copy_top_stack_value_to, " lw {}, 0(s2)", &str);
+ asm_macro!(copy_offset_stack_value_to, " lw {}, {}*8(s2)", &str, isize);
+ asm_macro!(deref_pointer_to_from, " lw {}, 0({})", &str, &str);
+ asm_macro!(copy_to_top_of_stack, " sw {}, 0(s2)", &str);
+ asm_macro!(move_stack_ptr_by_cells, " addi s2, s2, {}*8", isize);
+
+ fn pop_to(&mut self, reg: &str) {
+ self.copy_top_stack_value_to(reg);
+ self.move_stack_ptr_by_cells(1);
+ }
+
+ fn push_from(&mut self, reg: &str) {
+ self.move_stack_ptr_by_cells(-1);
+ self.copy_to_top_of_stack(reg);
+ }
+
pub fn assembly(&mut self) -> Result<String>{
- let mut lines = vec![];
let mut string_table = HashMap::new();
let mut string_index = 0;
// Static strings
- lines.push(".rodata\n".to_string());
+ self.label(".section .rodata\n");
for ir in &self.module.data {
match ir {
IR::StringDef(some_string) => {
string_table.insert(some_string.clone(), string_index);
- lines.push(format!("string_id_{}:", string_index));
- lines.push(format!(" .asciz \"{}\"", some_string)); // should this be .asciz?
- lines.push("".to_string());
+ self.label(format!("string_id_{}:", string_index));
+ self.line(format!(".asciz \"{}\"", some_string)); // should this be .asciz?
+ self.label("");
string_index += 1;
},
}
// Data stack
- lines.push(".data\n".to_string());
- lines.push("data_stack:".to_string());
- lines.push(format!(" .space {}", self.data_stack_size));
- lines.push("data_stack_end:".to_string());
+ self.label(".data\n");
+ self.label("data_stack:");
+ self.line(format!(".space {}", self.data_stack_size));
+ self.label("data_stack_end:");
// Code
- // TODO align?
- lines.push(".text\n".to_string());
+ self.label(".text\n");
+ self.label(".align 3\n");
let mut if_block_count = 0;
let mut if_stack = vec![];
let mut seen_else = HashSet::new();
+ let mut last_label = "";
for ir in &self.module.text {
match ir {
IR::Label(name) => {
- lines.push(format!("{}:", name));
- lines.push(" addi sp, sp, -16".to_string()); // allocate 16 bytes on stack
- lines.push(" sw ra, 8(sp)".to_string()); // store return address on stack
+ last_label = name;
+ if name == "main" {
+ self.label(".globl _start");
+ self.label("_start:");
+ self.line("la s2, data_stack_end"); // set stack pointer
+ } else {
+ self.label(format!(".globl {}", name));
+ self.label(format!("{}:", name));
+ }
+ self.line("addi sp, sp, -16"); // allocate 16 bytes on stack
+ self.line("sw ra, 8(sp)"); // store return address on stack
},
IR::Call(name) => {
- lines.push(format!(" call {}", name));
+ self.line(format!("call {}", name));
},
IR::Ret => {
- lines.push(" lw ra, 8(sp)".to_string()); // load return address from stack
- lines.push(" addi sp, sp, 16".to_string()); // restore stack pointer
- lines.push(" ret".to_string());
+ self.line("lw ra, 8(sp)"); // load return address from stack
+ self.line("addi sp, sp, 16"); // restore stack pointer
+ if last_label == "main" {
+ // exit 0 syscall
+ self.line("li a7, 93");
+ self.line("mv a0, x0");
+ self.line("ecall");
+ } else {
+ self.line("ret");
+ }
},
IR::Load => {
- copy_top_stack_value_to(&mut lines, "t0");
- deref_pointer_to_from(&mut lines, "t0", "t0");
- copy_to_top_of_stack(&mut lines, "t0");
+ self.copy_top_stack_value_to("t0");
+ self.deref_pointer_to_from("t0", "t0");
+ self.copy_to_top_of_stack("t0");
},
IR::Store => { // ( x addr -- )
- copy_top_stack_value_to(&mut lines, "t1");
- copy_offset_stack_value_to(&mut lines, "t0", 1);
- lines.push(" sw t0, 0(t1)".to_string()); // store x at addr
- move_stack_ptr_by_cells(&mut lines, 2);
+ self.copy_top_stack_value_to("t1");
+ self.copy_offset_stack_value_to("t0", 1);
+ self.line("sw t0, 0(t1)"); // store x at addr
+ self.move_stack_ptr_by_cells(2);
},
IR::StackPush(num) => {
- lines.push(format!(" li t0, {}", num));
- push_from(&mut lines, "t0");
+ self.line(format!("li t0, {}", num));
+ self.push_from("t0");
},
IR::AddU64 => {
- pop_to(&mut lines, "t0");
- pop_to(&mut lines, "t1");
- lines.push(" addi t0, t0, t1".to_string());
- push_from(&mut lines, "t0");
+ self.pop_to("t0");
+ self.pop_to("t1");
+ self.line("add t0, t0, t1");
+ self.push_from("t0");
},
IR::SubtractU64 => {
- pop_to(&mut lines, "t1");
- pop_to(&mut lines, "t0");
- lines.push(" sub t0, t0, t1".to_string());
- push_from(&mut lines, "t0");
+ self.pop_to("t1");
+ self.pop_to("t0");
+ self.line("sub t0, t0, t1");
+ self.push_from("t0");
},
IR::Dup => {
- copy_top_stack_value_to(&mut lines, "t0");
- push_from(&mut lines, "t0");
+ self.copy_top_stack_value_to("t0");
+ self.push_from("t0");
},
IR::Swap => {
- pop_to(&mut lines, "t0");
- pop_to(&mut lines, "t1");
- push_from(&mut lines, "t0");
- push_from(&mut lines, "t1");
+ self.pop_to("t0");
+ self.pop_to("t1");
+ self.push_from("t0");
+ self.push_from("t1");
},
IR::Drop => {
- move_stack_ptr_by_cells(&mut lines, 1);
+ self.move_stack_ptr_by_cells(1);
},
IR::Equals => {
// Yes, this is the same as subtract, since we're treating 0 as true, and
// others as false.
- pop_to(&mut lines, "t1");
- pop_to(&mut lines, "t0");
- lines.push(" sub t0, t0, t1".to_string());
- push_from(&mut lines, "t0");
+ self.pop_to("t1");
+ self.pop_to("t0");
+ self.line("sub t0, t0, t1");
+ self.push_from("t0");
},
IR::GreaterThan => {
- pop_to(&mut lines, "t1");
- pop_to(&mut lines, "t0");
- lines.push(" sgt t0, t0, t1".to_string());
- lines.push(" seqz t0, t0".to_string()); // remember, 0 is true, others are
- // false
- push_from(&mut lines, "t0");
+ self.pop_to("t1");
+ self.pop_to("t0");
+ self.line("sgt t0, t0, t1");
+ self.line("seqz t0, t0"); // remember, 0 is true, others are false
+ self.push_from("t0");
},
IR::BitwiseOr => {
- pop_to(&mut lines, "t1");
- pop_to(&mut lines, "t0");
- lines.push(" or t0, t0, t1".to_string());
- push_from(&mut lines, "t0");
+ self.pop_to("t1");
+ self.pop_to("t0");
+ self.line("or t0, t0, t1");
+ self.push_from("t0");
},
IR::Sys0 => {
- pop_to(&mut lines, "a7");
- lines.push(" ecall".to_string());
- push_from(&mut lines, "a0");
+ self.pop_to("a7");
+ self.line("ecall");
+ self.push_from("a0");
},
IR::Sys1 => {
- pop_to(&mut lines, "a7");
- pop_to(&mut lines, "a0");
- lines.push(" ecall".to_string());
- push_from(&mut lines, "a0");
+ self.pop_to("a7");
+ self.pop_to("a0");
+ self.line("ecall");
+ self.push_from("a0");
},
IR::Sys2 => {
- pop_to(&mut lines, "a7");
- pop_to(&mut lines, "a1");
- pop_to(&mut lines, "a0");
- lines.push(" ecall".to_string());
- push_from(&mut lines, "a0");
+ self.pop_to("a7");
+ self.pop_to("a1");
+ self.pop_to("a0");
+ self.line("ecall");
+ self.push_from("a0");
},
IR::Sys3 => {
- pop_to(&mut lines, "a7");
- pop_to(&mut lines, "a2");
- pop_to(&mut lines, "a1");
- pop_to(&mut lines, "a0");
- lines.push(" ecall".to_string());
- push_from(&mut lines, "a0");
+ self.pop_to("a7");
+ self.pop_to("a2");
+ self.pop_to("a1");
+ self.pop_to("a0");
+ self.line("ecall");
+ self.push_from("a0");
},
IR::Sys4 => {
- pop_to(&mut lines, "a7");
- pop_to(&mut lines, "a3");
- pop_to(&mut lines, "a2");
- pop_to(&mut lines, "a1");
- pop_to(&mut lines, "a0");
- lines.push(" ecall".to_string());
- push_from(&mut lines, "a0");
+ self.pop_to("a7");
+ self.pop_to("a3");
+ self.pop_to("a2");
+ self.pop_to("a1");
+ self.pop_to("a0");
+ self.line("ecall");
+ self.push_from("a0");
},
IR::Sys5 => {
- pop_to(&mut lines, "a7");
- pop_to(&mut lines, "a4");
- pop_to(&mut lines, "a3");
- pop_to(&mut lines, "a2");
- pop_to(&mut lines, "a1");
- pop_to(&mut lines, "a0");
- lines.push(" ecall".to_string());
- push_from(&mut lines, "a0");
+ self.pop_to("a7");
+ self.pop_to("a4");
+ self.pop_to("a3");
+ self.pop_to("a2");
+ self.pop_to("a1");
+ self.pop_to("a0");
+ self.line("ecall");
+ self.push_from("a0");
},
IR::Sys6 => {
- pop_to(&mut lines, "a7");
- pop_to(&mut lines, "a5");
- pop_to(&mut lines, "a4");
- pop_to(&mut lines, "a3");
- pop_to(&mut lines, "a2");
- pop_to(&mut lines, "a1");
- pop_to(&mut lines, "a0");
- lines.push(" ecall".to_string());
- push_from(&mut lines, "a0");
+ self.pop_to("a7");
+ self.pop_to("a5");
+ self.pop_to("a4");
+ self.pop_to("a3");
+ self.pop_to("a2");
+ self.pop_to("a1");
+ self.pop_to("a0");
+ self.line("ecall");
+ self.push_from("a0");
},
IR::PutN => {
- lines.push(" call putn".to_string());
+ self.line("call putn");
},
// https://cmput229.github.io/229-labs-RISCV/RISC-V-Examples_Public/03-Conditionals/03b-If_Else.html
IR::If => {
- pop_to(&mut lines, "t0");
- lines.push(format!(" bnez t0, _else_{}", if_block_count));
+ self.pop_to("t0");
+ self.line(format!("bnez t0, _else_{}", if_block_count));
if_stack.push(if_block_count);
if_block_count += 1;
},
IR::Else => {
let if_counter = if_stack.last().unwrap().clone();
- lines.push(format!(" j _endif_{}", if_counter));
- lines.push(format!("_else_{}:", if_counter));
+ self.line(format!("j _endif_{}", if_counter));
+ self.label(format!("_else_{}:", if_counter));
seen_else.insert(if_counter);
},
IR::EndIf => {
let stack = &mut if_stack;
let if_counter = stack.last().unwrap().clone();
if !seen_else.contains(&if_counter) {
- lines.push(format!("_else_{}:", if_counter));
+ self.label(format!("_else_{}:", if_counter));
} else {
- lines.push(format!("_endif_{}:", if_counter));
+ self.label(format!("_endif_{}:", if_counter));
seen_else.remove(&if_counter);
}
stack.pop();
}
}
- Ok(lines.join("\n"))
+ Ok(self.lines.join("\n"))
}
}