use anyhow::*;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
pub struct CodeGen<'a> {
module: &'a IRModule,
data_stack_size: usize,
}
+
+// Some inspiration
+// ================
+//
+// * https://github.com/aw/fiveforths/blob/master/docs/REFERENCE.md#registers-list
+// * Except using sp as a more C ABI style stack pointer, and s2 for the data stack
+//
+
+// Implementation Choices
+// ======================
+//
+// Data Stack pointer: s2
+// No return stack pointer (using C ABI, so sp, sorta)
+// Use t0, t1, t2 for temporary values in words
+// Data stack grows down
+
+
+macro_rules! asm_macro {
+ ($name:ident, $src:expr) => {
+ fn $name(lines: &mut Vec<String>) {
+ lines.push($src.to_string());
+ }
+ };
+ ($name:ident, $src:expr, $arg0:ty) => {
+ fn $name(lines: &mut Vec<String>, val0: $arg0) {
+ lines.push(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));
+ }
+ };
+}
+
+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 {
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());
// Code
// TODO align?
lines.push(".text\n".to_string());
+ let mut if_block_count = 0;
+ let mut if_stack = vec![];
+ let mut seen_else = HashSet::new();
+
for ir in &self.module.text {
match ir {
IR::Label(name) => {
lines.push(" addi sp, sp, 16".to_string()); // restore stack pointer
lines.push(" ret".to_string());
},
+ 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");
+ },
+ 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);
+ },
+ IR::StackPush(num) => {
+ lines.push(format!(" li t0, {}", num));
+ push_from(&mut lines, "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");
+ },
+ 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");
+ },
+ IR::Dup => {
+ copy_top_stack_value_to(&mut lines, "t0");
+ push_from(&mut lines, "t0");
+ },
+ IR::Swap => {
+ pop_to(&mut lines, "t0");
+ pop_to(&mut lines, "t1");
+ push_from(&mut lines, "t0");
+ push_from(&mut lines, "t1");
+ },
+ IR::Drop => {
+ move_stack_ptr_by_cells(&mut lines, 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");
+ },
+ 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");
+ },
+ 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");
+ },
+ IR::Sys0 => {
+ pop_to(&mut lines, "a7");
+ lines.push(" ecall".to_string());
+ push_from(&mut lines, "a0");
+ },
+ IR::Sys1 => {
+ pop_to(&mut lines, "a7");
+ pop_to(&mut lines, "a0");
+ lines.push(" ecall".to_string());
+ push_from(&mut lines, "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");
+ },
+ 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");
+ },
+ 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");
+ },
+ 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");
+ },
+ 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");
+ },
+ IR::PutN => {
+ lines.push(" call putn".to_string());
+ },
+ // 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));
+ 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));
+ 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));
+ } else {
+ lines.push(format!("_endif_{}:", if_counter));
+ seen_else.remove(&if_counter);
+ }
+ stack.pop();
+ }
_ => bail!("not implemented yet"),
}
}
Ok(lines.join("\n"))
}
}
+