mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
Merge remote-tracking branch 'origin/trunk' into dev-backend-num-to-float
This commit is contained in:
commit
511b8d5586
8 changed files with 664 additions and 317 deletions
|
@ -7,12 +7,13 @@ use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
|||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::ident::{ModuleName, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::gen_refcount::RefcountProcGenerator;
|
||||
use roc_mono::ir::{
|
||||
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc,
|
||||
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc, ProcLayout,
|
||||
SelfRecursive, Stmt,
|
||||
};
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutId, LayoutIds};
|
||||
use roc_reporting::internal_error;
|
||||
|
||||
mod generic64;
|
||||
|
@ -22,7 +23,7 @@ mod run_roc;
|
|||
|
||||
pub struct Env<'a> {
|
||||
pub arena: &'a Bump,
|
||||
pub interns: Interns,
|
||||
pub module_id: ModuleId,
|
||||
pub exposed_to_host: MutSet<Symbol>,
|
||||
pub lazy_literals: bool,
|
||||
pub generate_allocators: bool,
|
||||
|
@ -30,7 +31,7 @@ pub struct Env<'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.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Relocation {
|
||||
LocalData {
|
||||
|
@ -54,14 +55,32 @@ pub enum Relocation {
|
|||
},
|
||||
}
|
||||
|
||||
trait Backend<'a>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
/// new creates a new backend that will output to the specific Object.
|
||||
fn new(env: &'a Env) -> Self;
|
||||
trait Backend<'a> {
|
||||
fn env(&self) -> &Env<'a>;
|
||||
fn interns(&self) -> &Interns;
|
||||
|
||||
fn env(&self) -> &'a Env<'a>;
|
||||
// This method is suboptimal, but it seems to be the only way to make rust understand
|
||||
// that all of these values can be mutable at the same time. By returning them together,
|
||||
// rust understands that they are part of a single use of mutable self.
|
||||
fn env_interns_refcount_mut(
|
||||
&mut self,
|
||||
) -> (&Env<'a>, &mut Interns, &mut RefcountProcGenerator<'a>);
|
||||
|
||||
fn symbol_to_string(&self, symbol: Symbol, layout_id: LayoutId) -> String {
|
||||
layout_id.to_symbol_string(symbol, self.interns())
|
||||
}
|
||||
|
||||
fn defined_in_app_module(&self, symbol: Symbol) -> bool {
|
||||
symbol
|
||||
.module_string(self.interns())
|
||||
.starts_with(ModuleName::APP)
|
||||
}
|
||||
|
||||
fn refcount_proc_gen_mut(&mut self) -> &mut RefcountProcGenerator<'a>;
|
||||
|
||||
fn refcount_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
|
||||
fn refcount_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
|
||||
/// reset resets any registers or other values that may be occupied at the end of a procedure.
|
||||
/// It also passes basic procedure information to the builder for setup of the next function.
|
||||
|
@ -71,7 +90,7 @@ where
|
|||
/// 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 pointer 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], &[Relocation]);
|
||||
fn finalize(&mut self) -> (Vec<u8>, Vec<Relocation>);
|
||||
|
||||
// load_args is used to let the backend know what the args are.
|
||||
// The backend should track these args so it can use them as needed.
|
||||
|
@ -81,10 +100,14 @@ where
|
|||
fn build_wrapped_jmp(&mut self) -> (&'a [u8], u64);
|
||||
|
||||
/// build_proc creates a procedure and outputs it to the wrapped object writer.
|
||||
fn build_proc(&mut self, proc: &'a Proc<'a>) -> (&'a [u8], &[Relocation]) {
|
||||
let proc_name = LayoutIds::default()
|
||||
.get(proc.name, &proc.ret_layout)
|
||||
.to_symbol_string(proc.name, &self.env().interns);
|
||||
/// Returns the procedure bytes, its relocations, and the names of the refcounting functions it references.
|
||||
fn build_proc(
|
||||
&mut self,
|
||||
proc: &'a Proc<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
) -> (Vec<u8>, Vec<Relocation>, Vec<'a, (Symbol, String)>) {
|
||||
let layout_id = layout_ids.get(proc.name, &proc.ret_layout);
|
||||
let proc_name = self.symbol_to_string(proc.name, layout_id);
|
||||
self.reset(proc_name, &proc.is_self_recursive);
|
||||
self.load_args(proc.args, &proc.ret_layout);
|
||||
for (layout, sym) in proc.args {
|
||||
|
@ -93,7 +116,17 @@ where
|
|||
self.scan_ast(&proc.body);
|
||||
self.create_free_map();
|
||||
self.build_stmt(&proc.body, &proc.ret_layout);
|
||||
self.finalize()
|
||||
let mut rc_proc_names = bumpalo::vec![in self.env().arena];
|
||||
rc_proc_names.reserve(self.refcount_proc_symbols().len());
|
||||
for (rc_proc_sym, rc_proc_layout) in self.refcount_proc_symbols() {
|
||||
let name = layout_ids
|
||||
.get_toplevel(*rc_proc_sym, rc_proc_layout)
|
||||
.to_symbol_string(*rc_proc_sym, self.interns());
|
||||
|
||||
rc_proc_names.push((*rc_proc_sym, name));
|
||||
}
|
||||
let (bytes, relocs) = self.finalize();
|
||||
(bytes, relocs, rc_proc_names)
|
||||
}
|
||||
|
||||
/// build_stmt builds a statement and outputs at the end of the buffer.
|
||||
|
@ -110,9 +143,27 @@ where
|
|||
self.return_symbol(sym, ret_layout);
|
||||
self.free_symbols(stmt);
|
||||
}
|
||||
Stmt::Refcounting(_modify, following) => {
|
||||
// TODO: actually deal with refcounting. For hello world, we just skipped it.
|
||||
self.build_stmt(following, ret_layout);
|
||||
Stmt::Refcounting(modify, following) => {
|
||||
let sym = modify.get_symbol();
|
||||
let layout = *self.layout_map().get(&sym).unwrap();
|
||||
|
||||
// Expand the Refcounting statement into more detailed IR with a function call
|
||||
// If this layout requires a new RC proc, we get enough info to create a linker symbol
|
||||
// for it. Here we don't create linker symbols at this time, but in Wasm backend, we do.
|
||||
let (rc_stmt, new_proc_info) = {
|
||||
let (env, interns, rc_proc_gen) = self.env_interns_refcount_mut();
|
||||
let module_id = env.module_id;
|
||||
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
||||
|
||||
rc_proc_gen.expand_refcount_stmt(ident_ids, layout, modify, *following)
|
||||
};
|
||||
|
||||
if let Some((rc_proc_symbol, rc_proc_layout)) = new_proc_info {
|
||||
self.refcount_proc_symbols_mut()
|
||||
.push((rc_proc_symbol, rc_proc_layout));
|
||||
}
|
||||
|
||||
self.build_stmt(&rc_stmt, ret_layout)
|
||||
}
|
||||
Stmt::Switch {
|
||||
cond_symbol,
|
||||
|
@ -221,13 +272,9 @@ where
|
|||
arg_layouts,
|
||||
ret_layout,
|
||||
)
|
||||
} else if func_sym
|
||||
.module_string(&self.env().interns)
|
||||
.starts_with(ModuleName::APP)
|
||||
{
|
||||
let fn_name = LayoutIds::default()
|
||||
.get(*func_sym, layout)
|
||||
.to_symbol_string(*func_sym, &self.env().interns);
|
||||
} else if self.defined_in_app_module(*func_sym) {
|
||||
let layout_id = LayoutIds::default().get(*func_sym, layout);
|
||||
let fn_name = self.symbol_to_string(*func_sym, layout_id);
|
||||
// Now that the arguments are needed, load them if they are literals.
|
||||
self.load_literal_symbols(arguments);
|
||||
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
||||
|
@ -456,6 +503,23 @@ where
|
|||
);
|
||||
self.build_num_to_float(sym, &args[0], &arg_layouts[0], ret_layout)
|
||||
}
|
||||
LowLevel::NumGte => {
|
||||
debug_assert_eq!(
|
||||
2,
|
||||
args.len(),
|
||||
"NumGte: expected to have exactly two argument"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
arg_layouts[0], arg_layouts[1],
|
||||
"NumGte: expected all arguments of to have the same layout"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
Layout::Builtin(Builtin::Bool),
|
||||
*ret_layout,
|
||||
"NumGte: expected to have return layout of type Bool"
|
||||
);
|
||||
self.build_num_gte(sym, &args[0], &args[1], &arg_layouts[0])
|
||||
}
|
||||
LowLevel::NumRound => self.build_fn_call(
|
||||
sym,
|
||||
bitcode::NUM_ROUND[FloatWidth::F64].to_string(),
|
||||
|
@ -470,6 +534,28 @@ where
|
|||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
LowLevel::RefCountGetPtr => {
|
||||
debug_assert_eq!(
|
||||
1,
|
||||
args.len(),
|
||||
"RefCountGetPtr: expected to have exactly two argument"
|
||||
);
|
||||
self.build_refcount_getptr(sym, &args[0])
|
||||
}
|
||||
LowLevel::RefCountDec => self.build_fn_call(
|
||||
sym,
|
||||
bitcode::UTILS_DECREF.to_string(),
|
||||
args,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
LowLevel::RefCountInc => self.build_fn_call(
|
||||
sym,
|
||||
bitcode::UTILS_INCREF.to_string(),
|
||||
args,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
x => unimplemented!("low level, {:?}. is not yet implemented", x),
|
||||
}
|
||||
}
|
||||
|
@ -549,6 +635,18 @@ where
|
|||
ret_layout: &Layout<'a>,
|
||||
);
|
||||
|
||||
/// build_num_gte stores the result of `src1 >= src2` into dst.
|
||||
fn build_num_gte(
|
||||
&mut self,
|
||||
dst: &Symbol,
|
||||
src1: &Symbol,
|
||||
src2: &Symbol,
|
||||
arg_layout: &Layout<'a>,
|
||||
);
|
||||
|
||||
/// build_refcount_getptr loads the pointer to the reference count of src into dst.
|
||||
fn build_refcount_getptr(&mut self, dst: &Symbol, src: &Symbol);
|
||||
|
||||
/// literal_map gets the map from symbol to literal and layout, used for lazy loading and literal folding.
|
||||
fn literal_map(&mut self) -> &mut MutMap<Symbol, (&'a Literal<'a>, &'a Layout<'a>)>;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue