Traverse nested layouts for code gen helpers

This commit is contained in:
Brian Carroll 2021-12-15 01:03:51 +00:00
parent ae9e5f115f
commit 3eb9e9f7ac
3 changed files with 145 additions and 75 deletions

View file

@ -148,7 +148,7 @@ trait Backend<'a> {
// Expand the Refcounting statement into more detailed IR with a function call // 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 // 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. // 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 (rc_stmt, new_specializations) = {
let (env, interns, rc_proc_gen) = self.env_interns_helpers_mut(); let (env, interns, rc_proc_gen) = self.env_interns_helpers_mut();
let module_id = env.module_id; let module_id = env.module_id;
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap(); let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
@ -156,9 +156,8 @@ trait Backend<'a> {
rc_proc_gen.expand_refcount_stmt(ident_ids, layout, modify, *following) rc_proc_gen.expand_refcount_stmt(ident_ids, layout, modify, *following)
}; };
if let Some((rc_proc_symbol, rc_proc_layout)) = new_proc_info { for spec in new_specializations.into_iter() {
self.helper_proc_symbols_mut() self.helper_proc_symbols_mut().push(spec);
.push((rc_proc_symbol, rc_proc_layout));
} }
self.build_stmt(rc_stmt, ret_layout) self.build_stmt(rc_stmt, ret_layout)

View file

@ -519,7 +519,7 @@ impl<'a> WasmBackend<'a> {
.get_mut(&self.env.module_id) .get_mut(&self.env.module_id)
.unwrap(); .unwrap();
let (rc_stmt, maybe_new_proc_info) = self let (rc_stmt, new_specializations) = self
.helper_proc_gen .helper_proc_gen
.expand_refcount_stmt(ident_ids, *layout, modify, *following); .expand_refcount_stmt(ident_ids, *layout, modify, *following);
@ -528,8 +528,10 @@ impl<'a> WasmBackend<'a> {
println!("## rc_stmt:\n{}\n{:?}", rc_stmt.to_pretty(200), rc_stmt); println!("## rc_stmt:\n{}\n{:?}", rc_stmt.to_pretty(200), rc_stmt);
} }
// If this is the first call to a new helper proc, register its symbol data // If any new specializations were created, register their symbol data
maybe_new_proc_info.map(|info| self.register_helper_proc(info)); for spec in new_specializations.into_iter() {
self.register_helper_proc(spec);
}
self.build_stmt(rc_stmt, ret_layout); self.build_stmt(rc_stmt, ret_layout);
} }
@ -970,12 +972,14 @@ impl<'a> WasmBackend<'a> {
.get_mut(&self.env.module_id) .get_mut(&self.env.module_id)
.unwrap(); .unwrap();
let (replacement_expr, maybe_new_proc_info) = self let (replacement_expr, new_specializations) = self
.helper_proc_gen .helper_proc_gen
.replace_generic_equals(ident_ids, &layout, arguments); .specialize_equals(ident_ids, &layout, arguments);
// If this is the first call to a new helper proc, register its symbol data // If any new specializations were created, register their symbol data
maybe_new_proc_info.map(|info| self.register_helper_proc(info)); for spec in new_specializations.into_iter() {
self.register_helper_proc(spec);
}
self.build_expr(&return_sym, replacement_expr, &layout, storage); self.build_expr(&return_sym, replacement_expr, &layout, storage);
} }

View file

@ -9,7 +9,7 @@ use crate::ir::{
BranchInfo, Call, CallSpecId, CallType, Expr, HostExposedLayouts, Literal, ModifyRc, Proc, BranchInfo, Call, CallSpecId, CallType, Expr, HostExposedLayouts, Literal, ModifyRc, Proc,
ProcLayout, SelfRecursive, Stmt, UpdateModeId, ProcLayout, SelfRecursive, Stmt, UpdateModeId,
}; };
use crate::layout::{Builtin, Layout}; use crate::layout::{Builtin, Layout, UnionLayout};
const LAYOUT_BOOL: Layout = Layout::Builtin(Builtin::Bool); const LAYOUT_BOOL: Layout = Layout::Builtin(Builtin::Bool);
const LAYOUT_UNIT: Layout = Layout::Struct(&[]); const LAYOUT_UNIT: Layout = Layout::Struct(&[]);
@ -99,7 +99,7 @@ impl<'a> CodeGenHelp<'a> {
layout: Layout<'a>, layout: Layout<'a>,
modify: &ModifyRc, modify: &ModifyRc,
following: &'a Stmt<'a>, following: &'a Stmt<'a>,
) -> (&'a Stmt<'a>, Option<(Symbol, ProcLayout<'a>)>) { ) -> (&'a Stmt<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
if !Self::is_rc_implemented_yet(&layout) { if !Self::is_rc_implemented_yet(&layout) {
// Just a warning, so we can decouple backend development from refcounting development. // Just a warning, so we can decouple backend development from refcounting development.
// When we are closer to completion, we can change it to a panic. // When we are closer to completion, we can change it to a panic.
@ -107,7 +107,7 @@ impl<'a> CodeGenHelp<'a> {
"WARNING! MEMORY LEAK! Refcounting not yet implemented for Layout {:?}", "WARNING! MEMORY LEAK! Refcounting not yet implemented for Layout {:?}",
layout layout
); );
return (following, None); return (following, Vec::new_in(self.arena));
} }
let arena = self.arena; let arena = self.arena;
@ -116,8 +116,11 @@ impl<'a> CodeGenHelp<'a> {
ModifyRc::Inc(structure, amount) => { ModifyRc::Inc(structure, amount) => {
let layout_isize = self.layout_isize; let layout_isize = self.layout_isize;
let (is_existing, proc_name) = let (proc_name, new_procs_info) = self.get_or_create_proc_symbols_recursive(
self.get_proc_symbol(ident_ids, &layout, HelperOp::Rc(RefcountOp::Inc)); ident_ids,
&layout,
HelperOp::Rc(RefcountOp::Inc),
);
// Define a constant for the amount to increment // Define a constant for the amount to increment
let amount_sym = self.create_symbol(ident_ids, "amount"); let amount_sym = self.create_symbol(ident_ids, "amount");
@ -139,28 +142,17 @@ impl<'a> CodeGenHelp<'a> {
let call_stmt = Stmt::Let(call_result_empty, call_expr, LAYOUT_UNIT, following); let call_stmt = Stmt::Let(call_result_empty, call_expr, LAYOUT_UNIT, following);
let rc_stmt = arena.alloc(amount_stmt(arena.alloc(call_stmt))); let rc_stmt = arena.alloc(amount_stmt(arena.alloc(call_stmt)));
// Create a linker symbol for the helper proc if this is the first usage (rc_stmt, new_procs_info)
let new_proc_info = if is_existing {
None
} else {
Some((
proc_name,
ProcLayout {
arguments: arg_layouts,
result: LAYOUT_UNIT,
},
))
};
(rc_stmt, new_proc_info)
} }
ModifyRc::Dec(structure) => { ModifyRc::Dec(structure) => {
let (is_existing, proc_name) = let (proc_name, new_procs_info) = self.get_or_create_proc_symbols_recursive(
self.get_proc_symbol(ident_ids, &layout, HelperOp::Rc(RefcountOp::Dec)); ident_ids,
&layout,
HelperOp::Rc(RefcountOp::Dec),
);
// Call helper proc, passing the Roc structure // Call helper proc, passing the Roc structure
let arg_layouts = arena.alloc([layout, self.layout_isize]);
let call_result_empty = self.create_symbol(ident_ids, "call_result_empty"); let call_result_empty = self.create_symbol(ident_ids, "call_result_empty");
let call_expr = Expr::Call(Call { let call_expr = Expr::Call(Call {
call_type: CallType::ByName { call_type: CallType::ByName {
@ -179,20 +171,7 @@ impl<'a> CodeGenHelp<'a> {
following, following,
)); ));
// Create a linker symbol for the helper proc if this is the first usage (rc_stmt, new_procs_info)
let new_proc_info = if is_existing {
None
} else {
Some((
proc_name,
ProcLayout {
arguments: arg_layouts,
result: LAYOUT_UNIT,
},
))
};
(rc_stmt, new_proc_info)
} }
ModifyRc::DecRef(structure) => { ModifyRc::DecRef(structure) => {
@ -221,21 +200,22 @@ impl<'a> CodeGenHelp<'a> {
let call_stmt = Stmt::Let(call_result_empty, call_expr, LAYOUT_UNIT, following); let call_stmt = Stmt::Let(call_result_empty, call_expr, LAYOUT_UNIT, following);
let rc_stmt = arena.alloc(rc_ptr_stmt(arena.alloc(call_stmt))); let rc_stmt = arena.alloc(rc_ptr_stmt(arena.alloc(call_stmt)));
(rc_stmt, None) (rc_stmt, Vec::new_in(self.arena))
} }
} }
} }
/// Replace a generic `Lowlevel::Eq` call with a specialized helper proc. /// Replace a generic `Lowlevel::Eq` call with a specialized helper proc.
/// The helper procs themselves are to be generated later with `generate_procs` /// The helper procs themselves are to be generated later with `generate_procs`
pub fn replace_generic_equals( pub fn specialize_equals(
&mut self, &mut self,
ident_ids: &mut IdentIds, ident_ids: &mut IdentIds,
layout: &Layout<'a>, layout: &Layout<'a>,
arguments: &'a [Symbol], arguments: &'a [Symbol],
) -> (&'a Expr<'a>, Option<(Symbol, ProcLayout<'a>)>) { ) -> (&'a Expr<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
// Record a specialization and get its name // Record a specialization and get its name
let (is_existing, proc_name) = self.get_proc_symbol(ident_ids, layout, HelperOp::Eq); let (proc_name, new_procs_info) =
self.get_or_create_proc_symbols_recursive(ident_ids, layout, HelperOp::Eq);
// Call the specialized helper // Call the specialized helper
let arg_layouts = self.arena.alloc([*layout, *layout]); let arg_layouts = self.arena.alloc([*layout, *layout]);
@ -249,20 +229,7 @@ impl<'a> CodeGenHelp<'a> {
arguments, arguments,
})); }));
// Create a linker symbol for the helper proc if this is the first usage (expr, new_procs_info)
let new_proc_info = if is_existing {
None
} else {
Some((
proc_name,
ProcLayout {
arguments: arg_layouts,
result: LAYOUT_BOOL,
},
))
};
(expr, new_proc_info)
} }
// Check if refcounting is implemented yet. In the long term, this will be deleted. // Check if refcounting is implemented yet. In the long term, this will be deleted.
@ -300,7 +267,10 @@ impl<'a> CodeGenHelp<'a> {
HelperOp::Eq => match layout { HelperOp::Eq => match layout {
Layout::Builtin( Layout::Builtin(
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal, Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal,
) => panic!("No generated helper proc. Use direct code gen for {:?}", layout), ) => panic!(
"No generated helper proc. Use direct code gen for {:?}",
layout
),
Layout::Builtin(Builtin::Str) => { Layout::Builtin(Builtin::Str) => {
panic!("No generated helper proc. Use Zig builtin for Str.") panic!("No generated helper proc. Use Zig builtin for Str.")
@ -313,25 +283,122 @@ impl<'a> CodeGenHelp<'a> {
Vec::from_iter_in(procs_iter, arena) Vec::from_iter_in(procs_iter, arena)
} }
/// Find the Symbol of the procedure for this layout and refcount operation, /// Find the Symbol of the procedure for this layout and operation
/// or create one if needed. /// If any new helper procs are needed for this layout or its children,
fn get_proc_symbol( /// return their details in a vector.
fn get_or_create_proc_symbols_recursive(
&mut self, &mut self,
ident_ids: &mut IdentIds, ident_ids: &mut IdentIds,
layout: &Layout<'a>, layout: &Layout<'a>,
op: HelperOp, op: HelperOp,
) -> (bool, Symbol) { ) -> (Symbol, Vec<'a, (Symbol, ProcLayout<'a>)>) {
let mut new_procs_info = Vec::new_in(self.arena);
let proc_symbol =
self.get_or_create_proc_symbols_visit(ident_ids, &mut new_procs_info, op, layout);
(proc_symbol, new_procs_info)
}
fn get_or_create_proc_symbols_visit(
&mut self,
ident_ids: &mut IdentIds,
new_procs_info: &mut Vec<'a, (Symbol, ProcLayout<'a>)>,
op: HelperOp,
layout: &Layout<'a>,
) -> Symbol {
if let Layout::LambdaSet(lambda_set) = layout {
return self.get_or_create_proc_symbols_visit(
ident_ids,
new_procs_info,
op,
&lambda_set.runtime_representation(),
);
}
let (symbol, new_proc_layout) = self.get_or_create_proc_symbol(ident_ids, layout, op);
if let Some(proc_layout) = new_proc_layout {
new_procs_info.push((symbol, proc_layout));
let mut visit_child = |child| {
self.get_or_create_proc_symbols_visit(ident_ids, new_procs_info, op, child);
};
let mut visit_children = |children: &'a [Layout]| {
for child in children {
visit_child(child);
}
};
let mut visit_tags = |tags: &'a [&'a [Layout]]| {
for tag in tags {
visit_children(tag);
}
};
match layout {
Layout::Builtin(builtin) => match builtin {
Builtin::Dict(key, value) => {
visit_child(key);
visit_child(value);
}
Builtin::Set(element) | Builtin::List(element) => visit_child(element),
_ => {}
},
Layout::Struct(fields) => visit_children(fields),
Layout::Union(union_layout) => match union_layout {
UnionLayout::NonRecursive(tags) => visit_tags(tags),
UnionLayout::Recursive(tags) => visit_tags(tags),
UnionLayout::NonNullableUnwrapped(fields) => visit_children(fields),
UnionLayout::NullableWrapped { other_tags, .. } => visit_tags(other_tags),
UnionLayout::NullableUnwrapped { other_fields, .. } => {
visit_children(other_fields)
}
},
Layout::LambdaSet(_) => unreachable!(),
Layout::RecursivePointer => {}
}
}
symbol
}
fn get_or_create_proc_symbol(
&mut self,
ident_ids: &mut IdentIds,
layout: &Layout<'a>,
op: HelperOp,
) -> (Symbol, Option<ProcLayout<'a>>) {
let found = self.specs.iter().find(|(l, o, _)| l == layout && *o == op); let found = self.specs.iter().find(|(l, o, _)| l == layout && *o == op);
if let Some((_, _, existing_symbol)) = found { if let Some((_, _, existing_symbol)) = found {
(true, *existing_symbol) (*existing_symbol, None)
} else { } else {
let layout_name = layout_debug_name(&layout); let layout_name = layout_debug_name(&layout);
let unique_idx = self.specs.len(); let debug_name = format!("#help{:?}_{}", op, layout_name);
let debug_name = format!("#rc{:?}_{}_{}", op, layout_name, unique_idx);
let new_symbol: Symbol = self.create_symbol(ident_ids, &debug_name); let new_symbol: Symbol = self.create_symbol(ident_ids, &debug_name);
self.specs.push((*layout, op, new_symbol)); self.specs.push((*layout, op, new_symbol));
(false, new_symbol)
let new_proc_layout = match op {
HelperOp::Rc(rc) => match rc {
RefcountOp::Inc => Some(ProcLayout {
arguments: self.arena.alloc([*layout, self.layout_isize]),
result: LAYOUT_UNIT,
}),
RefcountOp::Dec => Some(ProcLayout {
arguments: self.arena.alloc([*layout]),
result: LAYOUT_UNIT,
}),
RefcountOp::DecRef => None,
},
HelperOp::Eq => Some(ProcLayout {
arguments: self.arena.alloc([*layout, *layout]),
result: LAYOUT_BOOL,
}),
};
(new_symbol, new_proc_layout)
} }
} }
@ -356,7 +423,7 @@ impl<'a> CodeGenHelp<'a> {
} }
RefcountOp::Dec | RefcountOp::DecRef => self.arena.alloc([roc_value]), RefcountOp::Dec | RefcountOp::DecRef => self.arena.alloc([roc_value]),
}, },
HelperOp::Eq => todo!(), HelperOp::Eq => self.arena.alloc([roc_value, (layout, Symbol::ARG_2)]),
} }
} }