Merge pull request #5365 from roc-lang/dev-backend-cli

Dev backend list tests
This commit is contained in:
Folkert de Vries 2023-05-07 14:47:39 +02:00 committed by GitHub
commit deb1e9952d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 1035 additions and 351 deletions

View file

@ -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, &param.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);