Mixed refactoring

Add error return value as opposed to using panic! and unimplemented!
Reorder some functions
Return relocations
Scan ast for variable lifetimes
Probably put too much into this cl, but it would be annoying to change
at this piont.
This commit is contained in:
Brendan Hansknecht 2020-11-18 23:07:41 -08:00
parent 44d6c3bc02
commit 4e973b9236
4 changed files with 247 additions and 51 deletions

View file

@ -15,7 +15,7 @@ use bumpalo::Bump;
use object::write::Object;
use roc_collections::all::{MutMap, MutSet};
use roc_module::symbol::{Interns, Symbol};
use roc_mono::ir::{Expr, Literal, Proc, Stmt};
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
use roc_mono::layout::Layout;
use target_lexicon::{BinaryFormat, Triple};
@ -44,61 +44,226 @@ pub fn build_module<'a>(
}
}
trait Backend<'a> {
// These relocations likely will need a length.
// They may even need more definition, but this should be at least good enough for how we will use elf.
enum Relocation<'a> {
LocalData { offset: u64, data: &'a [u8] },
LinkedFunction { offset: u64, name: &'a str },
LinkedData { offset: u64, name: &'a str },
}
trait Backend<'a>
where
Self: Sized,
{
/// new creates a new backend that will output to the specific Object.
fn new(env: &'a Env, target: &Triple) -> Self;
fn new(env: &'a Env, target: &Triple) -> 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);
/// 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(&mut self, proc: Proc<'a>) -> &'a [u8] {
self.reset();
// TODO: let the backend know of all the arguments.
self.build_stmt(&proc.body);
self.finalize()
}
/// last_seen_map gets the map from symbol to when it is last seen in the function.
fn last_seen_map(&mut self) -> &mut MutMap<Symbol, *const Stmt<'a>>;
/// 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>);
/// free_symbol frees any registers or stack space used to hold a symbol.
fn free_symbol(&mut self, sym: &Symbol);
/// return_symbol moves a symbol to the correct return location for the backend.
fn return_symbol(&mut self, sym: &Symbol) -> Result<(), String>;
/// 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.
/// For example, this can store the frame pionter and setup stack space.
/// finalize is run at the end of build_proc when all internal code is finalized.
fn finalize(&mut self) -> &'a [u8];
fn finalize(&mut self) -> Result<(&'a [u8], &[Relocation]), String>;
/// 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(&mut self, proc: Proc<'a>) -> Result<(&'a [u8], &[Relocation]), String> {
self.reset();
// TODO: let the backend know of all the arguments.
self.calculate_last_seen(&proc.body);
//println!("{:?}", self.last_seen_map());
self.build_stmt(&proc.body);
self.finalize()
}
/// build_stmt builds a statement and outputs at the end of the buffer.
fn build_stmt(&mut self, stmt: &Stmt<'a>) {
fn build_stmt(&mut self, stmt: &Stmt<'a>) -> Result<(), String> {
match stmt {
Stmt::Let(sym, expr, layout, following) => {
self.build_expr(sym, expr, layout);
self.maybe_free_symbol(sym, stmt);
self.build_stmt(following);
Ok(())
}
Stmt::Ret(sym) => {
self.return_symbol(sym);
self.maybe_free_symbol(sym, stmt);
Ok(())
}
x => unimplemented!("the statement, {:?}, is not yet implemented", x),
x => Err(format!("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, sym: &Symbol, expr: &Expr<'a>, layout: &Layout<'a>) {
fn build_expr(
&mut self,
sym: &Symbol,
expr: &Expr<'a>,
_layout: &Layout<'a>,
) -> Result<(), String> {
match expr {
Expr::Literal(lit) => {
self.set_symbol_to_lit(sym, lit, layout);
self.set_symbol_to_lit(sym, lit);
Ok(())
}
x => unimplemented!("the expression, {:?}, is not yet implemented", x),
x => Err(format!("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>);
/// maybe_free will check if the symbol is last seen in the current state. If so, it will free the symbol resources, like registers.
fn maybe_free_symbol(&mut self, sym: &Symbol, stmt: &Stmt<'a>) {
match self.last_seen_map().get(sym) {
Some(laststmt) if *laststmt == stmt as *const Stmt<'a> => {
//println!("Freeing symbol: {:?}", sym);
self.free_symbol(sym);
}
_ => {}
}
}
/// return_symbol moves a symbol to the correct return location for the backend.
fn return_symbol(&mut self, sym: &Symbol);
/// set_last_seen sets the statement a symbol was last seen in.
fn set_last_seen(&mut self, sym: Symbol, stmt: &Stmt<'a>) {
self.last_seen_map().insert(sym, stmt);
}
/// calculate_last_seen runs through the ast and fill the last seen map.
/// This must iterate through the ast in the same way that build_stmt does. i.e. then before else.
fn calculate_last_seen(&mut self, stmt: &Stmt<'a>) {
match stmt {
Stmt::Let(sym, expr, _, following) => {
self.set_last_seen(*sym, stmt);
match expr {
Expr::Literal(_) => {}
Expr::FunctionPointer(sym, _) => self.set_last_seen(*sym, stmt),
Expr::FunctionCall {
call_type, args, ..
} => {
for sym in *args {
self.set_last_seen(*sym, stmt);
}
match call_type {
CallType::ByName(_sym) => {
// Do nothing, by name is not a variable with lifetime.
}
CallType::ByPointer(sym) => {
self.set_last_seen(*sym, stmt);
}
}
}
Expr::RunLowLevel(_, args) => {
for sym in *args {
self.set_last_seen(*sym, stmt);
}
}
Expr::ForeignCall { arguments, .. } => {
for sym in *arguments {
self.set_last_seen(*sym, stmt);
}
}
Expr::Tag { arguments, .. } => {
for sym in *arguments {
self.set_last_seen(*sym, stmt);
}
}
Expr::Struct(syms) => {
for sym in *syms {
self.set_last_seen(*sym, stmt);
}
}
Expr::AccessAtIndex { structure, .. } => {
self.set_last_seen(*structure, stmt);
}
Expr::Array { elems, .. } => {
for sym in *elems {
self.set_last_seen(*sym, stmt);
}
}
Expr::Reuse { .. } => {
// Not sure what this is used for so leaving it blank for now.
}
Expr::Reset(_sym) => {
// Not sure what this is used for so leaving it blank for now.
}
Expr::EmptyArray => {}
Expr::RuntimeErrorFunction(_) => {}
}
self.calculate_last_seen(following);
}
Stmt::Switch {
cond_symbol,
branches,
default_branch,
..
} => {
self.set_last_seen(*cond_symbol, stmt);
for (_, branch) in *branches {
self.calculate_last_seen(branch);
}
self.calculate_last_seen(default_branch);
}
Stmt::Cond {
cond_symbol,
branching_symbol,
pass,
fail,
..
} => {
self.set_last_seen(*cond_symbol, stmt);
self.set_last_seen(*branching_symbol, stmt);
self.calculate_last_seen(pass);
self.calculate_last_seen(fail);
}
Stmt::Ret(sym) => {
self.set_last_seen(*sym, stmt);
}
Stmt::Inc(sym, following) => {
self.set_last_seen(*sym, stmt);
self.calculate_last_seen(following);
}
Stmt::Dec(sym, following) => {
self.set_last_seen(*sym, stmt);
self.calculate_last_seen(following);
}
Stmt::Join {
parameters,
continuation,
remainder,
..
} => {
for param in *parameters {
self.set_last_seen(param.symbol, stmt);
}
self.calculate_last_seen(continuation);
self.calculate_last_seen(remainder);
}
Stmt::Jump(JoinPointId(sym), symbols) => {
self.set_last_seen(*sym, stmt);
for sym in *symbols {
self.set_last_seen(*sym, stmt);
}
}
Stmt::RuntimeError(_) => {}
}
}
}