mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Merge pull request #5365 from roc-lang/dev-backend-cli
Dev backend list tests
This commit is contained in:
commit
deb1e9952d
13 changed files with 1035 additions and 351 deletions
|
@ -79,6 +79,18 @@ pub enum Relocation {
|
|||
},
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
enum UpdateMode {
|
||||
Immutable = 0,
|
||||
}
|
||||
|
||||
struct ListArgument<'a> {
|
||||
element_layout: InLayout<'a>,
|
||||
|
||||
alignment: Symbol,
|
||||
element_width: Symbol,
|
||||
}
|
||||
|
||||
trait Backend<'a> {
|
||||
fn env(&self) -> &Env<'a>;
|
||||
fn interns(&self) -> &Interns;
|
||||
|
@ -161,11 +173,70 @@ trait Backend<'a> {
|
|||
.starts_with(ModuleName::APP)
|
||||
}
|
||||
|
||||
fn list_argument(&mut self, list_layout: InLayout<'a>) -> ListArgument<'a> {
|
||||
let element_layout = match self.interner().get(list_layout) {
|
||||
Layout::Builtin(Builtin::List(e)) => e,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let (element_width_int, alignment_int) =
|
||||
self.interner().stack_size_and_alignment(element_layout);
|
||||
|
||||
let alignment = self.debug_symbol("alignment");
|
||||
self.load_literal_i32(&alignment, Ord::max(alignment_int, 8) as i32);
|
||||
|
||||
let element_width = self.debug_symbol("element_width");
|
||||
self.load_literal_i64(&element_width, element_width_int as i64);
|
||||
|
||||
ListArgument {
|
||||
element_layout,
|
||||
alignment,
|
||||
element_width,
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_fn_pointer(&mut self, layout: InLayout<'a>) -> Symbol {
|
||||
let box_layout = self.interner_mut().insert(Layout::Boxed(layout));
|
||||
|
||||
let element_increment = self.debug_symbol("element_increment");
|
||||
let element_increment_symbol = self.build_indirect_inc(layout);
|
||||
|
||||
let element_increment_string = self.function_symbol_to_string(
|
||||
element_increment_symbol,
|
||||
[box_layout].into_iter(),
|
||||
None,
|
||||
Layout::UNIT,
|
||||
);
|
||||
|
||||
self.build_fn_pointer(&element_increment, element_increment_string);
|
||||
|
||||
element_increment
|
||||
}
|
||||
|
||||
fn decrement_fn_pointer(&mut self, layout: InLayout<'a>) -> Symbol {
|
||||
let box_layout = self.interner_mut().insert(Layout::Boxed(layout));
|
||||
|
||||
let element_decrement = self.debug_symbol("element_decrement");
|
||||
let element_decrement_symbol = self.build_indirect_dec(layout);
|
||||
|
||||
let element_decrement_string = self.function_symbol_to_string(
|
||||
element_decrement_symbol,
|
||||
[box_layout].into_iter(),
|
||||
None,
|
||||
Layout::UNIT,
|
||||
);
|
||||
|
||||
self.build_fn_pointer(&element_decrement, element_decrement_string);
|
||||
|
||||
element_decrement
|
||||
}
|
||||
|
||||
fn helper_proc_gen_mut(&mut self) -> &mut CodeGenHelp<'a>;
|
||||
|
||||
fn helper_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
|
||||
fn helper_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
fn caller_procs(&self) -> &Vec<'a, CallerProc<'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.
|
||||
|
@ -205,7 +276,8 @@ trait Backend<'a> {
|
|||
}
|
||||
self.scan_ast(&proc.body);
|
||||
self.create_free_map();
|
||||
self.build_stmt(&proc.body, &proc.ret_layout);
|
||||
self.build_stmt(layout_ids, &proc.body, &proc.ret_layout);
|
||||
|
||||
let mut helper_proc_names = bumpalo::vec![in self.env().arena];
|
||||
helper_proc_names.reserve(self.helper_proc_symbols().len());
|
||||
for (rc_proc_sym, rc_proc_layout) in self.helper_proc_symbols() {
|
||||
|
@ -215,18 +287,34 @@ trait Backend<'a> {
|
|||
|
||||
helper_proc_names.push((*rc_proc_sym, name));
|
||||
}
|
||||
|
||||
for caller_proc in self.caller_procs() {
|
||||
let proc_layout = caller_proc.proc_layout;
|
||||
let proc_symbol = caller_proc.proc_symbol;
|
||||
let name = layout_ids
|
||||
.get_toplevel(proc_symbol, &proc_layout)
|
||||
.to_symbol_string(proc_symbol, self.interns());
|
||||
|
||||
helper_proc_names.push((proc_symbol, name));
|
||||
}
|
||||
|
||||
let (bytes, relocs) = self.finalize();
|
||||
(bytes, relocs, helper_proc_names)
|
||||
}
|
||||
|
||||
/// build_stmt builds a statement and outputs at the end of the buffer.
|
||||
fn build_stmt(&mut self, stmt: &Stmt<'a>, ret_layout: &InLayout<'a>) {
|
||||
fn build_stmt(
|
||||
&mut self,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
stmt: &Stmt<'a>,
|
||||
ret_layout: &InLayout<'a>,
|
||||
) {
|
||||
match stmt {
|
||||
Stmt::Let(sym, expr, layout, following) => {
|
||||
self.build_expr(sym, expr, layout);
|
||||
self.set_layout_map(*sym, layout);
|
||||
self.free_symbols(stmt);
|
||||
self.build_stmt(following, ret_layout);
|
||||
self.build_stmt(layout_ids, following, ret_layout);
|
||||
}
|
||||
Stmt::Ret(sym) => {
|
||||
self.load_literal_symbols(&[*sym]);
|
||||
|
@ -258,7 +346,7 @@ trait Backend<'a> {
|
|||
self.helper_proc_symbols_mut().push(spec);
|
||||
}
|
||||
|
||||
self.build_stmt(rc_stmt, ret_layout)
|
||||
self.build_stmt(layout_ids, rc_stmt, ret_layout)
|
||||
}
|
||||
Stmt::Switch {
|
||||
cond_symbol,
|
||||
|
@ -269,6 +357,7 @@ trait Backend<'a> {
|
|||
} => {
|
||||
self.load_literal_symbols(&[*cond_symbol]);
|
||||
self.build_switch(
|
||||
layout_ids,
|
||||
cond_symbol,
|
||||
cond_layout,
|
||||
branches,
|
||||
|
@ -286,7 +375,7 @@ trait Backend<'a> {
|
|||
for param in parameters.iter() {
|
||||
self.set_layout_map(param.symbol, ¶m.layout);
|
||||
}
|
||||
self.build_join(id, parameters, body, remainder, ret_layout);
|
||||
self.build_join(layout_ids, id, parameters, body, remainder, ret_layout);
|
||||
self.free_symbols(stmt);
|
||||
}
|
||||
Stmt::Jump(id, args) => {
|
||||
|
@ -311,14 +400,16 @@ trait Backend<'a> {
|
|||
}
|
||||
|
||||
fn roc_panic(&mut self, msg: Symbol, crash_tag: CrashTag) {
|
||||
let error_message = self.debug_symbol("error_message");
|
||||
|
||||
self.load_literal(
|
||||
&Symbol::DEV_TMP,
|
||||
&error_message,
|
||||
&Layout::U32,
|
||||
&Literal::Int((crash_tag as u128).to_ne_bytes()),
|
||||
);
|
||||
|
||||
// Now that the arguments are needed, load them if they are literals.
|
||||
let arguments = &[msg, Symbol::DEV_TMP];
|
||||
let arguments = &[msg, error_message];
|
||||
self.load_literal_symbols(arguments);
|
||||
self.build_fn_call(
|
||||
&Symbol::DEV_TMP2,
|
||||
|
@ -328,13 +419,14 @@ trait Backend<'a> {
|
|||
&Layout::UNIT,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&error_message);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
}
|
||||
|
||||
// build_switch generates a instructions for a switch statement.
|
||||
fn build_switch(
|
||||
&mut self,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
cond_symbol: &Symbol,
|
||||
cond_layout: &InLayout<'a>,
|
||||
branches: &'a [(u64, BranchInfo<'a>, Stmt<'a>)],
|
||||
|
@ -345,6 +437,7 @@ trait Backend<'a> {
|
|||
// build_join generates a instructions for a join statement.
|
||||
fn build_join(
|
||||
&mut self,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
id: &JoinPointId,
|
||||
parameters: &'a [Param<'a>],
|
||||
body: &'a Stmt<'a>,
|
||||
|
@ -429,6 +522,7 @@ trait Backend<'a> {
|
|||
internal_error!("the argument, {:?}, has no know layout", arg);
|
||||
}
|
||||
}
|
||||
|
||||
self.build_run_low_level(
|
||||
sym,
|
||||
lowlevel,
|
||||
|
@ -440,7 +534,31 @@ trait Backend<'a> {
|
|||
CallType::HigherOrder(higher_order) => {
|
||||
self.build_higher_order_lowlevel(sym, higher_order, *layout)
|
||||
}
|
||||
x => todo!("the call type, {:?}", x),
|
||||
CallType::Foreign {
|
||||
foreign_symbol,
|
||||
ret_layout,
|
||||
} => {
|
||||
let mut arg_layouts: bumpalo::collections::Vec<InLayout<'a>> =
|
||||
bumpalo::vec![in self.env().arena];
|
||||
arg_layouts.reserve(arguments.len());
|
||||
let layout_map = self.layout_map();
|
||||
for arg in *arguments {
|
||||
if let Some(layout) = layout_map.get(arg) {
|
||||
arg_layouts.push(*layout);
|
||||
} else {
|
||||
internal_error!("the argument, {:?}, has no know layout", arg);
|
||||
}
|
||||
}
|
||||
|
||||
self.load_literal_symbols(arguments);
|
||||
self.build_fn_call(
|
||||
sym,
|
||||
foreign_symbol.as_str().to_string(),
|
||||
arguments,
|
||||
arg_layouts.into_bump_slice(),
|
||||
ret_layout,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::EmptyArray => {
|
||||
|
@ -454,9 +572,6 @@ trait Backend<'a> {
|
|||
}) {
|
||||
syms.push(*sym);
|
||||
}
|
||||
// TODO: This could be a huge waste.
|
||||
// We probably want to call this within create_array, one element at a time.
|
||||
self.load_literal_symbols(syms.into_bump_slice());
|
||||
self.create_array(sym, elem_layout, elems);
|
||||
}
|
||||
Expr::Struct(fields) => {
|
||||
|
@ -1259,6 +1374,158 @@ trait Backend<'a> {
|
|||
let intrinsic = bitcode::NUM_IS_MULTIPLE_OF[int_width].to_string();
|
||||
self.build_fn_call(sym, intrinsic, args, arg_layouts, ret_layout);
|
||||
}
|
||||
LowLevel::ListSublist => {
|
||||
// list: RocList,
|
||||
// alignment: u32,
|
||||
// element_width: usize,
|
||||
// start: usize,
|
||||
// len: usize,
|
||||
// dec: Dec,
|
||||
|
||||
let list = args[0];
|
||||
let start = args[1];
|
||||
let len = args[2];
|
||||
|
||||
let list_layout = arg_layouts[0];
|
||||
let list_argument = self.list_argument(list_layout);
|
||||
let element_layout = list_argument.element_layout;
|
||||
|
||||
let args = [
|
||||
list,
|
||||
list_argument.alignment,
|
||||
list_argument.element_width,
|
||||
start,
|
||||
len,
|
||||
self.decrement_fn_pointer(element_layout),
|
||||
];
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
|
||||
let arg_layouts = [
|
||||
arg_layouts[0],
|
||||
Layout::U32,
|
||||
layout_usize,
|
||||
arg_layouts[1],
|
||||
arg_layouts[2],
|
||||
layout_usize,
|
||||
];
|
||||
|
||||
let intrinsic = bitcode::LIST_SUBLIST.to_string();
|
||||
self.build_fn_call(sym, intrinsic, &args, &arg_layouts, &list_layout);
|
||||
}
|
||||
LowLevel::ListSwap => {
|
||||
let list = args[0];
|
||||
let i = args[1];
|
||||
let j = args[2];
|
||||
|
||||
let list_layout = arg_layouts[0];
|
||||
let list_argument = self.list_argument(list_layout);
|
||||
|
||||
let update_mode = self.debug_symbol("update_mode");
|
||||
self.load_literal_i8(&update_mode, UpdateMode::Immutable as i8);
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
|
||||
// list: RocList,
|
||||
// alignment: u32,
|
||||
// element_width: usize,
|
||||
// index_1: usize,
|
||||
// index_2: usize,
|
||||
// update_mode: UpdateMode,
|
||||
|
||||
self.build_fn_call(
|
||||
sym,
|
||||
bitcode::LIST_SWAP.to_string(),
|
||||
&[
|
||||
list,
|
||||
list_argument.alignment,
|
||||
list_argument.element_width,
|
||||
i,
|
||||
j,
|
||||
update_mode,
|
||||
],
|
||||
&[
|
||||
list_layout,
|
||||
Layout::U32,
|
||||
layout_usize,
|
||||
layout_usize,
|
||||
layout_usize,
|
||||
Layout::U8,
|
||||
],
|
||||
ret_layout,
|
||||
);
|
||||
}
|
||||
LowLevel::ListReleaseExcessCapacity => {
|
||||
let list = args[0];
|
||||
|
||||
let list_layout = arg_layouts[0];
|
||||
let list_argument = self.list_argument(list_layout);
|
||||
|
||||
let update_mode = self.debug_symbol("update_mode");
|
||||
self.load_literal_i8(&update_mode, UpdateMode::Immutable as i8);
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
|
||||
// list: RocList,
|
||||
// alignment: u32,
|
||||
// element_width: usize,
|
||||
// update_mode: UpdateMode,
|
||||
|
||||
self.build_fn_call(
|
||||
sym,
|
||||
bitcode::LIST_RELEASE_EXCESS_CAPACITY.to_string(),
|
||||
&[
|
||||
list,
|
||||
list_argument.alignment,
|
||||
list_argument.element_width,
|
||||
update_mode,
|
||||
],
|
||||
&[list_layout, Layout::U32, layout_usize, Layout::U8],
|
||||
ret_layout,
|
||||
);
|
||||
}
|
||||
|
||||
LowLevel::ListDropAt => {
|
||||
let list = args[0];
|
||||
let drop_index = args[1];
|
||||
|
||||
let list_layout = arg_layouts[0];
|
||||
let list_argument = self.list_argument(list_layout);
|
||||
let element_layout = list_argument.element_layout;
|
||||
|
||||
let update_mode = self.debug_symbol("update_mode");
|
||||
self.load_literal_i8(&update_mode, UpdateMode::Immutable as i8);
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
let element_decrement = self.decrement_fn_pointer(element_layout);
|
||||
|
||||
// list: RocList,
|
||||
// alignment: u32,
|
||||
// element_width: usize,
|
||||
// drop_index: usize,
|
||||
// dec: Dec,
|
||||
|
||||
self.build_fn_call(
|
||||
sym,
|
||||
bitcode::LIST_DROP_AT.to_string(),
|
||||
&[
|
||||
list,
|
||||
list_argument.alignment,
|
||||
list_argument.element_width,
|
||||
drop_index,
|
||||
element_decrement,
|
||||
],
|
||||
&[
|
||||
list_layout,
|
||||
Layout::U32,
|
||||
layout_usize,
|
||||
layout_usize,
|
||||
layout_usize,
|
||||
],
|
||||
ret_layout,
|
||||
);
|
||||
}
|
||||
|
||||
x => todo!("low level, {:?}", x),
|
||||
}
|
||||
}
|
||||
|
@ -1560,6 +1827,9 @@ trait Backend<'a> {
|
|||
ret_layout: InLayout<'a>,
|
||||
);
|
||||
|
||||
fn build_indirect_inc(&mut self, layout: InLayout<'a>) -> Symbol;
|
||||
fn build_indirect_dec(&mut self, layout: InLayout<'a>) -> Symbol;
|
||||
|
||||
/// build_list_with_capacity creates and returns a list with the given capacity.
|
||||
fn build_list_with_capacity(
|
||||
&mut self,
|
||||
|
@ -1656,6 +1926,30 @@ trait Backend<'a> {
|
|||
/// load_literal sets a symbol to be equal to a literal.
|
||||
fn load_literal(&mut self, sym: &Symbol, layout: &InLayout<'a>, lit: &Literal<'a>);
|
||||
|
||||
fn load_literal_i64(&mut self, sym: &Symbol, value: i64) {
|
||||
let literal = Literal::Int((value as i128).to_ne_bytes());
|
||||
|
||||
self.load_literal(sym, &Layout::I64, &literal)
|
||||
}
|
||||
|
||||
fn load_literal_i32(&mut self, sym: &Symbol, value: i32) {
|
||||
let literal = Literal::Int((value as i128).to_ne_bytes());
|
||||
|
||||
self.load_literal(sym, &Layout::I32, &literal)
|
||||
}
|
||||
|
||||
fn load_literal_i16(&mut self, sym: &Symbol, value: i16) {
|
||||
let literal = Literal::Int((value as i128).to_ne_bytes());
|
||||
|
||||
self.load_literal(sym, &Layout::I16, &literal)
|
||||
}
|
||||
|
||||
fn load_literal_i8(&mut self, sym: &Symbol, value: i8) {
|
||||
let literal = Literal::Int((value as i128).to_ne_bytes());
|
||||
|
||||
self.load_literal(sym, &Layout::I8, &literal)
|
||||
}
|
||||
|
||||
/// create_empty_array creates an empty array with nullptr, zero length, and zero capacity.
|
||||
fn create_empty_array(&mut self, sym: &Symbol);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue