From 02e65853c456b7e0d9cb98180c96aac02b2f91a6 Mon Sep 17 00:00:00 2001 From: Bryan English Date: Sun, 18 Jan 2026 22:58:34 -0500 Subject: [PATCH] mostly complete asm generation --- hylo-lang/hyloc/src/main.rs | 6 +- hylo-lang/hyloc/src/riscv_asm_codegen.rs | 205 ++++++++++++++++++++++- 2 files changed, 208 insertions(+), 3 deletions(-) diff --git a/hylo-lang/hyloc/src/main.rs b/hylo-lang/hyloc/src/main.rs index 275e031..579930c 100644 --- a/hylo-lang/hyloc/src/main.rs +++ b/hylo-lang/hyloc/src/main.rs @@ -10,7 +10,9 @@ use anyhow::Result; fn main() -> Result<()> { let filename = std::env::args().nth(1).expect("must provide a file to compile"); let module = ir::compile(&filename)?; - let mut interp = Interpreter::new(&module)?; - interp.run()?; + // let mut interp = Interpreter::new(&module)?; + // interp.run()?; + let mut generator = riscv_asm_codegen::CodeGen::new(&module, 4096); + println!("{}", generator.assembly()?); Ok(()) } diff --git a/hylo-lang/hyloc/src/riscv_asm_codegen.rs b/hylo-lang/hyloc/src/riscv_asm_codegen.rs index 521d95b..7810f56 100644 --- a/hylo-lang/hyloc/src/riscv_asm_codegen.rs +++ b/hylo-lang/hyloc/src/riscv_asm_codegen.rs @@ -2,13 +2,65 @@ use hylo_ir::*; 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) { + lines.push($src.to_string()); + } + }; + ($name:ident, $src:expr, $arg0:ty) => { + fn $name(lines: &mut Vec, val0: $arg0) { + lines.push(format!($src, val0)); + } + }; + ($name:ident, $src:expr, $arg0:ty, $arg1:ty) => { + fn $name(lines: &mut Vec, 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, reg: &str) { + copy_top_stack_value_to(lines, reg); + move_stack_ptr_by_cells(lines, 1); +} + +fn push_from(lines: &mut Vec, 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 { @@ -42,11 +94,16 @@ impl<'a> CodeGen<'a> { 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) => { @@ -62,6 +119,151 @@ impl<'a> CodeGen<'a> { 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"), } } @@ -69,3 +271,4 @@ impl<'a> CodeGen<'a> { Ok(lines.join("\n")) } } + -- 2.43.0