mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 00:09:33 +03:00
Defer specialization by one level only
This commit is contained in:
parent
ff7429cab4
commit
c6a0970b73
@ -227,8 +227,14 @@ pub fn build(
|
||||
|
||||
// Populate Procs further and get the low-level Expr from the canonical Expr
|
||||
let main_body = Expr::new(&mut mono_env, loc_expr.value, &mut procs);
|
||||
let mut headers = {
|
||||
let num_headers = match &procs.pending_specializations {
|
||||
Some(map) => map.len(),
|
||||
None => 0,
|
||||
};
|
||||
|
||||
let mut headers = Vec::with_capacity(procs.pending_specializations.len());
|
||||
Vec::with_capacity(num_headers)
|
||||
};
|
||||
let mut layout_cache = LayoutCache::default();
|
||||
let mut procs = roc_mono::expr::specialize_all(&mut mono_env, procs, &mut layout_cache);
|
||||
|
||||
|
@ -75,8 +75,14 @@ macro_rules! assert_llvm_evals_to {
|
||||
};
|
||||
|
||||
let main_body = Expr::new(&mut mono_env, loc_expr.value, &mut procs);
|
||||
let mut headers = {
|
||||
let num_headers = match &procs.pending_specializations {
|
||||
Some(map) => map.len(),
|
||||
None => 0
|
||||
};
|
||||
|
||||
let mut headers = Vec::with_capacity(procs.pending_specializations.len());
|
||||
Vec::with_capacity(num_headers)
|
||||
};
|
||||
let mut layout_cache = roc_mono::layout::LayoutCache::default();
|
||||
let mut procs = roc_mono::expr::specialize_all(&mut mono_env, procs, &mut layout_cache);
|
||||
|
||||
@ -246,7 +252,14 @@ macro_rules! assert_opt_evals_to {
|
||||
};
|
||||
let main_body = Expr::new(&mut mono_env, loc_expr.value, &mut procs);
|
||||
|
||||
let mut headers = Vec::with_capacity(procs.pending_specializations.len());
|
||||
let mut headers = {
|
||||
let num_headers = match &procs.pending_specializations {
|
||||
Some(map) => map.len(),
|
||||
None => 0
|
||||
};
|
||||
|
||||
Vec::with_capacity(num_headers)
|
||||
};
|
||||
let mut layout_cache = roc_mono::layout::LayoutCache::default();
|
||||
let mut procs = roc_mono::expr::specialize_all(&mut mono_env, procs, &mut layout_cache);
|
||||
|
||||
|
@ -38,7 +38,8 @@ pub struct Proc<'a> {
|
||||
pub struct Procs<'a> {
|
||||
pub partial_procs: MutMap<Symbol, PartialProc<'a>>,
|
||||
pub module_thunks: MutSet<Symbol>,
|
||||
pub pending_specializations: MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization<'a>>>,
|
||||
pub pending_specializations:
|
||||
Option<MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization<'a>>>>,
|
||||
pub specialized: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
||||
pub runtime_errors: MutSet<Symbol>,
|
||||
}
|
||||
@ -81,8 +82,7 @@ impl<'a> Procs<'a> {
|
||||
ret_var: Variable,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
) -> Layout<'a> {
|
||||
let (pattern_vars, pattern_symbols, body) =
|
||||
patterns_to_when(env, loc_args, ret_var, loc_body);
|
||||
let (_, pattern_symbols, body) = patterns_to_when(env, loc_args, ret_var, loc_body);
|
||||
|
||||
// an anonymous closure. These will always be specialized already
|
||||
// by the surrounding context, so we can add pending specializations
|
||||
@ -104,20 +104,19 @@ impl<'a> Procs<'a> {
|
||||
|
||||
layout
|
||||
}
|
||||
}
|
||||
|
||||
fn add_pending_specialization(
|
||||
&mut self,
|
||||
symbol: Symbol,
|
||||
layout: Layout<'a>,
|
||||
pending: PendingSpecialization<'a>,
|
||||
) {
|
||||
let all_pending = self
|
||||
.pending_specializations
|
||||
.entry(symbol)
|
||||
.or_insert_with(|| HashMap::with_capacity_and_hasher(1, default_hasher()));
|
||||
fn add_pending<'a>(
|
||||
pending_specializations: &mut MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization<'a>>>,
|
||||
symbol: Symbol,
|
||||
layout: Layout<'a>,
|
||||
pending: PendingSpecialization<'a>,
|
||||
) {
|
||||
let all_pending = pending_specializations
|
||||
.entry(symbol)
|
||||
.or_insert_with(|| HashMap::with_capacity_and_hasher(1, default_hasher()));
|
||||
|
||||
all_pending.insert(layout, pending);
|
||||
}
|
||||
all_pending.insert(layout, pending);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -1319,19 +1318,40 @@ fn call_by_name<'a>(
|
||||
fn_var,
|
||||
};
|
||||
|
||||
// TODO should pending_procs hold a Rc<Proc>?
|
||||
let partial_proc = procs
|
||||
.partial_procs
|
||||
.get(&proc_name)
|
||||
.unwrap_or_else(|| panic!("Could not find partial_proc for {:?}", proc_name))
|
||||
.clone();
|
||||
|
||||
match specialize(env, procs, proc_name, layout_cache, pending, partial_proc) {
|
||||
Ok(proc) => {
|
||||
procs.specialized.insert((proc_name, layout.clone()), proc);
|
||||
// When requested (that is, when procs.pending_specializations is `Some`),
|
||||
// store a pending specialization rather than specializing immediately.
|
||||
//
|
||||
// We do this so that we can do specialization in two passes: first,
|
||||
// build the mono_expr with all the specialized calls in place (but
|
||||
// no specializations performed yet), and then second, *after*
|
||||
// de-duplicating requested specializations (since multiple modules
|
||||
// which could be getting monomorphized in parallel might request
|
||||
// the same specialization independently), we work through the
|
||||
// queue of pending specializations to complete each specialization
|
||||
// exactly once.
|
||||
match &mut procs.pending_specializations {
|
||||
Some(pending_specializations) => {
|
||||
// register the pending specialization, so this gets code genned later
|
||||
add_pending(pending_specializations, proc_name, layout.clone(), pending);
|
||||
}
|
||||
Err(_) => {
|
||||
procs.runtime_errors.insert(proc_name);
|
||||
None => {
|
||||
// TODO should pending_procs hold a Rc<Proc>?
|
||||
let partial_proc = procs
|
||||
.partial_procs
|
||||
.get(&proc_name)
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Could not find partial_proc for {:?}", proc_name)
|
||||
})
|
||||
.clone();
|
||||
|
||||
match specialize(env, procs, proc_name, layout_cache, pending, partial_proc) {
|
||||
Ok(proc) => {
|
||||
procs.specialized.insert((proc_name, layout.clone()), proc);
|
||||
}
|
||||
Err(_) => {
|
||||
procs.runtime_errors.insert(proc_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1354,61 +1374,39 @@ pub fn specialize_all<'a>(
|
||||
mut procs: Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
) -> Procs<'a> {
|
||||
let mut is_finished = procs.pending_specializations.is_empty();
|
||||
let mut pending_specializations = procs.pending_specializations.unwrap_or(MutMap::default());
|
||||
|
||||
// TODO replace this synchronous loop with a work-stealing queue which
|
||||
// processes each entry in pending_specializations in parallel, one
|
||||
// module at a time (because the &mut env will need exclusive access to
|
||||
// that module's IdentIds; the only reason Env is &mut in specialize is
|
||||
// that we need to generate unique symbols and register them in them module's
|
||||
// IdentIds).
|
||||
while !is_finished {
|
||||
let Procs {
|
||||
partial_procs,
|
||||
module_thunks,
|
||||
mut pending_specializations,
|
||||
specialized,
|
||||
runtime_errors,
|
||||
} = procs;
|
||||
// When calling from_can, pending_specializations should be unavailable.
|
||||
// This must be a single pass, and we must not add any more entries to it!
|
||||
procs.pending_specializations = None;
|
||||
|
||||
procs = Procs {
|
||||
partial_procs,
|
||||
module_thunks,
|
||||
pending_specializations: MutMap::default(),
|
||||
specialized,
|
||||
runtime_errors,
|
||||
};
|
||||
for (name, mut by_layout) in pending_specializations.drain() {
|
||||
// Use the function's symbol's home module as the home module
|
||||
// when doing canonicalization. This will be important to determine
|
||||
// whether or not it's safe to defer specialization.
|
||||
env.home = name.module_id();
|
||||
|
||||
for (name, mut by_layout) in pending_specializations.drain() {
|
||||
// Use the function's symbol's home module as the home module
|
||||
// when doing canonicalization. This will be important to determine
|
||||
// whether or not it's safe to defer specialization.
|
||||
env.home = name.module_id();
|
||||
for (layout, pending) in by_layout.drain() {
|
||||
// If we've already seen this (Symbol, Layout) combination before,
|
||||
// don't try to specialize it again. If we do, we'll loop forever!
|
||||
if !procs.specialized.contains_key(&(name, layout.clone())) {
|
||||
// TODO should pending_procs hold a Rc<Proc>?
|
||||
let partial_proc = procs
|
||||
.partial_procs
|
||||
.get(&name)
|
||||
.unwrap_or_else(|| panic!("Could not find partial_proc for {:?}", name))
|
||||
.clone();
|
||||
|
||||
for (layout, pending) in by_layout.drain() {
|
||||
// If we've already seen this (Symbol, Layout) combination before,
|
||||
// don't try to specialize it again. If we do, we'll loop forever!
|
||||
if !procs.specialized.contains_key(&(name, layout.clone())) {
|
||||
// TODO should pending_procs hold a Rc<Proc>?
|
||||
let partial_proc = procs
|
||||
.partial_procs
|
||||
.get(&name)
|
||||
.unwrap_or_else(|| panic!("Could not find partial_proc for {:?}", name))
|
||||
.clone();
|
||||
|
||||
match specialize(env, &mut procs, name, layout_cache, pending, partial_proc) {
|
||||
Ok(proc) => {
|
||||
procs.specialized.insert((name, layout), proc);
|
||||
}
|
||||
Err(_) => {
|
||||
procs.runtime_errors.insert(name);
|
||||
}
|
||||
match specialize(env, &mut procs, name, layout_cache, pending, partial_proc) {
|
||||
Ok(proc) => {
|
||||
procs.specialized.insert((name, layout), proc);
|
||||
}
|
||||
Err(_) => {
|
||||
procs.runtime_errors.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_finished = procs.pending_specializations.is_empty();
|
||||
}
|
||||
|
||||
procs
|
||||
|
Loading…
Reference in New Issue
Block a user