Add join points and tail call optimization to the dev backend.

This commit is contained in:
Brendan Hansknecht 2021-09-20 23:13:30 -07:00
parent 946502b8ea
commit 16d098da5e
6 changed files with 375 additions and 124 deletions

View file

@ -9,10 +9,10 @@ use roc_module::ident::{ModuleName, TagName};
use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, Symbol};
use roc_mono::ir::{
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Proc, Stmt,
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc,
SelfRecursive, Stmt,
};
use roc_mono::layout::{Builtin, Layout, LayoutIds};
use target_lexicon::Triple;
mod generic64;
mod object_builder;
@ -46,6 +46,10 @@ pub enum Relocation {
offset: u64,
name: String,
},
JmpToReturn {
inst_loc: u64,
offset: u64,
},
}
trait Backend<'a>
@ -53,12 +57,13 @@ where
Self: Sized,
{
/// new creates a new backend that will output to the specific Object.
fn new(env: &'a Env, target: &Triple) -> Result<Self, String>;
fn new(env: &'a Env) -> Result<Self, String>;
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);
/// It also passes basic procedure information to the builder for setup of the next function.
fn reset(&mut self, name: String, is_self_recursive: SelfRecursive);
/// finalize does any setup and cleanup that should happen around the procedure.
/// finalize does setup because things like stack size and jump locations are not know until the function is written.
@ -79,7 +84,10 @@ where
/// build_proc creates a procedure and outputs it to the wrapped object writer.
fn build_proc(&mut self, proc: Proc<'a>) -> Result<(&'a [u8], &[Relocation]), String> {
self.reset();
let proc_name = LayoutIds::default()
.get(proc.name, &proc.ret_layout)
.to_symbol_string(proc.name, &self.env().interns);
self.reset(proc_name, proc.is_self_recursive);
self.load_args(proc.args, &proc.ret_layout)?;
for (layout, sym) in proc.args {
self.set_layout_map(*sym, layout)?;
@ -128,6 +136,36 @@ where
self.free_symbols(stmt)?;
Ok(())
}
Stmt::Join {
id,
parameters,
body,
remainder,
} => {
for param in parameters.iter() {
self.set_layout_map(param.symbol, &param.layout)?;
}
self.build_join(id, parameters, body, remainder, ret_layout)?;
self.free_symbols(stmt)?;
Ok(())
}
Stmt::Jump(id, args) => {
let mut arg_layouts: bumpalo::collections::Vec<Layout<'a>> =
bumpalo::vec![in self.env().arena];
arg_layouts.reserve(args.len());
let layout_map = self.layout_map();
for arg in *args {
if let Some(layout) = layout_map.get(arg) {
// This is safe because every value in the map is always set with a valid layout and cannot be null.
arg_layouts.push(unsafe { *(*layout) });
} else {
return Err(format!("the argument, {:?}, has no know layout", arg));
}
}
self.build_jump(id, args, arg_layouts.into_bump_slice(), ret_layout)?;
self.free_symbols(stmt)?;
Ok(())
}
x => Err(format!("the statement, {:?}, is not yet implemented", x)),
}
}
@ -141,6 +179,25 @@ where
ret_layout: &Layout<'a>,
) -> Result<(), String>;
// build_join generates a instructions for a join statement.
fn build_join(
&mut self,
id: &JoinPointId,
parameters: &'a [Param<'a>],
body: &'a Stmt<'a>,
remainder: &'a Stmt<'a>,
ret_layout: &Layout<'a>,
) -> Result<(), String>;
// build_jump generates a instructions for a jump statement.
fn build_jump(
&mut self,
id: &JoinPointId,
args: &'a [Symbol],
arg_layouts: &[Layout<'a>],
ret_layout: &Layout<'a>,
) -> Result<(), String>;
/// build_expr builds the expressions for the specified symbol.
/// The builder must keep track of the symbol because it may be referred to later.
fn build_expr(
@ -507,7 +564,7 @@ where
/// load_literal sets a symbol to be equal to a literal.
fn load_literal(&mut self, sym: &Symbol, lit: &Literal<'a>) -> Result<(), String>;
/// return_symbol moves a symbol to the correct return location for the backend.
/// return_symbol moves a symbol to the correct return location for the backend and adds a jump to the end of the function.
fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>) -> Result<(), String>;
/// free_symbols will free all symbols for the given statement.