Merge branch 'trunk' into document-only-exposed-values

This commit is contained in:
Chadtech 2021-06-13 13:59:51 -04:00 committed by GitHub
commit 64aba9ed46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1248 additions and 254 deletions

View file

@ -2298,7 +2298,7 @@ fn dict_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
let make_err = tag(
"Err",
vec![tag("OutOfBounds", Vec::new(), var_store)],
vec![tag("KeyNotFound", Vec::new(), var_store)],
var_store,
);

View file

@ -60,7 +60,6 @@ pub enum Expr {
Float(Variable, Variable, f64),
Str(InlinableString),
List {
list_var: Variable, // required for uniqueness of the list
elem_var: Variable,
loc_elems: Vec<Located<Expr>>,
},
@ -304,7 +303,6 @@ pub fn canonicalize_expr<'a>(
if loc_elems.is_empty() {
(
List {
list_var: var_store.fresh(),
elem_var: var_store.fresh(),
loc_elems: Vec::new(),
},
@ -331,7 +329,6 @@ pub fn canonicalize_expr<'a>(
(
List {
list_var: var_store.fresh(),
elem_var: var_store.fresh(),
loc_elems: can_elems,
},
@ -1234,7 +1231,6 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
| other @ ForeignCall { .. } => other,
List {
list_var,
elem_var,
loc_elems,
} => {
@ -1250,7 +1246,6 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
}
List {
list_var,
elem_var,
loc_elems: new_elems,
}

View file

@ -220,7 +220,6 @@ pub fn constrain_expr(
List {
elem_var,
loc_elems,
list_var: _unused,
} => {
if loc_elems.is_empty() {
exists(

View file

@ -16,6 +16,7 @@ roc_builtins = { path = "../builtins" }
roc_unify = { path = "../unify" }
roc_solve = { path = "../solve" }
roc_mono = { path = "../mono" }
morphic_lib = { path = "../../vendor/morphic_lib" }
im = "14" # im and im-rc should always have the same version!
im-rc = "14" # im and im-rc should always have the same version!
bumpalo = { version = "3.6.1", features = ["collections"] }

View file

@ -42,6 +42,7 @@ use inkwell::values::{
};
use inkwell::OptimizationLevel;
use inkwell::{AddressSpace, IntPredicate};
use morphic_lib::{CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions};
use roc_builtins::bitcode;
use roc_collections::all::{ImMap, MutMap, MutSet};
use roc_module::ident::TagName;
@ -583,22 +584,35 @@ pub fn construct_optimization_passes<'a>(
(mpm, fpm)
}
pub fn promote_to_main_function<'a, 'ctx, 'env>(
fn promote_to_main_function<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
mod_solutions: &'a ModSolutions,
symbol: Symbol,
layout: TopLevelFunctionLayout<'a>,
top_level: TopLevelFunctionLayout<'a>,
) -> (&'static str, FunctionValue<'ctx>) {
let fn_name = layout_ids
.get(symbol, &(env.arena.alloc(layout).full()))
.to_symbol_string(symbol, &env.interns);
let it = top_level.arguments.iter().copied();
let bytes = roc_mono::alias_analysis::func_name_bytes_help(symbol, it, top_level.result);
let func_name = FuncName(&bytes);
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
let roc_main_fn = env.module.get_function(&fn_name).unwrap();
let mut it = func_solutions.specs();
let func_spec = it.next().unwrap();
debug_assert!(
it.next().is_none(),
"we expect only one specialization of this symbol"
);
let roc_main_fn = function_value_by_func_spec(env, *func_spec, symbol, Layout::Struct(&[]));
let main_fn_name = "$Test.main";
// Add main to the module.
let main_fn = expose_function_to_host_help(env, roc_main_fn, main_fn_name);
let main_fn = expose_function_to_host_help(
env,
&inlinable_string::InlinableString::from(main_fn_name),
roc_main_fn,
main_fn_name,
);
(main_fn_name, main_fn)
}
@ -772,6 +786,7 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
pub fn build_exp_call<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
func_spec_solutions: &FuncSpecSolutions,
scope: &mut Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>,
layout: &Layout<'a>,
@ -784,7 +799,10 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
match call_type {
CallType::ByName {
name, full_layout, ..
name,
full_layout,
specialization_id,
..
} => {
let mut arg_tuples: Vec<BasicValueEnum> =
Vec::with_capacity_in(arguments.len(), env.arena);
@ -793,12 +811,15 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
arg_tuples.push(load_symbol(scope, symbol));
}
call_with_args(
let bytes = specialization_id.to_bytes();
let callee_var = CalleeSpecVar(&bytes);
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
roc_call_with_args(
env,
layout_ids,
&full_layout,
*name,
parent,
func_spec,
arg_tuples.into_bump_slice(),
)
}
@ -811,16 +832,26 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
op,
closure_layout,
function_owns_closure_data,
} => run_higher_order_low_level(
env,
layout_ids,
scope,
layout,
*op,
*closure_layout,
*function_owns_closure_data,
arguments,
),
specialization_id,
..
} => {
let bytes = specialization_id.to_bytes();
let callee_var = CalleeSpecVar(&bytes);
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
// let fn_val = function_value_by_func_spec(env, func_spec, symbol, *layout);
run_higher_order_low_level(
env,
layout_ids,
scope,
layout,
*op,
*closure_layout,
func_spec,
*function_owns_closure_data,
arguments,
)
}
CallType::Foreign {
foreign_symbol,
@ -831,6 +862,7 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
build_foreign_symbol(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
foreign_symbol,
@ -845,6 +877,7 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
pub fn build_exp_expr<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
func_spec_solutions: &FuncSpecSolutions,
scope: &mut Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>,
layout: &Layout<'a>,
@ -855,7 +888,15 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
match expr {
Literal(literal) => build_exp_literal(env, layout, literal),
Call(call) => build_exp_call(env, layout_ids, scope, parent, layout, call),
Call(call) => build_exp_call(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
layout,
call,
),
Struct(sorted_fields) => {
let ctx = env.context;
@ -1802,6 +1843,7 @@ fn list_literal<'a, 'ctx, 'env>(
fn invoke_roc_function<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
func_spec_solutions: &FuncSpecSolutions,
scope: &mut Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>,
symbol: Symbol,
@ -1846,7 +1888,7 @@ fn invoke_roc_function<'a, 'ctx, 'env>(
scope.insert(symbol, (layout, call_result));
build_exp_stmt(env, layout_ids, scope, parent, pass);
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, pass);
scope.remove(&symbol);
}
@ -1876,7 +1918,7 @@ fn invoke_roc_function<'a, 'ctx, 'env>(
(Layout::Struct(&[]), exception_object),
);
build_exp_stmt(env, layout_ids, scope, parent, fail);
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, fail);
}
call_result
@ -1911,6 +1953,7 @@ fn decrement_with_size_check<'a, 'ctx, 'env>(
pub fn build_exp_stmt<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
func_spec_solutions: &FuncSpecSolutions,
scope: &mut Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>,
stmt: &roc_mono::ir::Stmt<'a>,
@ -1935,7 +1978,15 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
for (symbol, expr, layout) in queue {
debug_assert!(layout != &Layout::RecursivePointer);
let val = build_exp_expr(env, layout_ids, scope, parent, layout, &expr);
let val = build_exp_expr(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
layout,
&expr,
);
// Make a new scope which includes the binding we just encountered.
// This should be done *after* compiling the bound expr, since any
@ -1948,7 +1999,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
stack.push(*symbol);
}
let result = build_exp_stmt(env, layout_ids, scope, parent, cont);
let result = build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont);
for symbol in stack {
scope.remove(&symbol);
@ -1979,7 +2030,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
// when the fail case is just Rethrow, there is no cleanup work to do
// so we can just treat this invoke as a normal call
let stmt = roc_mono::ir::Stmt::Let(*symbol, Expr::Call(call.clone()), *layout, pass);
build_exp_stmt(env, layout_ids, scope, parent, &stmt)
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, &stmt)
}
Invoke {
@ -1993,13 +2044,20 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
CallType::ByName {
name,
ref full_layout,
specialization_id,
..
} => {
let function_value = function_value_by_name(env, layout_ids, *full_layout, name);
let bytes = specialization_id.to_bytes();
let callee_var = CalleeSpecVar(&bytes);
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
let function_value =
function_value_by_func_spec(env, func_spec, name, *full_layout);
invoke_roc_function(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
*symbol,
@ -2019,6 +2077,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
} => build_foreign_symbol(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
foreign_symbol,
@ -2065,7 +2124,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
ret_type,
};
build_switch_ir(env, layout_ids, scope, parent, switch_args)
build_switch_ir(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
switch_args,
)
}
Join {
id,
@ -2096,7 +2162,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
scope.join_points.insert(*id, (cont_block, joinpoint_args));
// construct the blocks that may jump to this join point
build_exp_stmt(env, layout_ids, scope, parent, remainder);
build_exp_stmt(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
remainder,
);
let phi_block = builder.get_insert_block().unwrap();
@ -2109,7 +2182,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
}
// put the continuation in
let result = build_exp_stmt(env, layout_ids, scope, parent, continuation);
let result = build_exp_stmt(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
continuation,
);
// remove this join point again
scope.join_points.remove(&id);
@ -2153,7 +2233,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
);
}
build_exp_stmt(env, layout_ids, scope, parent, cont)
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont)
}
Dec(symbol) => {
let (value, layout) = load_symbol_and_layout(scope, symbol);
@ -2162,7 +2242,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
decrement_refcount_layout(env, parent, layout_ids, value, layout);
}
build_exp_stmt(env, layout_ids, scope, parent, cont)
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont)
}
DecRef(symbol) => {
let (value, layout) = load_symbol_and_layout(scope, symbol);
@ -2214,7 +2294,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
}
}
build_exp_stmt(env, layout_ids, scope, parent, cont)
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont)
}
}
}
@ -2404,6 +2484,7 @@ fn const_i128<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, value: i128) -> IntValu
fn build_switch_ir<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
func_spec_solutions: &FuncSpecSolutions,
scope: &Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>,
switch_args: SwitchArgsIr<'a, 'ctx>,
@ -2526,7 +2607,14 @@ fn build_switch_ir<'a, 'ctx, 'env>(
{
builder.position_at_end(then_block);
let branch_val = build_exp_stmt(env, layout_ids, scope, parent, true_branch);
let branch_val = build_exp_stmt(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
true_branch,
);
if then_block.get_terminator().is_none() {
builder.build_unconditional_branch(cont_block);
@ -2537,7 +2625,14 @@ fn build_switch_ir<'a, 'ctx, 'env>(
{
builder.position_at_end(else_block);
let branch_val = build_exp_stmt(env, layout_ids, scope, parent, false_branch);
let branch_val = build_exp_stmt(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
false_branch,
);
if else_block.get_terminator().is_none() {
builder.build_unconditional_branch(cont_block);
@ -2587,7 +2682,14 @@ fn build_switch_ir<'a, 'ctx, 'env>(
for ((_, _, branch_expr), (_, block)) in branches.iter().zip(cases) {
builder.position_at_end(block);
let branch_val = build_exp_stmt(env, layout_ids, scope, parent, branch_expr);
let branch_val = build_exp_stmt(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
branch_expr,
);
if block.get_terminator().is_none() {
builder.build_unconditional_branch(cont_block);
@ -2598,7 +2700,14 @@ fn build_switch_ir<'a, 'ctx, 'env>(
// The block for the conditional's default branch.
builder.position_at_end(default_block);
let default_val = build_exp_stmt(env, layout_ids, scope, parent, default_branch);
let default_val = build_exp_stmt(
env,
layout_ids,
func_spec_solutions,
scope,
parent,
default_branch,
);
if default_block.get_terminator().is_none() {
builder.build_unconditional_branch(cont_block);
@ -2646,16 +2755,19 @@ pub fn create_entry_block_alloca<'a, 'ctx>(
fn expose_function_to_host<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
symbol: Symbol,
roc_function: FunctionValue<'ctx>,
) {
let c_function_name: String =
format!("roc_{}_exposed", roc_function.get_name().to_str().unwrap());
// Assumption: there is only one specialization of a host-exposed function
let ident_string = symbol.ident_string(&env.interns);
let c_function_name: String = format!("roc__{}_1_exposed", ident_string);
expose_function_to_host_help(env, roc_function, &c_function_name);
expose_function_to_host_help(env, ident_string, roc_function, &c_function_name);
}
fn expose_function_to_host_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
ident_string: &inlinable_string::InlinableString,
roc_function: FunctionValue<'ctx>,
c_function_name: &str,
) -> FunctionValue<'ctx> {
@ -2716,8 +2828,7 @@ fn expose_function_to_host_help<'a, 'ctx, 'env>(
// STEP 3: build a {} -> u64 function that gives the size of the return type
let size_function_type = env.context.i64_type().fn_type(&[], false);
let size_function_name: String =
format!("roc_{}_size", roc_function.get_name().to_str().unwrap());
let size_function_name: String = format!("roc__{}_size", ident_string);
let size_function = add_func(
env.module,
@ -2978,23 +3089,41 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
pub fn build_proc_headers<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
mod_solutions: &'a ModSolutions,
procedures: MutMap<(Symbol, TopLevelFunctionLayout<'a>), roc_mono::ir::Proc<'a>>,
scope: &mut Scope<'a, 'ctx>,
// alias_analysis_solutions: AliasAnalysisSolutions,
) -> std::vec::Vec<(roc_mono::ir::Proc<'a>, FunctionValue<'ctx>)> {
) -> Vec<
'a,
(
roc_mono::ir::Proc<'a>,
&'a [(&'a FuncSpecSolutions, FunctionValue<'ctx>)],
),
> {
// Populate Procs further and get the low-level Expr from the canonical Expr
let mut headers = std::vec::Vec::with_capacity(procedures.len());
let mut headers = Vec::with_capacity_in(procedures.len(), env.arena);
for ((symbol, layout), proc) in procedures {
let fn_val = build_proc_header(env, layout_ids, symbol, layout, &proc);
let name_bytes = roc_mono::alias_analysis::func_name_bytes(&proc);
let func_name = FuncName(&name_bytes);
if proc.args.is_empty() {
// this is a 0-argument thunk, i.e. a top-level constant definition
// it must be in-scope everywhere in the module!
scope.insert_top_level_thunk(symbol, env.arena.alloc(layout), fn_val);
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
let it = func_solutions.specs();
let mut function_values = Vec::with_capacity_in(it.size_hint().0, env.arena);
for specialization in it {
let fn_val = build_proc_header(env, *specialization, symbol, &proc);
if proc.args.is_empty() {
// this is a 0-argument thunk, i.e. a top-level constant definition
// it must be in-scope everywhere in the module!
scope.insert_top_level_thunk(symbol, env.arena.alloc(layout), fn_val);
}
let func_spec_solutions = func_solutions.spec(specialization).unwrap();
function_values.push((func_spec_solutions, fn_val));
}
headers.push((proc, fn_val));
headers.push((proc, function_values.into_bump_slice()));
}
headers
@ -3026,18 +3155,6 @@ pub fn build_procedures_return_main<'a, 'ctx, 'env>(
.unwrap()
}
// Coming soon
// pub enum AliasAnalysisSolutions {
// NotAvailable,
// Available(morphic_lib::Solutions),
// }
//
// impl std::fmt::Debug for AliasAnalysisSolutions {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(f, "AliasAnalysisSolutions {{}}")
// }
// }
fn build_procedures_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
opt_level: OptLevel,
@ -3047,82 +3164,107 @@ fn build_procedures_help<'a, 'ctx, 'env>(
let mut layout_ids = roc_mono::layout::LayoutIds::default();
let mut scope = Scope::default();
// Coming Soon
//
// if false {
// let it = state.procedures.iter().map(|x| x.1);
//
// match roc_mono::alias_analysis::spec_program(it) {
// Err(e) => panic!("Error in alias analysis: {:?}", e),
// Ok(solutions) => {
// state.alias_analysis_solutions =
// AliasAnalysisSolutions::Available(solutions)
// }
// }
// }
let it = procedures.iter().map(|x| x.1);
let solutions = match roc_mono::alias_analysis::spec_program(it) {
Err(e) => panic!("Error in alias analysis: {:?}", e),
Ok(solutions) => solutions,
};
let solutions = env.arena.alloc(solutions);
let mod_solutions = solutions
.mod_solutions(roc_mono::alias_analysis::MOD_APP)
.unwrap();
// Add all the Proc headers to the module.
// We have to do this in a separate pass first,
// because their bodies may reference each other.
let headers = build_proc_headers(env, &mut layout_ids, procedures, &mut scope);
let headers = build_proc_headers(env, &mod_solutions, procedures, &mut scope);
let (_, function_pass) = construct_optimization_passes(env.module, opt_level);
for (proc, fn_val) in headers {
let mut current_scope = scope.clone();
for (proc, fn_vals) in headers {
for (func_spec_solutions, fn_val) in fn_vals {
let mut current_scope = scope.clone();
// only have top-level thunks for this proc's module in scope
// this retain is not needed for correctness, but will cause less confusion when debugging
let home = proc.name.module_id();
current_scope.retain_top_level_thunks_for_module(home);
// only have top-level thunks for this proc's module in scope
// this retain is not needed for correctness, but will cause less confusion when debugging
let home = proc.name.module_id();
current_scope.retain_top_level_thunks_for_module(home);
build_proc(&env, &mut layout_ids, scope.clone(), proc, fn_val);
// call finalize() before any code generation/verification
env.dibuilder.finalize();
if fn_val.verify(true) {
function_pass.run_on(&fn_val);
} else {
let mode = "NON-OPTIMIZED";
eprintln!(
"\n\nFunction {:?} failed LLVM verification in {} build. Its content was:\n",
fn_val.get_name().to_str().unwrap(),
mode,
build_proc(
&env,
mod_solutions,
&mut layout_ids,
func_spec_solutions,
scope.clone(),
&proc,
*fn_val,
);
fn_val.print_to_stderr();
// module.print_to_stderr();
// call finalize() before any code generation/verification
env.dibuilder.finalize();
panic!(
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
fn_val.get_name().to_str().unwrap(),
mode,
);
if fn_val.verify(true) {
function_pass.run_on(&fn_val);
} else {
let mode = "NON-OPTIMIZED";
eprintln!(
"\n\nFunction {:?} failed LLVM verification in {} build. Its content was:\n",
fn_val.get_name().to_str().unwrap(),
mode,
);
fn_val.print_to_stderr();
// module.print_to_stderr();
panic!(
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
fn_val.get_name().to_str().unwrap(),
mode,
);
}
}
}
main_data.map(|(main_fn_symbol, main_fn_layout)| {
promote_to_main_function(env, &mut layout_ids, main_fn_symbol, main_fn_layout)
promote_to_main_function(env, mod_solutions, main_fn_symbol, main_fn_layout)
})
}
fn func_spec_name<'a>(
arena: &'a Bump,
interns: &Interns,
symbol: Symbol,
func_spec: FuncSpec,
) -> bumpalo::collections::String<'a> {
use std::fmt::Write;
let mut buf = bumpalo::collections::String::with_capacity_in(1, arena);
let ident_string = symbol.ident_string(interns);
let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap();
write!(buf, "{}_{}_", module_string, ident_string).unwrap();
for byte in func_spec.0.iter() {
write!(buf, "{:x?}", byte).unwrap();
}
buf
}
fn build_proc_header<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
func_spec: FuncSpec,
symbol: Symbol,
top_level: TopLevelFunctionLayout<'a>,
proc: &roc_mono::ir::Proc<'a>,
) -> FunctionValue<'ctx> {
let layout = env.arena.alloc(top_level).full();
let args = proc.args;
let arena = env.arena;
let fn_name = layout_ids
.get(symbol, &layout)
.to_symbol_string(symbol, &env.interns);
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
let ret_type = basic_type_from_layout(env, &proc.ret_layout);
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
@ -3147,7 +3289,7 @@ fn build_proc_header<'a, 'ctx, 'env>(
fn_val.set_subprogram(subprogram);
if env.exposed_to_host.contains(&symbol) {
expose_function_to_host(env, fn_val);
expose_function_to_host(env, symbol, fn_val);
}
fn_val
@ -3169,7 +3311,7 @@ pub fn build_closure_caller<'a, 'ctx, 'env>(
// e.g. `roc__main_1_Fx_caller`
let function_name = format!(
"roc_{}_{}_caller",
"roc__{}_{}_caller",
def_name,
alias_symbol.ident_string(&env.interns)
);
@ -3403,14 +3545,14 @@ fn build_host_exposed_alias_size_help<'a, 'ctx, 'env>(
let size_function_type = env.context.i64_type().fn_type(&[], false);
let size_function_name: String = if let Some(label) = opt_label {
format!(
"roc_{}_{}_{}_size",
"roc__{}_{}_{}_size",
def_name,
alias_symbol.ident_string(&env.interns),
label
)
} else {
format!(
"roc_{}_{}_size",
"roc__{}_{}_size",
def_name,
alias_symbol.ident_string(&env.interns)
)
@ -3434,9 +3576,11 @@ fn build_host_exposed_alias_size_help<'a, 'ctx, 'env>(
pub fn build_proc<'a, 'ctx, 'env>(
env: &'a Env<'a, 'ctx, 'env>,
mod_solutions: &'a ModSolutions,
layout_ids: &mut LayoutIds<'a>,
func_spec_solutions: &FuncSpecSolutions,
mut scope: Scope<'a, 'ctx>,
proc: roc_mono::ir::Proc<'a>,
proc: &roc_mono::ir::Proc<'a>,
fn_val: FunctionValue<'ctx>,
) {
use roc_mono::ir::HostExposedLayouts;
@ -3454,16 +3598,28 @@ pub fn build_proc<'a, 'ctx, 'env>(
// * roc__mainForHost_1_Update_result_size() -> i64
let evaluator_layout = env.arena.alloc(top_level).full();
let evaluator_name = layout_ids
.get(symbol, &evaluator_layout)
.to_symbol_string(symbol, &env.interns);
let evaluator = function_value_by_name_help(
env,
evaluator_layout,
let it = top_level.arguments.iter().copied();
let bytes = roc_mono::alias_analysis::func_name_bytes_help(
symbol,
&evaluator_name,
it,
top_level.result,
);
let func_name = FuncName(&bytes);
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
let mut it = func_solutions.specs();
let func_spec = it.next().unwrap();
debug_assert!(
it.next().is_none(),
"we expect only one specialization of this symbol"
);
let evaluator =
function_value_by_func_spec(env, *func_spec, symbol, evaluator_layout);
let ident_string = proc.name.ident_string(&env.interns);
let fn_name: String = format!("{}_1", ident_string);
build_closure_caller(
env, &fn_name, evaluator, name, arguments, closure, result,
@ -3503,7 +3659,14 @@ pub fn build_proc<'a, 'ctx, 'env>(
scope.insert(*arg_symbol, (*layout, arg_val));
}
let body = build_exp_stmt(env, layout_ids, &mut scope, fn_val, &proc.body);
let body = build_exp_stmt(
env,
layout_ids,
func_spec_solutions,
&mut scope,
fn_val,
&proc.body,
);
// only add a return if codegen did not already add one
if let Some(block) = builder.get_insert_block() {
@ -3523,15 +3686,13 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) {
}
}
fn function_value_by_name<'a, 'ctx, 'env>(
fn function_value_by_func_spec<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
layout: Layout<'a>,
func_spec: FuncSpec,
symbol: Symbol,
layout: Layout<'a>,
) -> FunctionValue<'ctx> {
let fn_name = layout_ids
.get(symbol, &layout)
.to_symbol_string(symbol, &env.interns);
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
let fn_name = fn_name.as_str();
function_value_by_name_help(env, layout, symbol, fn_name)
@ -3573,15 +3734,14 @@ fn function_value_by_name_help<'a, 'ctx, 'env>(
// #[allow(clippy::cognitive_complexity)]
#[inline(always)]
fn call_with_args<'a, 'ctx, 'env>(
fn roc_call_with_args<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
layout: &Layout<'a>,
symbol: Symbol,
_parent: FunctionValue<'ctx>,
func_spec: FuncSpec,
args: &[BasicValueEnum<'ctx>],
) -> BasicValueEnum<'ctx> {
let fn_val = function_value_by_name(env, layout_ids, *layout, symbol);
let fn_val = function_value_by_func_spec(env, func_spec, symbol, *layout);
let call = env.builder.build_call(fn_val, args, "call");
@ -3664,6 +3824,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
return_layout: &Layout<'a>,
op: LowLevel,
function_layout: Layout<'a>,
func_spec: FuncSpec,
function_owns_closure_data: bool,
args: &[Symbol],
) -> BasicValueEnum<'ctx> {
@ -3676,18 +3837,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
($index:expr) => {{
let function_symbol = args[$index];
let fn_name = layout_ids
.get(function_symbol, &function_layout)
.to_symbol_string(function_symbol, &env.interns);
env.module
.get_function(fn_name.as_str())
.unwrap_or_else(|| {
panic!(
"Could not get pointer to unknown function {:?} {:?}",
fn_name, function_layout
)
})
function_value_by_func_spec(env, func_spec, function_symbol, function_layout)
}};
}
@ -4892,6 +5042,7 @@ fn build_foreign_symbol_write_result_into_ptr<'a, 'ctx, 'env>(
fn build_foreign_symbol<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
func_spec_solutions: &FuncSpecSolutions,
scope: &mut Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>,
foreign: &roc_module::ident::ForeignSymbol,
@ -4955,7 +5106,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>(
scope.insert(symbol, (*ret_layout, call_result));
build_exp_stmt(env, layout_ids, scope, parent, pass);
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, pass);
scope.remove(&symbol);
}
@ -4987,7 +5138,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>(
(Layout::Struct(&[]), exception_object),
);
build_exp_stmt(env, layout_ids, scope, parent, fail);
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, fail);
}
call_result

View file

@ -17,6 +17,65 @@ pub const MOD_APP: ModName = ModName(b"UserApp");
pub const STATIC_STR_NAME: ConstName = ConstName(&Symbol::STR_ALIAS_ANALYSIS_STATIC.to_ne_bytes());
pub fn func_name_bytes(proc: &Proc) -> [u8; 16] {
func_name_bytes_help(proc.name, proc.args.iter().map(|x| x.0), proc.ret_layout)
}
pub fn func_name_bytes_help<'a, I>(
symbol: Symbol,
argument_layouts: I,
return_layout: Layout<'a>,
) -> [u8; 16]
where
I: Iterator<Item = Layout<'a>>,
{
let mut name_bytes = [0u8; 16];
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
let layout_hash = {
let mut hasher = DefaultHasher::new();
for layout in argument_layouts {
match layout {
Layout::Closure(_, lambda_set, _) => {
lambda_set.runtime_representation().hash(&mut hasher);
}
_ => {
layout.hash(&mut hasher);
}
}
}
match return_layout {
Layout::Closure(_, lambda_set, _) => {
lambda_set.runtime_representation().hash(&mut hasher);
}
_ => {
return_layout.hash(&mut hasher);
}
}
hasher.finish()
};
let sbytes = symbol.to_ne_bytes();
let lbytes = layout_hash.to_ne_bytes();
let it = sbytes
.iter()
.chain(lbytes.iter())
.zip(name_bytes.iter_mut());
for (source, target) in it {
*target = *source;
}
name_bytes
}
pub fn spec_program<'a, I>(procs: I) -> Result<morphic_lib::Solutions>
where
I: Iterator<Item = &'a Proc<'a>>,
@ -40,36 +99,7 @@ where
for proc in procs {
let spec = proc_spec(proc)?;
let mut name_bytes = [0u8; 16];
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
let layout_hash = {
let mut hasher = DefaultHasher::new();
for (layout, _) in proc.args.iter() {
layout.hash(&mut hasher);
}
proc.ret_layout.hash(&mut hasher);
hasher.finish()
};
let sbytes = proc.name.to_ne_bytes();
let lbytes = layout_hash.to_ne_bytes();
let it = sbytes
.iter()
.chain(lbytes.iter())
.zip(name_bytes.iter_mut());
for (source, target) in it {
*target = *source;
}
m.add_func(FuncName(&name_bytes), spec)?;
m.add_func(FuncName(&func_name_bytes(proc)), spec)?;
if format!("{:?}", proc.name).contains("mainForHost") {
main_function = Some(proc.name);
@ -112,6 +142,8 @@ where
p.build()?
};
// eprintln!("{}", program.to_source_string());
morphic_lib::solve(program)
}
}
@ -345,16 +377,17 @@ fn call_spec(
ByName {
name: symbol,
full_layout: _,
ret_layout: _,
arg_layouts: _,
ret_layout,
arg_layouts,
specialization_id,
} => {
let array = specialization_id.to_bytes();
let spec_var = CalleeSpecVar(&array);
let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?;
let slice = &symbol.to_ne_bytes();
let name = FuncName(slice);
let it = arg_layouts.iter().copied();
let bytes = func_name_bytes_help(*symbol, it, *ret_layout);
let name = FuncName(&bytes);
let module = MOD_APP;
builder.add_call(block, spec_var, module, name, arg_value_id)
}
@ -381,7 +414,64 @@ fn call_spec(
*update_mode,
call.arguments,
),
HigherOrderLowLevel { .. } => {
HigherOrderLowLevel {
specialization_id,
closure_layout: _,
op,
arg_layouts,
ret_layout,
..
} => {
let array = specialization_id.to_bytes();
let spec_var = CalleeSpecVar(&array);
let symbol = {
use roc_module::low_level::LowLevel::*;
match op {
ListMap | ListMapWithIndex => call.arguments[1],
ListMap2 => call.arguments[2],
ListMap3 => call.arguments[3],
ListWalk | ListWalkUntil | ListWalkBackwards | DictWalk => call.arguments[2],
ListKeepIf | ListKeepOks | ListKeepErrs => call.arguments[1],
ListSortWith => call.arguments[1],
_ => unreachable!(),
}
};
let it = arg_layouts.iter().copied();
let bytes = func_name_bytes_help(symbol, it, *ret_layout);
let name = FuncName(&bytes);
let module = MOD_APP;
{
use roc_module::low_level::LowLevel::*;
match op {
DictWalk => {
let dict = env.symbols[&call.arguments[0]];
let default = env.symbols[&call.arguments[1]];
let bag = builder.add_get_tuple_field(block, dict, DICT_BAG_INDEX)?;
let _cell = builder.add_get_tuple_field(block, dict, DICT_CELL_INDEX)?;
let first = builder.add_bag_get(block, bag)?;
let argument = builder.add_make_tuple(block, &[first, default])?;
builder.add_call(block, spec_var, module, name, argument)?;
}
_ => {
// fake a call to the function argument
// to make sure the function is specialized
// very invalid
let arg_value_id = build_tuple_value(builder, env, block, &[])?;
builder.add_call(block, spec_var, module, name, arg_value_id)?;
}
}
}
// TODO overly pessimstic
// filter_map because one of the arguments is a function name, which
// is not defined in the env
@ -740,6 +830,9 @@ fn str_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
const LIST_CELL_INDEX: u32 = 0;
const LIST_BAG_INDEX: u32 = 1;
const DICT_CELL_INDEX: u32 = LIST_CELL_INDEX;
const DICT_BAG_INDEX: u32 = LIST_BAG_INDEX;
fn new_list(builder: &mut FuncDefBuilder, block: BlockId, element_type: TypeId) -> Result<ValueId> {
let cell = builder.add_new_heap_cell(block)?;
let bag = builder.add_empty_bag(block, element_type)?;

View file

@ -454,7 +454,12 @@ impl<'a> Context<'a> {
}
HigherOrderLowLevel {
op, closure_layout, ..
op,
closure_layout,
specialization_id,
arg_layouts,
ret_layout,
..
} => {
macro_rules! create_call {
($borrows:expr) => {
@ -464,6 +469,9 @@ impl<'a> Context<'a> {
op: *op,
closure_layout: *closure_layout,
function_owns_closure_data: true,
specialization_id: *specialization_id,
arg_layouts,
ret_layout: *ret_layout,
}
} else {
call_type

View file

@ -1146,8 +1146,13 @@ pub enum CallType<'a> {
op: LowLevel,
/// the layout of the closure argument, if any
closure_layout: Layout<'a>,
/// specialization id of the function argument
specialization_id: CallSpecId,
/// does the function need to own the closure data
function_owns_closure_data: bool,
/// function layout
arg_layouts: &'a [Layout<'a>],
ret_layout: Layout<'a>,
},
}
@ -2575,8 +2580,8 @@ fn cleanup_attempted_type<'a>(
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TopLevelFunctionLayout<'a> {
arguments: &'a [Layout<'a>],
result: Layout<'a>,
pub arguments: &'a [Layout<'a>],
pub result: Layout<'a>,
}
impl<'a> TopLevelFunctionLayout<'a> {
@ -2701,21 +2706,29 @@ macro_rules! match_on_closure_argument {
let arena = $env.arena;
let function_layout = arena.alloc(top_level).full();
let arg_layouts = top_level.arguments;
let ret_layout = top_level.result;
match closure_data_layout {
Layout::Closure(_, lambda_set, _) => {
lowlevel_match_on_lambda_set(
$env,
lambda_set,
$closure_data_symbol,
|top_level_function, closure_data, function_layout| self::Call {
|top_level_function, closure_data, function_layout, specialization_id| self::Call {
call_type: CallType::HigherOrderLowLevel {
op: $op,
closure_layout: function_layout,
function_owns_closure_data: false
specialization_id,
function_owns_closure_data: false,
arg_layouts,
ret_layout,
},
arguments: arena.alloc([$($x,)* top_level_function, closure_data]),
},
arena.alloc(top_level).full(),
function_layout,
$layout,
$assigned,
$hole,
@ -3393,7 +3406,6 @@ pub fn with_hole<'a>(
}
List {
list_var: _,
elem_var,
loc_elems,
} => {
@ -7696,7 +7708,7 @@ fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
hole: &'a Stmt<'a>,
) -> Stmt<'a>
where
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>) -> Call<'a> + Copy,
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
{
match lambda_set.runtime_representation() {
Layout::Union(_) => {
@ -7733,7 +7745,13 @@ where
Layout::Struct(_) => {
let function_symbol = lambda_set.set[0].0;
let call = to_lowlevel_call(function_symbol, closure_data_symbol, function_layout);
let call_spec_id = env.next_call_specialization_id();
let call = to_lowlevel_call(
function_symbol,
closure_data_symbol,
function_layout,
call_spec_id,
);
build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
}
@ -7787,7 +7805,7 @@ fn lowlevel_union_lambda_set_to_switch<'a, ToLowLevelCall>(
hole: &'a Stmt<'a>,
) -> Stmt<'a>
where
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>) -> Call<'a> + Copy,
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
{
debug_assert!(!lambda_set.is_empty());
@ -7800,7 +7818,13 @@ where
let hole = Stmt::Jump(join_point_id, env.arena.alloc([assigned]));
let call = to_lowlevel_call(*function_symbol, closure_data_symbol, function_layout);
let call_spec_id = env.next_call_specialization_id();
let call = to_lowlevel_call(
*function_symbol,
closure_data_symbol,
function_layout,
call_spec_id,
);
let stmt = build_call(env, call, assigned, return_layout, env.arena.alloc(hole));
branches.push((i as u64, BranchInfo::None, stmt));
@ -8206,7 +8230,7 @@ fn lowlevel_enum_lambda_set_to_switch<'a, ToLowLevelCall>(
hole: &'a Stmt<'a>,
) -> Stmt<'a>
where
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>) -> Call<'a> + Copy,
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
{
debug_assert!(!lambda_set.is_empty());
@ -8219,7 +8243,13 @@ where
let hole = Stmt::Jump(join_point_id, env.arena.alloc([result_symbol]));
let call = to_lowlevel_call(*function_symbol, closure_data_symbol, function_layout);
let call_spec_id = env.next_call_specialization_id();
let call = to_lowlevel_call(
*function_symbol,
closure_data_symbol,
function_layout,
call_spec_id,
);
let stmt = build_call(
env,
call,

View file

@ -43,6 +43,7 @@ pub enum Layout<'a> {
Struct(&'a [Layout<'a>]),
Union(UnionLayout<'a>),
RecursivePointer,
/// A function. The types of its arguments, then the type of its return value.
FunctionPointer(&'a [Layout<'a>], &'a Layout<'a>),
Closure(&'a [Layout<'a>], LambdaSet<'a>, &'a Layout<'a>),