From f7d5e56796f5c9008df29f40c3d19627756632c8 Mon Sep 17 00:00:00 2001 From: Bryan English Date: Mon, 19 Jan 2026 15:01:52 -0500 Subject: [PATCH] qemu setup, plus some compiler cleanup --- hylo-lang/.gitignore | 1 + hylo-lang/flake.nix | 1 + hylo-lang/hyloc/src/riscv_asm_codegen.rs | 284 ++++++++++++----------- hylo-lang/qemu/run.sh | 17 ++ hylo-lang/qemu/setup.sh | 17 ++ 5 files changed, 190 insertions(+), 130 deletions(-) create mode 100644 hylo-lang/qemu/run.sh create mode 100644 hylo-lang/qemu/setup.sh diff --git a/hylo-lang/.gitignore b/hylo-lang/.gitignore index eb5a316..7fa3f17 100644 --- a/hylo-lang/.gitignore +++ b/hylo-lang/.gitignore @@ -1 +1,2 @@ target +qemu/machine diff --git a/hylo-lang/flake.nix b/hylo-lang/flake.nix index e198852..550936f 100644 --- a/hylo-lang/flake.nix +++ b/hylo-lang/flake.nix @@ -11,6 +11,7 @@ in { devShells.${system}.default = pkgs.mkShell { packages = [ + pkgs.qemu ]; }; }; diff --git a/hylo-lang/hyloc/src/riscv_asm_codegen.rs b/hylo-lang/hyloc/src/riscv_asm_codegen.rs index 7810f56..3da147d 100644 --- a/hylo-lang/hyloc/src/riscv_asm_codegen.rs +++ b/hylo-lang/hyloc/src/riscv_asm_codegen.rs @@ -3,10 +3,12 @@ use hylo_ir::*; use anyhow::*; use std::collections::{HashMap, HashSet}; +use std::fmt::Display; pub struct CodeGen<'a> { module: &'a IRModule, data_stack_size: usize, + lines: Vec, } @@ -28,61 +30,68 @@ pub struct CodeGen<'a> { macro_rules! asm_macro { ($name:ident, $src:expr) => { - fn $name(lines: &mut Vec) { - lines.push($src.to_string()); + fn $name(&mut self) { + self.ine($src); } }; ($name:ident, $src:expr, $arg0:ty) => { - fn $name(lines: &mut Vec, 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, 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, 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 { module: ir_mod, - data_stack_size + data_stack_size, + lines: vec![], } } + fn line(&mut self, line: S) { + self.lines.push(format!(" {}", line)); + } + + fn label(&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{ - 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; }, @@ -91,175 +100,190 @@ impl<'a> CodeGen<'a> { } // 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(); @@ -268,7 +292,7 @@ impl<'a> CodeGen<'a> { } } - Ok(lines.join("\n")) + Ok(self.lines.join("\n")) } } diff --git a/hylo-lang/qemu/run.sh b/hylo-lang/qemu/run.sh new file mode 100644 index 0000000..a9d3a7e --- /dev/null +++ b/hylo-lang/qemu/run.sh @@ -0,0 +1,17 @@ + +qemu-system-riscv64 \ + -machine virt \ + -cpu rv64 \ + -m 1G \ + -device virtio-blk-device,drive=hd \ + -drive file=machine/overlay.qcow2,if=none,id=hd \ + -device virtio-net-device,netdev=net \ + -netdev user,id=net,hostfwd=tcp::2222-:22 \ + -kernel machine/u-boot-qemu/uboot.elf \ + -object rng-random,filename=/dev/urandom,id=rng \ + -device virtio-rng-device,rng=rng \ + -append "root=LABEL=rootfs console=ttyS0" \ + -nographic \ + +echo "now do:" +echo " ssh debian@localhost -p 2222" diff --git a/hylo-lang/qemu/setup.sh b/hylo-lang/qemu/setup.sh new file mode 100644 index 0000000..c692095 --- /dev/null +++ b/hylo-lang/qemu/setup.sh @@ -0,0 +1,17 @@ + +mkdir machine +cd machine +wget "https://gitlab.com/api/v4/projects/giomasce%2Fdqib/jobs/artifacts/master/download?job=convert_riscv64-virt" -O debian-rv64.zip + +unzip ./debian-rv64.zip + +# Grab the URL from https://packages.debian.org/sid/u-boot-qemu +wget "http://ftp.us.debian.org/debian/pool/main/u/u-boot/u-boot-qemu_2025.01-3.1_all.deb" -O u-boot-qemu.deb +mkdir u-boot-qemu +cd u-boot-qemu +ar -x ../u-boot-qemu.deb +tar xvf data.tar.xz +cp ./usr/lib/u-boot/qemu-riscv64_smode/uboot.elf ./uboot.elf + +qemu-img create -o backing_file=./dqib_riscv64-virt/image.qcow2,backing_fmt=qcow2 -f qcow2 overlay.qcow2 + -- 2.43.0