diff --git a/compiler/gen_dev/src/elf.rs b/compiler/gen_dev/src/elf.rs index 3352d7853d..ffd924cbd7 100644 --- a/compiler/gen_dev/src/elf.rs +++ b/compiler/gen_dev/src/elf.rs @@ -59,9 +59,9 @@ pub fn build_module<'a>( } // Build procedures. - let mut backend: X86_64Backend = Backend::new(); + let mut backend: X86_64Backend = Backend::new(env); for (proc_id, proc) in procs { - let proc_data = backend.build_proc(env, proc); + let proc_data = backend.build_proc(proc); output.add_symbol_data(proc_id, text, proc_data, 16); } Ok(output) diff --git a/compiler/gen_dev/src/lib.rs b/compiler/gen_dev/src/lib.rs index 1650995746..8dfb7652c3 100644 --- a/compiler/gen_dev/src/lib.rs +++ b/compiler/gen_dev/src/lib.rs @@ -11,11 +11,11 @@ // re-enable this when working on performance optimizations than have it block PRs. #![allow(clippy::large_enum_variant)] -use bumpalo::Bump; +use bumpalo::{collections::Vec, Bump}; use object::write::Object; use roc_collections::all::{MutMap, MutSet}; use roc_module::symbol::{Interns, Symbol}; -use roc_mono::ir::Proc; +use roc_mono::ir::{Expr, Literal, Proc, Stmt}; use roc_mono::layout::Layout; use target_lexicon::{BinaryFormat, Triple}; @@ -44,9 +44,11 @@ pub fn build_module<'a>( } } -trait Backend { +trait Backend<'a> { /// new creates a new backend that will output to the specific Object. - fn new() -> Self; + fn new(env: &'a Env) -> Self; + + fn env(&self) -> &'a Env<'a>; /// reset resets any registers or other values that may be occupied at the end of a procedure. fn reset(&mut self); @@ -54,12 +56,56 @@ trait Backend { /// build_proc creates a procedure and outputs it to the wrapped object writer. /// This will need to return the list of relocations because they have to be added differently based on file format. /// Also, assembly will of course be generated by individual calls on backend like may setup_stack. - fn build_proc<'a>(&mut self, env: &'a Env, _proc: Proc<'a>) -> &'a [u8] { - let mut out = bumpalo::vec![in env.arena; 0x55, 0x48, 0x89, 0xE5]; - let body = [0xb8, 0x06, 0x00, 0x00, 0x00]; - out.extend(body.iter()); - out.push(0x5D); - out.push(0xC3); - out.into_bump_slice() + fn build_proc(&mut self, proc: Proc<'a>) -> &'a [u8] { + self.reset(); + // TODO: let the backend know of all the arguments. + let mut buf = bumpalo::vec!(in self.env().arena); + self.build_stmt(&mut buf, &proc.body); + self.wrap_proc(buf).into_bump_slice() } + + /// wrap_proc does any setup and cleanup that should happen around the procedure. + /// For example, this can store the frame pionter and setup stack space. + /// wrap_proc is run at the end of build_proc when all internal code is finalized. + /// wrap_proc returns a Vec because it is expected to prepend data. + fn wrap_proc(&mut self, buf: Vec<'a, u8>) -> Vec<'a, u8>; + + /// build_stmt builds a statement and outputs at the end of the buffer. + fn build_stmt(&mut self, buf: &mut Vec<'a, u8>, stmt: &Stmt<'a>) { + match stmt { + Stmt::Let(sym, expr, layout, following) => { + self.build_expr(buf, sym, expr, layout); + self.build_stmt(buf, following); + } + Stmt::Ret(sym) => { + self.return_symbol(buf, sym); + } + x => unimplemented!("the statement, {:?}, is not yet implemented", x), + } + } + + /// build_expr builds the expressions for the specified symbol. + /// The builder must keep track of the symbol because it may be refered to later. + /// In many cases values can be lazy loaded, like literals. + fn build_expr( + &mut self, + _buf: &mut Vec<'a, u8>, + sym: &Symbol, + expr: &Expr<'a>, + layout: &Layout<'a>, + ) { + match expr { + Expr::Literal(lit) => { + self.set_symbol_to_lit(sym, lit, layout); + } + x => unimplemented!("the expression, {:?}, is not yet implemented", x), + } + } + + /// set_symbol_to_lit sets a symbol to be equal to a literal. + /// When the symbol is used, the literal should be loaded. + fn set_symbol_to_lit(&mut self, sym: &Symbol, lit: &Literal<'a>, layout: &Layout<'a>); + + /// return_symbol moves a symbol to the correct return location for the backend. + fn return_symbol(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol); } diff --git a/compiler/gen_dev/src/x86_64.rs b/compiler/gen_dev/src/x86_64.rs index 0c10845f31..7378f270b4 100644 --- a/compiler/gen_dev/src/x86_64.rs +++ b/compiler/gen_dev/src/x86_64.rs @@ -1,23 +1,77 @@ -use crate::Backend; +use crate::{Backend, Env}; +use bumpalo::collections::Vec; +use roc_collections::all::MutMap; +use roc_module::symbol::Symbol; +use roc_mono::ir::Literal; +use roc_mono::layout::Layout; -pub struct X86_64Backend { +pub struct X86_64Backend<'a> { + env: &'a Env<'a>, + + // This will need to hold info a symbol is held in a register or on the stack as well. + symbols_map: MutMap, Layout<'a>)>, // This is gonna need to include a lot of data. Right now I can think of quite a few. -// Registers order by priority with info of what data is stored in them. -// Scope with knows were all variables are currently stored.X86_64Backend + // Registers order by priority with info of what data is stored in them. + // Scope with knows were all variables are currently stored.X86_64Backend -// Since this is x86_64 the calling convetions is really just windows or linux/macos. -// Hopefully this will be easy to extract into a trait somehow. Cause probably don't want if's everywhere. -// Also, don't really want to build an x86_64-win backend specifically for it. + // Since this is x86_64 the calling convetions is really just windows or linux/macos. + // Hopefully this will be easy to extract into a trait somehow. Cause probably don't want if's everywhere. + // Also, don't really want to build an x86_64-win backend specifically for it. -// function parameter registers listed by order. Need to know the float equivalent registers as well. -// Probably need to encode stack parameter knowledge here too. -// return parameter register. This includes dealing with multiple value returns. + // function parameter registers listed by order. Need to know the float equivalent registers as well. + // Probably need to encode stack parameter knowledge here too. + // return parameter register. This includes dealing with multiple value returns. } -impl Backend for X86_64Backend { - fn new() -> Self { - X86_64Backend {} +impl<'a> Backend<'a> for X86_64Backend<'a> { + fn new(env: &'a Env) -> Self { + X86_64Backend { + env, + symbols_map: MutMap::default(), + } } - fn reset(&mut self) {} + fn env(&self) -> &'a Env<'a> { + self.env + } + + fn reset(&mut self) { + self.symbols_map.clear(); + } + + fn wrap_proc(&mut self, body: Vec<'a, u8>) -> Vec<'a, u8> { + // push rbp (0x55) + // mov rbp, rsp (0x48, 0x89, 0xE5) + let mut out = bumpalo::vec![in self.env.arena; 0x55, 0x48, 0x89, 0xE5]; + out.reserve(body.len() + 2); + + // TODO: handle allocating and cleaning up data on the stack. + + out.extend(body); + + // pop rbp + out.push(0x5D); + // ret + out.push(0xC3); + + out + } + + fn set_symbol_to_lit(&mut self, sym: &Symbol, lit: &Literal<'a>, layout: &Layout<'a>) { + self.symbols_map + .insert(sym.clone(), (lit.clone(), layout.clone())); + } + + fn return_symbol(&mut self, body: &mut Vec<'a, u8>, sym: &Symbol) { + //let body = bumpalo::vec![in env.arena; 0xb8, 0x06, 0x00, 0x00, 0x00]; + match self.symbols_map.get(sym) { + Some((Literal::Int(x), _)) => { + // movabs rax, ... + body.extend(&[0x48, 0xB8]); + body.extend(&x.to_le_bytes()); + } + Some(x) => unimplemented!("return value, {:?}, is not yet implemented", x), + None => panic!("Unknown return symbol: {}", sym), + } + } } diff --git a/compiler/gen_dev/tests/gen_num.rs b/compiler/gen_dev/tests/gen_num.rs index a28a4ffecb..aeb61b1769 100644 --- a/compiler/gen_dev/tests/gen_num.rs +++ b/compiler/gen_dev/tests/gen_num.rs @@ -15,7 +15,15 @@ mod gen_num { #[test] fn i64_values() { - assert_evals_to!("6", 6, i64); + assert_evals_to!("0", 0, i64); + assert_evals_to!("-0", 0, i64); + assert_evals_to!("-1", -1, i64); + assert_evals_to!("1", 1, i64); + assert_evals_to!("9_000_000_000_000", 9_000_000_000_000, i64); + assert_evals_to!("-9_000_000_000_000", -9_000_000_000_000, i64); + assert_evals_to!("0b1010", 0b1010, i64); + assert_evals_to!("0o17", 0o17, i64); + assert_evals_to!("0x1000_0000_0000_0000", 0x1000_0000_0000_0000, i64); } /* #[test] @@ -44,12 +52,10 @@ mod gen_num { assert_evals_to!("Num.abs -4.7", 4.7, f64); assert_evals_to!("Num.abs 5.8", 5.8, f64); } - */ #[test] fn i64_abs() { //assert_evals_to!("Num.abs -6", 6, i64); - /* assert_evals_to!("Num.abs 7", 7, i64); assert_evals_to!("Num.abs 0", 0, i64); assert_evals_to!("Num.abs -0", 0, i64); @@ -57,10 +63,8 @@ mod gen_num { assert_evals_to!("Num.abs 1", 1, i64); assert_evals_to!("Num.abs 9_000_000_000_000", 9_000_000_000_000, i64); assert_evals_to!("Num.abs -9_000_000_000_000", 9_000_000_000_000, i64); - */ } - /* #[test] fn gen_if_fn() { assert_evals_to!(