mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Add ability to return int literals
This commit is contained in:
parent
86c3c0a409
commit
a8986087f9
4 changed files with 136 additions and 32 deletions
|
@ -59,9 +59,9 @@ pub fn build_module<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build procedures.
|
// Build procedures.
|
||||||
let mut backend: X86_64Backend = Backend::new();
|
let mut backend: X86_64Backend = Backend::new(env);
|
||||||
for (proc_id, proc) in procs {
|
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);
|
output.add_symbol_data(proc_id, text, proc_data, 16);
|
||||||
}
|
}
|
||||||
Ok(output)
|
Ok(output)
|
||||||
|
|
|
@ -11,11 +11,11 @@
|
||||||
// re-enable this when working on performance optimizations than have it block PRs.
|
// re-enable this when working on performance optimizations than have it block PRs.
|
||||||
#![allow(clippy::large_enum_variant)]
|
#![allow(clippy::large_enum_variant)]
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::{collections::Vec, Bump};
|
||||||
use object::write::Object;
|
use object::write::Object;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
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 roc_mono::layout::Layout;
|
||||||
use target_lexicon::{BinaryFormat, Triple};
|
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.
|
/// 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.
|
/// reset resets any registers or other values that may be occupied at the end of a procedure.
|
||||||
fn reset(&mut self);
|
fn reset(&mut self);
|
||||||
|
@ -54,12 +56,56 @@ trait Backend {
|
||||||
/// build_proc creates a procedure and outputs it to the wrapped object writer.
|
/// 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.
|
/// 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.
|
/// 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] {
|
fn build_proc(&mut self, proc: Proc<'a>) -> &'a [u8] {
|
||||||
let mut out = bumpalo::vec![in env.arena; 0x55, 0x48, 0x89, 0xE5];
|
self.reset();
|
||||||
let body = [0xb8, 0x06, 0x00, 0x00, 0x00];
|
// TODO: let the backend know of all the arguments.
|
||||||
out.extend(body.iter());
|
let mut buf = bumpalo::vec!(in self.env().arena);
|
||||||
out.push(0x5D);
|
self.build_stmt(&mut buf, &proc.body);
|
||||||
out.push(0xC3);
|
self.wrap_proc(buf).into_bump_slice()
|
||||||
out.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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<Symbol, (Literal<'a>, Layout<'a>)>,
|
||||||
// This is gonna need to include a lot of data. Right now I can think of quite a few.
|
// 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.
|
// Registers order by priority with info of what data is stored in them.
|
||||||
// Scope with knows were all variables are currently stored.X86_64Backend
|
// 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.
|
// 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.
|
// 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.
|
// 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.
|
// function parameter registers listed by order. Need to know the float equivalent registers as well.
|
||||||
// Probably need to encode stack parameter knowledge here too.
|
// Probably need to encode stack parameter knowledge here too.
|
||||||
// return parameter register. This includes dealing with multiple value returns.
|
// return parameter register. This includes dealing with multiple value returns.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backend for X86_64Backend {
|
impl<'a> Backend<'a> for X86_64Backend<'a> {
|
||||||
fn new() -> Self {
|
fn new(env: &'a Env) -> Self {
|
||||||
X86_64Backend {}
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,15 @@ mod gen_num {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn i64_values() {
|
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]
|
#[test]
|
||||||
|
@ -44,12 +52,10 @@ mod gen_num {
|
||||||
assert_evals_to!("Num.abs -4.7", 4.7, f64);
|
assert_evals_to!("Num.abs -4.7", 4.7, f64);
|
||||||
assert_evals_to!("Num.abs 5.8", 5.8, f64);
|
assert_evals_to!("Num.abs 5.8", 5.8, f64);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn i64_abs() {
|
fn i64_abs() {
|
||||||
//assert_evals_to!("Num.abs -6", 6, i64);
|
//assert_evals_to!("Num.abs -6", 6, i64);
|
||||||
/*
|
|
||||||
assert_evals_to!("Num.abs 7", 7, 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);
|
||||||
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 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);
|
||||||
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]
|
#[test]
|
||||||
fn gen_if_fn() {
|
fn gen_if_fn() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue